public IEnumerator RealTimeWithAuthUrl_WhenTokenExpired_ShouldRetry_WhenRetryFails_ShouldSetError([ValueSource(nameof(_protocols))] Protocol protocol) { return(UniTask.ToCoroutine(async() => { var authRestClient = await AblySandbox.GetRestClient(protocol); var token = await authRestClient.Auth.RequestTokenAsync(new TokenParams { Ttl = TimeSpan.FromMilliseconds(1000) }); // this realtime client will have a key for the sandbox, thus a means to renew var realtimeClient = await AblySandbox.GetRealtimeClient(protocol, (options, _) => { options.TokenDetails = token; options.AuthUrl = new Uri(_errorUrl); options.AutoConnect = false; }); var awaiter = new TaskCompletionAwaiter(5000); realtimeClient.Connection.Once(ConnectionEvent.Disconnected, state => { state.Reason.Code.Should().Be(ErrorCodes.ClientAuthProviderRequestFailed); awaiter.SetCompleted(); }); await Task.Delay(2000); realtimeClient.Connect(); var result = await awaiter.Task; result.Should().BeTrue(); })); }
public async Task WhenWebSocketClientIsNull_SendShouldSetDisconnectedState(Protocol protocol) { var client = await GetRealtimeClient(protocol); await WaitForState(client); //wait for connection client.Connection.State.Should().Be(ConnectionState.Connected); var transportWrapper = client.ConnectionManager.Transport as TestTransportWrapper; transportWrapper.Should().NotBe(null); var wsTransport = transportWrapper._wrappedTransport as MsWebSocketTransport; wsTransport.Should().NotBe(null); wsTransport._socket.ClientWebSocket = null; var tca = new TaskCompletionAwaiter(); client.Connection.On(s => { if (s.Current == ConnectionState.Disconnected) { tca.SetCompleted(); } }); await client.Channels.Get("test").PublishAsync("event", "data"); await tca.Task; // should auto connect again await WaitForState(client); }
public async Task WhenFakeDisconnectedMessageContainsTokenError_ForcesClientToReauthenticate(Protocol protocol) { var reconnectAwaiter = new TaskCompletionAwaiter(); var client = await GetRealtimeClient(protocol, (options, settings) => { options.UseTokenAuth = true; }); await client.WaitForState(ConnectionState.Connected); var initialToken = client.RestClient.AblyAuth.CurrentToken; client.Connection.Once(ConnectionEvent.Disconnected, state2 => { client.Connection.Once(ConnectionEvent.Connected, state3 => { reconnectAwaiter.SetCompleted(); }); }); client.FakeProtocolMessageReceived(new ProtocolMessage(ProtocolMessage.MessageAction.Disconnected) { Error = new ErrorInfo("testing RTN22a", ErrorCodes.TokenError) }); var didReconnect = await reconnectAwaiter.Task; didReconnect.Should().BeTrue(); client.RestClient.AblyAuth.CurrentToken.Should().NotBe(initialToken); client.Close(); }
public async Task SubscribeDevice_ShouldUsePushDeviceAuthentication() { const string deviceIdentityToken = "identityToken"; var taskAwaiter = new TaskCompletionAwaiter(); async Task <AblyResponse> RequestHandler(AblyRequest request) { request.Headers.Should().ContainKey(Defaults.DeviceIdentityTokenHeader).WhoseValue.Should() .Be(deviceIdentityToken); taskAwaiter.SetCompleted(); return(new AblyResponse() { TextResponse = JsonConvert.SerializeObject(new PushChannelSubscription()) }); } var client = GetRestClient(RequestHandler, mobileDevice: new FakeMobileDevice()); client.Device = new LocalDevice() { Id = "id", DeviceIdentityToken = deviceIdentityToken }; var pushChannel = client.Channels.Get("test").Push; await pushChannel.SubscribeDevice(); (await taskAwaiter).Should().BeTrue("Didn't validate function"); }
public async Task Auth_WithRealtimeClient_WhenAuthFails_ShouldTransitionToOrRemainInTheCorrectState(Protocol protocol) { async Task TestConnectingBecomesDisconnected(string context, Action <ClientOptions, TestEnvironmentSettings> optionsAction) { TaskCompletionAwaiter tca = new TaskCompletionAwaiter(5000); var realtimeClient = await GetRealtimeClient(protocol, optionsAction); realtimeClient.Connection.On(ConnectionEvent.Disconnected, change => { change.Previous.Should().Be(ConnectionState.Connecting); change.Reason.Code.Should().Be(ErrorCodes.ClientAuthProviderRequestFailed); tca.SetCompleted(); }); realtimeClient.Connection.Connect(); await realtimeClient.ProcessCommands(); (await tca.Task).Should().BeTrue(context); } // authUrl fails void AuthUrlOptions(ClientOptions options, TestEnvironmentSettings settings) { options.AutoConnect = false; options.AuthUrl = new Uri(ErrorUrl); options.RealtimeRequestTimeout = TimeSpan.FromSeconds(2); options.HttpRequestTimeout = TimeSpan.FromSeconds(2); }
public Action <T> Wrap(Action <T> callback) { return(csc => { callback(csc); _tca.SetCompleted(); }); }
public async Task WhenConnectedMessageReceived_ShouldEmitUpdate(Protocol protocol) { var updateAwaiter = new TaskCompletionAwaiter(5000); var client = await GetRealtimeClient(protocol, (options, settings) => { options.UseTokenAuth = true; options.AutoConnect = true; }); await client.WaitForState(ConnectionState.Connected); client.Connection.ConnectionStateTtl.Should().NotBe(TimeSpan.MaxValue); var key = client.Connection.Key; client.Connection.Once(state => { // RTN4h - can emit UPDATE event if (state.Event == ConnectionEvent.Update) { // should have both previous and current attributes set to CONNECTED state.Current.Should().Be(ConnectionState.Connected); state.Previous.Should().Be(ConnectionState.Connected); state.Reason.Message = "fake-error"; updateAwaiter.SetCompleted(); } else { throw new Exception($"'{state.Event}' was handled. Only an 'Update' event should have occured"); } }); client.FakeProtocolMessageReceived(new ProtocolMessage(ProtocolMessage.MessageAction.Connected) { ConnectionDetails = new ConnectionDetails { ConnectionKey = "key", ClientId = "RTN21", ConnectionStateTtl = TimeSpan.MaxValue }, Error = new ErrorInfo("fake-error"), }); var didUpdate = await updateAwaiter.Task; didUpdate.Should().BeTrue(); // RTN21 - new connection details over write old values client.Connection.Key.Should().NotBe(key); client.ClientId.Should().Be("RTN21"); client.Connection.ConnectionStateTtl.Should().Be(TimeSpan.MaxValue); }
public async Task WhenSendingMessage_AckCallbackCalled_ForMultipleMessages() { // Arrange var client = await GetConnectedClient(); var callbacks = new List <ValueTuple <bool, ErrorInfo> >(); var message1 = new ProtocolMessage(ProtocolMessage.MessageAction.Message, "Test"); var message2 = new ProtocolMessage(ProtocolMessage.MessageAction.Message, "Test"); var message3 = new ProtocolMessage(ProtocolMessage.MessageAction.Message, "Test"); var awaiter = new TaskCompletionAwaiter(); Action <bool, ErrorInfo> GetCallback(int forCount) => (ack, err) => { if (callbacks.Count == forCount) { callbacks.Add((ack, err)); } if (callbacks.Count == 3) { awaiter.SetCompleted(); } }; var ackMessage = new ProtocolMessage(ProtocolMessage.MessageAction.Ack) { MsgSerial = 0, Count = 3 }; // Act client.Workflow.QueueAck(message1, GetCallback(0)); client.Workflow.QueueAck(message2, GetCallback(1)); client.Workflow.QueueAck(message3, GetCallback(2)); client.ExecuteCommand(ProcessMessageCommand.Create(ackMessage)); await client.ProcessCommands(); await awaiter.Task; // Assert callbacks.Count.Should().Be(3); Assert.True(callbacks.TrueForAll(c => c.Item1)); // Ack Assert.True(callbacks.TrueForAll(c => c.Item2 == null)); // No error }
public async Task WithConnectedClient_AuthorizeObtainsNewTokenAndUpgradesConnection_AndShouldEmitUpdate(Protocol protocol) { const string validClientId1 = "RTC8"; const string invalidClientId = "RTC8-incompatible-clientId"; // For a realtime client, Auth#authorize instructs the library to obtain // a token using the provided tokenParams and authOptions and upgrade // the current connection to use that token var client = await GetRealtimeClient(protocol, (opts, _) => { opts.ClientId = validClientId1; opts.UseTokenAuth = true; }); var awaiter = new TaskCompletionAwaiter(); client.Connection.On(ConnectionEvent.Update, args => { awaiter.SetCompleted(); }); await client.WaitForState(ConnectionState.Connected); var tokenDetails = await client.Auth.AuthorizeAsync(new TokenParams { ClientId = validClientId1 }); tokenDetails.ClientId.Should().Be(validClientId1); client.Connection.State.Should().Be(ConnectionState.Connected); client.RestClient.AblyAuth.CurrentToken.Should().Be(tokenDetails); var didUpdate = await awaiter.Task; client.Connection.State.Should().Be(ConnectionState.Connected); didUpdate.Should().BeTrue( "the AUTH message should trigger a CONNECTED response from the server that causes an UPDATE to be emitted."); client.Connection.On(args => { if (args.Current != ConnectionState.Failed) { Assert.True(false, $"unexpected state '{args.Current}'"); } }); // AuthorizeAsync will not return until either a CONNECTED or ERROR response // (or timeout) is seen from Ably, so we do not need to use WaitForState() here await Assert.ThrowsAsync <AblyException>(() => client.Auth.AuthorizeAsync(new TokenParams { ClientId = invalidClientId })); client.Connection.State.Should().Be(ConnectionState.Failed); }
public async Task ListSubscriptions_ShouldCallApiWithCorrectParameters() { const string channelName = "testChannel"; const string clientId = "clientId"; const string deviceid = "deviceId"; const string deviceIdentityToken = "token"; var taskAwaiter = new TaskCompletionAwaiter(); async Task <AblyResponse> RequestHandler(AblyRequest request) { request.Url.Should().Be("/push/channelSubscriptions"); request.Method.Should().Be(HttpMethod.Get); var queryParams = request.QueryParameters; queryParams.Should().ContainKey("clientId").WhoseValue.Should().Be(clientId); queryParams.Should().ContainKey("channel").WhoseValue.Should().Be(channelName); queryParams.Should().ContainKey("deviceId").WhoseValue.Should().Be(deviceid); queryParams.Should().ContainKey("concatFilters").WhoseValue.Should().Be("true"); taskAwaiter.SetCompleted(); return(new AblyResponse { TextResponse = JsonConvert.SerializeObject(new List <PushChannelSubscription>()) }); } var client = GetRestClient(RequestHandler, mobileDevice: new FakeMobileDevice()); client.Device = new LocalDevice() { DeviceIdentityToken = deviceIdentityToken, ClientId = clientId, Id = deviceid }; var pushChannel = client.Channels.Get(channelName).Push; var subscriptions = await pushChannel.ListSubscriptions(); subscriptions.Should().NotBeNull(); (await taskAwaiter).Should().BeTrue("Didn't validate function"); }
public async Task UnsubscribeDevice_ShouldSendADeleteRequestTo_PushChannelSubscriptions_WithCorrectParameters_And_AuthHeader() { const string channelName = "testChannel"; const string deviceId = "deviceId"; const string deviceIdentityToken = "token"; var taskAwaiter = new TaskCompletionAwaiter(); async Task <AblyResponse> RequestHandler(AblyRequest request) { // RSH7c2, check the correct request is made request.Url.Should().Be("/push/channelSubscriptions"); request.Method.Should().Be(HttpMethod.Delete); var queryParams = request.QueryParameters; queryParams.Should().ContainKey("deviceId").WhoseValue.Should().Be(deviceId); queryParams.Should().ContainKey("channel").WhoseValue.Should().Be(channelName); queryParams.Should().NotContainKey("clientId"); // Check the auth header RSH7c3 request.Headers.Should().ContainKey(Defaults.DeviceIdentityTokenHeader).WhoseValue.Should() .Be(deviceIdentityToken); taskAwaiter.SetCompleted(); return(new AblyResponse() { TextResponse = JsonConvert.SerializeObject(new PushChannelSubscription()) }); } var client = GetRestClient(RequestHandler, mobileDevice: new FakeMobileDevice()); client.Device = new LocalDevice() { Id = deviceId, DeviceIdentityToken = deviceIdentityToken }; var pushChannel = client.Channels.Get(channelName).Push; await pushChannel.UnsubscribeDevice(); (await taskAwaiter).Should().BeTrue("Didn't validate function"); }
/// <summary> /// This method yields the current thread and waits until the whole command queue is processed. /// </summary> /// <returns></returns> public static async Task ProcessCommands(this IRealtimeClient client) { var realtime = (AblyRealtime)client; var taskAwaiter = new TaskCompletionAwaiter(); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed _ = Task.Run(async() => { while (true) { await Task.Delay(50); if (realtime.Workflow.IsProcessingCommands() == false) { taskAwaiter.SetCompleted(); } } }); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed await taskAwaiter.Task; }
public async Task ShouldSuccessfullyPublishAPayload(Protocol protocol) { // Arrange var client = await GetRealtimeClient(protocol); var channelName = "pushenabled:test".AddRandomSuffix(); var channel = client.Channels.Get(channelName); await channel.AttachAsync(); var pushPayload = JObject.FromObject(new { notification = new { title = "test", body = "message body" }, data = new { foo = "bar" }, }); var pushRecipient = JObject.FromObject(new { transportType = "ablyChannel", channel = channelName, ablyKey = client.Options.Key, ablyUrl = "https://" + client.Options.FullRestHost(), }); var awaiter = new TaskCompletionAwaiter(); channel.Subscribe(message => { // Assert message.Name.Should().Be("__ably_push__"); var payload = JObject.Parse((string)message.Data); payload["data"].Should().BeEquivalentTo(pushPayload["data"]); ((string)payload["notification"]["title"]).Should().Be("test"); ((string)payload["notification"]["body"]).Should().Be("message body"); awaiter.SetCompleted(); }); // Act await client.Push.Admin.PublishAsync(pushRecipient, pushPayload); // Wait for 10 seconds for awaiter.SetCompleted() and make sure it was called. (await awaiter.Task).Should().BeTrue(); }
public async Task WhenDisconnectedMessageContainsTokenError_ForcesClientToReauthenticate(Protocol protocol) { var authClient = await GetRestClient(protocol); var reconnectAwaiter = new TaskCompletionAwaiter(60000); var client = await GetRealtimeClient(protocol, (options, settings) => { options.AuthCallback = tokenParams => { var results = authClient.AblyAuth.RequestToken(new TokenParams { ClientId = "RTN22a", Ttl = TimeSpan.FromSeconds(35) }); return(Task.FromResult <object>(results)); }; options.ClientId = "RTN22a"; }); await client.WaitForState(ConnectionState.Connected); var initialToken = client.RestClient.AblyAuth.CurrentToken; client.Connection.Once(ConnectionEvent.Disconnected, state2 => { client.Connection.Once(ConnectionEvent.Connected, state3 => { reconnectAwaiter.SetCompleted(); }); }); client.FakeProtocolMessageReceived(new ProtocolMessage(ProtocolMessage.MessageAction.Disconnected) { Error = new ErrorInfo("testing RTN22a", ErrorCodes.TokenError) }); var didReconnect = await reconnectAwaiter.Task; didReconnect.Should().BeTrue(); client.RestClient.AblyAuth.CurrentToken.Should().NotBe(initialToken); client.Close(); }
public async Task RealTimeWithAuthCallback_WhenTokenExpired_ShouldRetry_WhenRetryFails_ShouldSetError(Protocol protocol) { // create a short lived token var authRestClient = await GetRestClient(protocol); var token = await authRestClient.Auth.RequestTokenAsync(new TokenParams { Ttl = TimeSpan.FromMilliseconds(1000), }); bool didRetry = false; var realtimeClient = await GetRealtimeClient(protocol, (options, _) => { options.TokenDetails = token; options.AuthCallback = tokenParams => { didRetry = true; throw new Exception("AuthCallback failed"); }; options.AutoConnect = false; }); var awaiter = new TaskCompletionAwaiter(5000); realtimeClient.Connection.Once(ConnectionEvent.Disconnected, state => { state.Reason.Code.Should().Be(ErrorCodes.ClientAuthProviderRequestFailed); awaiter.SetCompleted(); }); await Task.Delay(2000); realtimeClient.Connect(); var result = await awaiter.Task; result.Should().BeTrue(); didRetry.Should().BeTrue(); }
public IEnumerator RealtimeWithAuthError_WhenTokenExpired_ShouldRetry_WhenRetryFails_ShouldSetError([ValueSource(nameof(_protocols))] Protocol protocol) { return(UniTask.ToCoroutine(async() => { var helper = new RSA4Helper(this); var restClient = await AblySandbox.GetRestClient(protocol); var token = await restClient.Auth.AuthorizeAsync(new TokenParams { Ttl = TimeSpan.FromMilliseconds(1000), }); // this realtime client will have a key for the sandbox, thus a means to renew var realtimeClient = await AblySandbox.GetRealtimeClient(protocol, (options, _) => { options.TokenDetails = token; options.AutoConnect = false; }); realtimeClient.RestClient.ExecuteHttpRequest = helper.AblyResponseWith500Status; var awaiter = new TaskCompletionAwaiter(5000); realtimeClient.Connection.Once(ConnectionEvent.Disconnected, state => { state.Reason.Code.Should().Be(ErrorCodes.ClientAuthProviderRequestFailed); awaiter.SetCompleted(); }); await Task.Delay(2000); realtimeClient.Connect(); var result = await awaiter.Task; result.Should().BeTrue(); helper.Requests.Count.Should().Be(1); helper.Requests[0].Url.EndsWith("requestToken").Should().BeTrue(); })); }
public async Task SubscribeChannel_ShouldSendARequestTo_PushChannelSubscriptions_WithCorrectParameters() { const string channelName = "testChannel"; const string clientId = "client101"; var taskAwaiter = new TaskCompletionAwaiter(); async Task <AblyResponse> RequestHandler(AblyRequest request) { request.Url.Should().Be("/push/channelSubscriptions"); var postData = (PushChannelSubscription)request.PostData; postData.Should().NotBeNull(); postData.Channel.Should().Be(channelName); postData.ClientId.Should().Be(clientId); postData.DeviceId.Should().BeNullOrEmpty(); taskAwaiter.SetCompleted(); return(new AblyResponse() { TextResponse = JsonConvert.SerializeObject(new PushChannelSubscription()) }); } var client = GetRestClient(RequestHandler, mobileDevice: new FakeMobileDevice()); client.Device = new LocalDevice() { ClientId = clientId, DeviceIdentityToken = "identityToken" }; var pushChannel = client.Channels.Get(channelName).Push; await pushChannel.SubscribeClient(); (await taskAwaiter).Should().BeTrue("Didn't validate function"); }
public IEnumerator Auth_WithRealtimeClient_WhenAuthFails_ShouldTransitionToOrRemainInTheCorrectState([ValueSource(nameof(_protocols))] Protocol protocol) { return(UniTask.ToCoroutine(async() => { async Task TestConnectingBecomesDisconnected(string context, Action <ClientOptions, TestEnvironmentSettings> optionsAction) { TaskCompletionAwaiter tca = new TaskCompletionAwaiter(5000); var realtimeClient = await AblySandbox.GetRealtimeClient(protocol, optionsAction); realtimeClient.Connection.On(ConnectionEvent.Disconnected, change => { change.Previous.Should().Be(ConnectionState.Connecting); change.Reason.Code.Should().Be(ErrorCodes.ClientAuthProviderRequestFailed); tca.SetCompleted(); }); realtimeClient.Connection.Connect(); await realtimeClient.ProcessCommands(); (await tca.Task).Should().BeTrue(context); } // authUrl fails void AuthUrlOptions(ClientOptions options, TestEnvironmentSettings settings) { options.AutoConnect = false; options.AuthUrl = new Uri(_errorUrl); options.RealtimeRequestTimeout = TimeSpan.FromSeconds(2); options.HttpRequestTimeout = TimeSpan.FromSeconds(2); } // authCallback fails static void AuthCallbackOptions(ClientOptions options, TestEnvironmentSettings settings) { options.AutoConnect = false; options.AuthCallback = (tokenParams) => throw new Exception("AuthCallback force error"); }
public async Task WhenAClientAttachedToPresenceChannel_ShouldEmitPresentForEachMember(Protocol protocol) { var channelName = "presence".AddRandomSuffix(); var clientA = await GetRealtimeClient(protocol); await clientA.WaitForState(ConnectionState.Connected); clientA.Connection.State.ShouldBeEquivalentTo(ConnectionState.Connected); var channelA = clientA.Channels.Get(channelName); channelA.Attach(); await channelA.WaitForState(ChannelState.Attached); channelA.State.ShouldBeEquivalentTo(ChannelState.Attached); // enters 250 members on a single connection A for (int i = 0; i < ExpectedEnterCount; i++) { var clientId = GetClientId(i); await channelA.Presence.EnterClientAsync(clientId, null); } var clientB = await GetRealtimeClient(protocol); await clientB.WaitForState(ConnectionState.Connected); clientB.Connection.State.ShouldBeEquivalentTo(ConnectionState.Connected); var channelB = clientB.Channels.Get(channelName); channelB.Attach(); await channelB.WaitForState(ChannelState.Attached); channelB.State.ShouldBeEquivalentTo(ChannelState.Attached); // checks for PRESENT events to be emitted on another connection for each member List <PresenceMessage> presenceMessages = new List <PresenceMessage>(); var awaiter = new TaskCompletionAwaiter(timeoutMs: 200000); channelB.Presence.Subscribe(x => { presenceMessages.Add(x); if (presenceMessages.Count == ExpectedEnterCount) { awaiter.SetCompleted(); } }); var received250MessagesBeforeTimeout = await awaiter.Task; received250MessagesBeforeTimeout.ShouldBeEquivalentTo(true); // all 250 members should be present in a Presence#get request var messages = await channelB.Presence.GetAsync(new GetOptions { WaitForSync = true }); var messageList = messages as IList <PresenceMessage> ?? messages.ToList(); messageList.Count().ShouldBeEquivalentTo(ExpectedEnterCount); foreach (var m in messageList) { presenceMessages.Select(x => x.ClientId == m.ClientId).Any().Should().BeTrue(); } clientA.Close(); clientB.Close(); }
public async Task WithConnectedClient_WhenDowngradingCapabilities_ChannelShouldBecomeFailed(Protocol protocol) { var clientId = "RTC8a1-downgrade".AddRandomSuffix(); var channelName = "RTC8a1-downgrade-channel".AddRandomSuffix(); var wrongChannelName = "wrong".AddRandomSuffix(); var(realtime, channel) = await SetupRealtimeClient(); channel.On(statechange => Output.WriteLine($"Changed state: {statechange.Previous} to {statechange.Current}. Error: {statechange.Error}")); realtime.Connection.Once(ConnectionEvent.Disconnected, change => throw new Exception("Should not require a disconnect")); var result = await channel.PublishAsync("test", "should-not-fail"); result.IsSuccess.Should().BeTrue(); ChannelStateChange stateChange = null; var failedAwaiter = new TaskCompletionAwaiter(2000); channel.Once(ChannelEvent.Failed, state => { stateChange = state; failedAwaiter.SetCompleted(); }); await DowngradeCapability(realtime); await channel.WaitForState(ChannelState.Failed, TimeSpan.FromSeconds(6)); await failedAwaiter.Task; stateChange.Should().NotBeNull("channel should have failed"); stateChange.Error.Code.Should().Be(ErrorCodes.OperationNotPermittedWithCapability); stateChange.Error.Message.Should().Contain("Channel denied access"); async Task DowngradeCapability(AblyRealtime rt) { var capability = new Capability(); capability.AddResource(wrongChannelName).AllowSubscribe(); var newToken = await rt.Auth.AuthorizeAsync(new TokenParams { Capability = capability, ClientId = clientId, }); newToken.Should().NotBeNull(); } async Task <(AblyRealtime, IRealtimeChannel)> SetupRealtimeClient() { var capability = new Capability(); capability.AddResource(channelName).AllowAll(); var restClient = await GetRestClient(protocol); var tokenDetails = await restClient.Auth.RequestTokenAsync(new TokenParams { ClientId = clientId, Capability = capability }); var rt = await GetRealtimeClient(protocol, (opts, _) => { opts.Token = tokenDetails.Token; }); await rt.WaitForState(ConnectionState.Connected); var ch = rt.Channels.Get(channelName); await ch.AttachAsync(); return(rt, ch); } }
public async Task WithConnectedClient_WhenUpgradingCapabilities_ConnectionShouldNotBeImpaired(Protocol protocol) { var clientId = "RTC8a1".AddRandomSuffix(); var capability = new Capability(); capability.AddResource("foo").AllowPublish(); var restClient = await GetRestClient(protocol, options => { options.UseTokenAuth = true; }); var tokenDetails = await restClient.Auth.RequestTokenAsync(new TokenParams { ClientId = clientId, Capability = capability, }); var realtime = await GetRealtimeClient(protocol, (opts, _) => { opts.Token = tokenDetails.Token; }); await realtime.WaitForState(); // upgrade of capabilities without any loss of continuity or connectivity realtime.Connection.Once(ConnectionEvent.Disconnected, change => throw new Exception("should not disconnect")); var fooChannel = realtime.Channels.Get("foo"); var barChannel = realtime.Channels.Get("bar"); var fooSuccessAWaiter = new TaskCompletionAwaiter(5000); fooChannel.Publish("test", "should-not-fail", (b, info) => { // foo should succeed b.Should().BeTrue(); info.Should().BeNull(); fooSuccessAWaiter.SetCompleted(); }); fooChannel.Attach(); Assert.True(await fooSuccessAWaiter.Task); var barFailAwaiter = new TaskCompletionAwaiter(5000); barChannel.Publish("test", "should-fail", (b, info) => { // bar should fail b.Should().BeFalse(); info.Code.Should().Be(ErrorCodes.OperationNotPermittedWithCapability); barFailAwaiter.SetCompleted(); }); barChannel.Attach(); Assert.True(await barFailAwaiter.Task); // upgrade bar capability = new Capability(); capability.AddResource("bar").AllowPublish(); await realtime.Auth.AuthorizeAsync(new TokenParams { Capability = capability, ClientId = clientId, }); realtime.Connection.State.Should().Be(ConnectionState.Connected); var barSuccessAwaiter = new TaskCompletionAwaiter(5000); barChannel.Attach((b2, info2) => { b2.Should().BeTrue(); barChannel.Publish("test", "should-succeed", (b, info) => { b.Should().BeTrue(); info.Should().BeNull(); barSuccessAwaiter.SetCompleted(); }); }); Assert.True(await barSuccessAwaiter.Task); }