diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d34ec8e5c..f6e3f44bc 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -36,6 +36,12 @@ jobs: env: WORKSPACE_PATH: ${{ github.workspace }} run: .\\.github\\workflows\\release\\build-packages.ps1 + - name: Run unit tests + env: + PN_PUB_KEY: ${{ secrets.PN_PUB_KEY }} + PN_SUB_KEY: ${{ secrets.PN_SUB_KEY }} + PN_SEC_KEY: ${{ secrets.PN_SEC_KEY }} + run: dotnet test .\\src\\UnitTests\\PubnubApiPCL.Tests\\PubnubApiPCL.Tests.csproj --verbosity normal --logger trx - name: Cancel workflow runs for commit on error if: failure() uses: ./.github/.release/actions/actions/utils/fast-jobs-failure diff --git a/src/Api/PubnubApi/EventEngine/Presence/Common/PresenceInput.cs b/src/Api/PubnubApi/EventEngine/Presence/Common/PresenceInput.cs index d1023f128..b3d5874c8 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/Common/PresenceInput.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/Common/PresenceInput.cs @@ -5,15 +5,15 @@ namespace PubnubApi.EventEngine.Presence.Common { public class PresenceInput { - public IEnumerable Channels { get; set; } - public IEnumerable ChannelGroups { get; set; } + public IEnumerable Channels { get; set; } = Enumerable.Empty(); + public IEnumerable ChannelGroups { get; set; } = Enumerable.Empty(); public static PresenceInput operator +(PresenceInput a, PresenceInput b) { return new PresenceInput { - Channels = a.Channels?.Union(b.Channels ?? new string[0]), - ChannelGroups = a.ChannelGroups?.Union(b.ChannelGroups ?? new string[0]), + Channels = a.Channels?.Union(b.Channels ?? new string[0]).ToArray(), + ChannelGroups = a.ChannelGroups?.Union(b.ChannelGroups ?? new string[0]).ToArray(), }; } @@ -21,8 +21,8 @@ public class PresenceInput { return new PresenceInput { - Channels = a.Channels?.Except(b.Channels ?? new string[0]), - ChannelGroups = a.ChannelGroups?.Except(b.ChannelGroups ?? new string[0]), + Channels = a.Channels?.Except(b.Channels ?? new string[0]).ToArray(), + ChannelGroups = a.ChannelGroups?.Except(b.ChannelGroups ?? new string[0]).ToArray(), }; } @@ -32,5 +32,15 @@ public bool IsEmpty() || ((Channels != null && Channels.Count() == 0) && (ChannelGroups != null && ChannelGroups.Count() == 0)); } + + public override bool Equals(object obj) + { + if (obj is null || obj is not PresenceInput) + return false; + + var typedObj = obj as PresenceInput; + return this.Channels.SequenceEqual(typedObj.Channels) + && this.ChannelGroups.SequenceEqual(typedObj.ChannelGroups); + } } } diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelDelayedHeartbeatInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelDelayedHeartbeatInvocation.cs new file mode 100644 index 000000000..2c8bd81d6 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelDelayedHeartbeatInvocation.cs @@ -0,0 +1,6 @@ +using PubnubApi.EventEngine.Core; + +namespace PubnubApi.EventEngine.Presence.Invocations +{ + public class CancelDelayedHeartbeatInvocation : Core.IEffectInvocation {} +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelWaitInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelWaitInvocation.cs new file mode 100644 index 000000000..31958bf02 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelWaitInvocation.cs @@ -0,0 +1,6 @@ +using PubnubApi.EventEngine.Core; + +namespace PubnubApi.EventEngine.Presence.Invocations +{ + public class CancelWaitInvocation : Core.IEffectInvocation {} +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/DelayedHeartbeatInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/DelayedHeartbeatInvocation.cs new file mode 100644 index 000000000..966b36756 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/DelayedHeartbeatInvocation.cs @@ -0,0 +1,12 @@ +using PubnubApi.EventEngine.Presence.Common; +using PubnubApi.EventEngine.Core; + +namespace PubnubApi.EventEngine.Presence.Invocations +{ + public class DelayedHeartbeatInvocation : Core.IEffectInvocation + { + public PresenceInput Input { get; set; } + public int Attempts { get; set; } + public PNStatus Reason { get; set; } + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/HearbeatInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/HearbeatInvocation.cs new file mode 100644 index 000000000..0a69f12ef --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/HearbeatInvocation.cs @@ -0,0 +1,10 @@ +using PubnubApi.EventEngine.Presence.Common; +using PubnubApi.EventEngine.Core; + +namespace PubnubApi.EventEngine.Presence.Invocations +{ + public class HeartbeatInvocation : Core.IEffectInvocation + { + public PresenceInput Input { get; set; } + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/LeaveInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/LeaveInvocation.cs new file mode 100644 index 000000000..2cbb0e97b --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/LeaveInvocation.cs @@ -0,0 +1,10 @@ +using PubnubApi.EventEngine.Presence.Common; +using PubnubApi.EventEngine.Core; + +namespace PubnubApi.EventEngine.Presence.Invocations +{ + public class LeaveInvocation : Core.IEffectInvocation + { + public PresenceInput Input { get; set; } + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/TerminateEventEngineInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/TerminateEventEngineInvocation.cs new file mode 100644 index 000000000..82c797480 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/TerminateEventEngineInvocation.cs @@ -0,0 +1,6 @@ +using PubnubApi.EventEngine.Core; + +namespace PubnubApi.EventEngine.Presence.Invocations +{ + public class TerminateEventEngineInvocation : Core.IEffectInvocation {} +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/WaitInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/WaitInvocation.cs new file mode 100644 index 000000000..5818a36a8 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/WaitInvocation.cs @@ -0,0 +1,10 @@ +using PubnubApi.EventEngine.Presence.Common; +using PubnubApi.EventEngine.Core; + +namespace PubnubApi.EventEngine.Presence.Invocations +{ + public class WaitInvocation : Core.IEffectInvocation + { + public PresenceInput Input { get; set; } + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/APresenceState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/APresenceState.cs index 18d4453c9..473452faf 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/States/APresenceState.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/States/APresenceState.cs @@ -1,11 +1,12 @@ using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Presence.Common; +using PubnubApi.EventEngine.Presence.Invocations; namespace PubnubApi.EventEngine.Presence.States { public abstract class APresenceState : Core.State { - public PresenceInput Input { get; set; } + public PresenceInput Input { get; set; } = new PresenceInput(); // empty by default public bool IsEmpty() { @@ -16,12 +17,22 @@ protected TransitionResult HandleLeftEvent(Events.LeftEvent e) { var newInput = this.Input - e.Input; - return newInput.IsEmpty() - ? (TransitionResult)new InactiveState() - : (TransitionResult)new HeartbeatingState() + State state = newInput.IsEmpty() + ? new InactiveState() + : new HeartbeatingState() { Input = newInput, }; + + return state.With(new LeaveInvocation(){ Input = e.Input }); + } + + public override bool Equals(object obj) + { + if (obj is null || obj is not APresenceState) + return false; + + return this.Input.Equals(((APresenceState)obj).Input); } } } diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/CooldownState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/CooldownState.cs index 997548867..83e261fac 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/States/CooldownState.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/States/CooldownState.cs @@ -22,11 +22,12 @@ public override TransitionResult Transition(IEvent ev) Input = e.Input != this.Input ? this.Input + e.Input : this.Input, }, Events.LeftEvent e => HandleLeftEvent(e), - Events.LeftAllEvent e => new InactiveState(), + Events.LeftAllEvent e => new InactiveState() + .With(new LeaveInvocation(){ Input = this.Input }), Events.DisconnectEvent e => new StoppedState() { Input = this.Input, - }, + }.With(new LeaveInvocation(){ Input = this.Input }), Events.TimesUpEvent e => new HeartbeatingState() { Input = this.Input, diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/FailedState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/FailedState.cs index 8eefb7245..eabc2aea6 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/States/FailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/States/FailedState.cs @@ -23,7 +23,8 @@ public override TransitionResult Transition(IEvent ev) Input = e.Input != this.Input ? this.Input + e.Input : this.Input, }, Events.LeftEvent e => HandleLeftEvent(e), - Events.LeftAllEvent e => new InactiveState(), + Events.LeftAllEvent e => new InactiveState() + .With(new LeaveInvocation(){ Input = this.Input }), Events.ReconnectEvent e => new HeartbeatingState() { Input = this.Input, @@ -31,7 +32,7 @@ public override TransitionResult Transition(IEvent ev) Events.DisconnectEvent e => new StoppedState() { Input = this.Input, - }, + }.With(new LeaveInvocation() { Input = this.Input }), _ => null, }; } diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/HeartBeatingState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/HeartBeatingState.cs index b995cc152..3bab13550 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/States/HeartBeatingState.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/States/HeartBeatingState.cs @@ -21,7 +21,8 @@ public override TransitionResult Transition(IEvent ev) Input = e.Input != this.Input ? this.Input + e.Input : this.Input, }, Events.LeftEvent e => HandleLeftEvent(e), - Events.LeftAllEvent e => new InactiveState(), + Events.LeftAllEvent e => new InactiveState() + .With(new LeaveInvocation(){ Input = this.Input }), Events.HeartbeatSuccessEvent e => new CooldownState() { Input = this.Input, @@ -30,7 +31,7 @@ public override TransitionResult Transition(IEvent ev) Events.DisconnectEvent e => new StoppedState() { Input = this.Input, - }, + }.With(new LeaveInvocation(){ Input = this.Input }), _ => null, }; } diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/InactiveState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/InactiveState.cs index 74f4f238c..74aeace2d 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/States/InactiveState.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/States/InactiveState.cs @@ -1,4 +1,5 @@ using PubnubApi.EventEngine.Presence.Invocations; +using PubnubApi.EventEngine.Presence.Common; using PubnubApi.EventEngine.Core; using System.Collections.Generic; using System; @@ -9,7 +10,7 @@ public class InactiveState : APresenceState { public InactiveState() { - Input = null; + Input = new PresenceInput(); } // TODO: Dummy Invocation until we have real ones diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/ReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/ReconnectingState.cs index 4ef39e747..afdb5b818 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/States/ReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/States/ReconnectingState.cs @@ -24,7 +24,8 @@ public override TransitionResult Transition(IEvent ev) Input = e.Input != this.Input ? this.Input + e.Input : this.Input, }, Events.LeftEvent e => HandleLeftEvent(e), - Events.LeftAllEvent e => new InactiveState(), + Events.LeftAllEvent e => new InactiveState() + .With(new LeaveInvocation(){ Input = this.Input }), Events.HeartbeatSuccessEvent e => new CooldownState() { Input = this.Input, @@ -38,7 +39,7 @@ public override TransitionResult Transition(IEvent ev) Events.DisconnectEvent e => new StoppedState() { Input = this.Input, - }, + }.With(new LeaveInvocation(){ Input = this.Input }), _ => null, }; } diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index 369ae84c1..f1fc8e8cf 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -249,6 +249,23 @@ + + + + + + + + + + + + + + + + + HttpUtility\HttpUtility.cs diff --git a/src/UnitTests/PubnubApi.Tests/EncryptionTests.cs b/src/UnitTests/PubnubApi.Tests/EncryptionTests.cs index b7b0dd806..64b402fba 100644 --- a/src/UnitTests/PubnubApi.Tests/EncryptionTests.cs +++ b/src/UnitTests/PubnubApi.Tests/EncryptionTests.cs @@ -821,4 +821,4 @@ public static string DecodeEncodedNonAsciiCharacters(string value) }); } } -} \ No newline at end of file +} diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/CooldownState.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/CooldownState.cs index 41a920608..c9604db5b 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/CooldownState.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/CooldownState.cs @@ -3,68 +3,104 @@ using PubnubApi.EventEngine.Presence.Common; using PubnubApi.EventEngine.Presence.Events; using PubnubApi.EventEngine.Presence.States; +using PubnubApi.EventEngine.Presence.Invocations; +using System.Linq; namespace PubnubApi.Tests.EventEngine.Presence { internal class CooldownTransitions { + // Test case: + // - Current state + // - Event + // - Expected next state + // - Invocations private static readonly object[] testCases = { new object[] { new CooldownState(), new JoinedEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new HeartbeatingState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, + null }, new object[] { new CooldownState() { Input = new PresenceInput() { Channels = new [] { "a", "b" } } }, new LeftEvent() { Input = new PresenceInput() { Channels = new [] { "b" } } }, new HeartbeatingState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new [] { "b" } } } } }, new object[] { new CooldownState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new LeftEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new InactiveState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new [] { "a" } } } } }, new object[] { new CooldownState(), new LeftAllEvent(), new InactiveState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new string[] { } } } } }, new object[] { new CooldownState(), new HeartbeatSuccessEvent(), null, + null }, new object[] { new CooldownState(), new HeartbeatFailureEvent() { Status = new PNStatus() }, null, - }, - new object[] { - new CooldownState(), - new HeartbeatGiveUpEvent() { Status = new PNStatus() { Category = PNStatusCategory.PNCancelledCategory } }, - null, + null }, new object[] { new CooldownState(), new ReconnectEvent(), null, + null }, new object[] { new CooldownState(), new DisconnectEvent(), new StoppedState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() } } }, new object[] { new CooldownState(), new TimesUpEvent(), new HeartbeatingState(), + null }, }; - [TestCasesSource(nameof(testCases))] - public void TestTransition(State @sut, IEvent @ev, State @expected) + [TestCaseSource(nameof(testCases))] + public void TestTransition(APresenceState @sut, IEvent @ev, APresenceState @expected, IEffectInvocation[] @_) { - Assert.AreEqual(expected, sut.Transition(ev)); + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + Assert.AreEqual(@expected, result.State); + } + + [TestCaseSource(nameof(testCases))] + public void TestReturnedInvocations(State @sut, IEvent @ev, State @_, IEffectInvocation[] @expected) + { + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + foreach (var item in result.Invocations) + { + Assert.True(expected.Select(i => i.GetType()).Contains(item.GetType())); + } } } } diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/FailedState.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/FailedState.cs index 11a173ca0..aa814001e 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/FailedState.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/FailedState.cs @@ -3,6 +3,8 @@ using PubnubApi.EventEngine.Presence.Common; using PubnubApi.EventEngine.Presence.Events; using PubnubApi.EventEngine.Presence.States; +using PubnubApi.EventEngine.Presence.Invocations; +using System.Linq; namespace PubnubApi.Tests.EventEngine.Presence { @@ -13,58 +15,87 @@ internal class FailedStateTransitions new FailedState(), new JoinedEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new HeartbeatingState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, + null }, new object[] { new FailedState() { Input = new PresenceInput() { Channels = new [] { "a", "b" } } }, new LeftEvent() { Input = new PresenceInput() { Channels = new [] { "b" } } }, new HeartbeatingState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new [] { "b" } } } } }, new object[] { new FailedState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new LeftEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new InactiveState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new [] { "a" } } } } }, new object[] { new FailedState(), new LeftAllEvent(), new InactiveState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new string[] { } } } } }, new object[] { new FailedState(), new HeartbeatSuccessEvent(), null, + null }, new object[] { new FailedState(), new HeartbeatFailureEvent() { Status = new PNStatus() }, null, - }, - new object[] { - new FailedState(), - new HeartbeatGiveUpEvent() { Status = new PNStatus() { Category = PNStatusCategory.PNCancelledCategory } }, - null, + null }, new object[] { new FailedState(), new ReconnectEvent(), new HeartbeatingState(), + null }, new object[] { new FailedState(), new DisconnectEvent(), new StoppedState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() } } }, new object[] { new FailedState(), new TimesUpEvent(), null, + null }, }; - [TestCasesSource(nameof(testCases))] - public void TestTransition(State @sut, IEvent @ev, State @expected) + [TestCaseSource(nameof(testCases))] + public void TestTransition(APresenceState @sut, IEvent @ev, APresenceState @expected, IEffectInvocation[] @_) { - Assert.AreEqual(expected, sut.Transition(ev)); + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + Assert.AreEqual(@expected, result.State); + } + + [TestCaseSource(nameof(testCases))] + public void TestReturnedInvocations(State @sut, IEvent @ev, State @_, IEffectInvocation[] @expected) + { + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + foreach (var item in result.Invocations) + { + Assert.True(expected.Select(i => i.GetType()).Contains(item.GetType())); + } } } } diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/HeartbeatingState.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/HeartbeatingState.cs index 2286b0c1b..f6cc52b41 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/HeartbeatingState.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/HeartbeatingState.cs @@ -3,6 +3,8 @@ using PubnubApi.EventEngine.Presence.Common; using PubnubApi.EventEngine.Presence.Events; using PubnubApi.EventEngine.Presence.States; +using PubnubApi.EventEngine.Presence.Invocations; +using System.Linq; namespace PubnubApi.Tests.EventEngine.Presence { @@ -13,58 +15,87 @@ internal class HeartbeatingStateTransitions new HeartbeatingState(), new JoinedEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new HeartbeatingState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, + null }, new object[] { new HeartbeatingState() { Input = new PresenceInput() { Channels = new [] { "a", "b" } } }, new LeftEvent() { Input = new PresenceInput() { Channels = new [] { "b" } } }, new HeartbeatingState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new [] { "b" } } } } }, new object[] { new HeartbeatingState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new LeftEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new InactiveState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new [] { "a" } } } } }, new object[] { new HeartbeatingState(), new LeftAllEvent(), new InactiveState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new string[] { } } } } }, new object[] { new HeartbeatingState(), new HeartbeatSuccessEvent(), new CooldownState(), + null }, new object[] { new HeartbeatingState(), new HeartbeatFailureEvent() { Status = new PNStatus() }, new ReconnectingState(), - }, - new object[] { - new HeartbeatingState(), - new HeartbeatGiveUpEvent() { Status = new PNStatus() { Category = PNStatusCategory.PNCancelledCategory } }, - null, + null }, new object[] { new HeartbeatingState(), new ReconnectEvent(), null, + null }, new object[] { new HeartbeatingState(), new DisconnectEvent(), new StoppedState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() } } }, new object[] { new HeartbeatingState(), new TimesUpEvent(), null, + null }, }; - [TestCasesSource(nameof(testCases))] - public void TestTransition(State @sut, IEvent @ev, State @expected) + [TestCaseSource(nameof(testCases))] + public void TestTransition(APresenceState @sut, IEvent @ev, APresenceState @expected, IEffectInvocation[] @_) { - Assert.AreEqual(expected, sut.Transition(ev)); + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + Assert.AreEqual(@expected, result.State); + } + + [TestCaseSource(nameof(testCases))] + public void TestReturnedInvocations(State @sut, IEvent @ev, State @_, IEffectInvocation[] @expected) + { + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + foreach (var item in result.Invocations) + { + Assert.True(expected.Select(i => i.GetType()).Contains(item.GetType())); + } } } } diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/InactiveState.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/InactiveState.cs index a2760d6fb..61e9cec00 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/InactiveState.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/InactiveState.cs @@ -3,6 +3,7 @@ using PubnubApi.EventEngine.Presence.Common; using PubnubApi.EventEngine.Presence.Events; using PubnubApi.EventEngine.Presence.States; +using System.Linq; namespace PubnubApi.Tests.EventEngine.Presence { @@ -13,53 +14,81 @@ internal class InactiveStateTransitions new InactiveState(), new JoinedEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new HeartbeatingState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, + null }, new object[] { new InactiveState(), new LeftEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, null, + null }, new object[] { new InactiveState(), new LeftAllEvent(), null, + null }, new object[] { new InactiveState(), new HeartbeatSuccessEvent(), null, + null }, new object[] { new InactiveState(), new HeartbeatFailureEvent() { Status = new PNStatus() }, null, - }, - new object[] { - new InactiveState(), - new HeartbeatGiveUpEvent() { Status = new PNStatus() { Category = PNStatusCategory.PNCancelledCategory } }, - null, + null }, new object[] { new InactiveState(), new ReconnectEvent(), null, + null }, new object[] { new InactiveState(), new DisconnectEvent(), null, + null }, new object[] { new InactiveState(), new TimesUpEvent(), null, + null }, }; - [TestCasesSource(nameof(testCases))] - public void TestTransition(State @sut, IEvent @ev, State @expected) + [TestCaseSource(nameof(testCases))] + public void TestTransition(APresenceState @sut, IEvent @ev, APresenceState @expected, IEffectInvocation[] @_) { - Assert.AreEqual(expected, sut.Transition(ev)); + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + Assert.AreEqual(@expected, result.State); + } + + [TestCaseSource(nameof(testCases))] + public void TestReturnedInvocations(State @sut, IEvent @ev, State @_, IEffectInvocation[] @expected) + { + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + foreach (var item in result.Invocations) + { + Assert.True(expected.Select(i => i.GetType()).Contains(item.GetType())); + } } } } diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/ReconnectingState.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/ReconnectingState.cs index f068a66ae..37161acf9 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/ReconnectingState.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/ReconnectingState.cs @@ -3,6 +3,8 @@ using PubnubApi.EventEngine.Presence.Common; using PubnubApi.EventEngine.Presence.Events; using PubnubApi.EventEngine.Presence.States; +using PubnubApi.EventEngine.Presence.Invocations; +using System.Linq; namespace PubnubApi.Tests.EventEngine.Presence { @@ -13,63 +15,93 @@ internal class ReconnectingStateTransitions new ReconnectingState(), new JoinedEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new HeartbeatingState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, + null }, new object[] { new ReconnectingState() { Input = new PresenceInput() { Channels = new [] { "a", "b" } } }, new LeftEvent() { Input = new PresenceInput() { Channels = new [] { "b" } } }, new HeartbeatingState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new [] { "b" } } } } }, new object[] { new ReconnectingState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new LeftEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new InactiveState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new [] { "a" } } } } }, new object[] { new ReconnectingState(), new LeftAllEvent(), new InactiveState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() { Channels = new string[] { } } } } }, new object[] { new ReconnectingState(), new HeartbeatSuccessEvent(), new CooldownState(), + null }, new object[] { new ReconnectingState() { RetryCount = 1 }, - new HeartbeatFailureEvent(), + new HeartbeatFailureEvent() { Status = new PNStatus() }, new ReconnectingState() { RetryCount = 2 }, - }, - new object[] { - new ReconnectingState(), - new HeartbeatFailureEvent() { Status = new PNStatus() { Category = PNStatusCategory.PNCancelledCategory} }, - null, + null }, new object[] { new ReconnectingState(), new HeartbeatGiveUpEvent(), new FailedState(), + null }, new object[] { new ReconnectingState(), new ReconnectEvent(), null, + null }, new object[] { new ReconnectingState(), new DisconnectEvent(), new StoppedState(), + new IEffectInvocation[] { new LeaveInvocation() { Input = new PresenceInput() } } }, new object[] { new ReconnectingState(), new TimesUpEvent(), null, + null }, }; - [TestCasesSource(nameof(testCases))] - public void TestTransition(State @sut, IEvent @ev, State @expected) + [TestCaseSource(nameof(testCases))] + public void TestTransition(APresenceState @sut, IEvent @ev, APresenceState @expected, IEffectInvocation[] @_) { - Assert.AreEqual(expected, sut.Transition(ev)); + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + Assert.AreEqual(@expected, result.State); + } + + [TestCaseSource(nameof(testCases))] + public void TestReturnedInvocations(State @sut, IEvent @ev, State @_, IEffectInvocation[] @expected) + { + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + foreach (var item in result.Invocations) + { + Assert.True(expected.Select(i => i.GetType()).Contains(item.GetType())); + } } } } diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/StoppedState.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/StoppedState.cs index 7e7c0cffc..516e366d6 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/StoppedState.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/Presence/StoppedState.cs @@ -3,6 +3,7 @@ using PubnubApi.EventEngine.Presence.Common; using PubnubApi.EventEngine.Presence.Events; using PubnubApi.EventEngine.Presence.States; +using System.Linq; namespace PubnubApi.Tests.EventEngine.Presence { @@ -12,59 +13,88 @@ internal class StoppedStateTransitions new object[] { new StoppedState(), new JoinedEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, - new StoppedState(), + new StoppedState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, + null }, new object[] { new StoppedState() { Input = new PresenceInput() { Channels = new [] { "a", "b" } } }, new LeftEvent() { Input = new PresenceInput() { Channels = new [] { "b" } } }, new StoppedState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, + null }, new object[] { new StoppedState() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new LeftEvent() { Input = new PresenceInput() { Channels = new [] { "a" } } }, new InactiveState(), + null }, new object[] { new StoppedState(), new LeftAllEvent(), new InactiveState(), + null }, new object[] { new StoppedState(), new HeartbeatSuccessEvent(), null, + null }, new object[] { new StoppedState(), new HeartbeatFailureEvent() { Status = new PNStatus() }, null, - }, - new object[] { - new StoppedState(), - new HeartbeatGiveUpEvent() { Status = new PNStatus() { Category = PNStatusCategory.PNCancelledCategory } }, - null, + null }, new object[] { new StoppedState(), new ReconnectEvent(), new HeartbeatingState(), + null }, new object[] { new StoppedState(), new DisconnectEvent(), null, + null }, new object[] { new StoppedState(), new TimesUpEvent(), null, + null }, }; - [TestCasesSource(nameof(testCases))] - public void TestTransition(State @sut, IEvent @ev, State @expected) + [TestCaseSource(nameof(testCases))] + public void TestTransition(APresenceState @sut, IEvent @ev, APresenceState @expected, IEffectInvocation[] @_) { - Assert.AreEqual(expected, sut.Transition(ev)); + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + Assert.AreEqual(@expected, result.State); + } + + [TestCaseSource(nameof(testCases))] + public void TestReturnedInvocations(State @sut, IEvent @ev, State @_, IEffectInvocation[] @expected) + { + var result = @sut.Transition(@ev); + + if (result == null && expected == null) + { + // it's expected result + return; + } + + foreach (var item in result.Invocations) + { + Assert.True(expected.Select(i => i.GetType()).Contains(item.GetType())); + } } } } diff --git a/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj b/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj index 714a4e134..141a87c0a 100644 --- a/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj +++ b/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj @@ -60,6 +60,12 @@ + + + + + +