diff --git a/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs b/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs index fc1d48a..2e08d5f 100644 --- a/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs +++ b/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs @@ -17,6 +17,7 @@ namespace SAHB.GraphQLClient.Subscription public class GraphQLSubscriptionClient : IGraphQLSubscriptionClient { private long _operationCounter = 1; + private object _locker = new object(); private readonly Dictionary _operations = new Dictionary(); private const int ReceiveChunkSize = 1024; @@ -91,7 +92,11 @@ public async Task> ExecuteOperation(params G throw new InvalidOperationException("GraphQLSubscriptionClient is not initilized"); // Get operationId - var operationId = _operationCounter++; + long operationId; + lock (_locker) + { + operationId = _operationCounter++; + } // Get query var selectionSet = FieldBuilder.GenerateSelectionSet(typeof(T)); diff --git a/tests/SAHB.GraphQL.Client.Subscription.Integration.Tests/Chat/TestChat.cs b/tests/SAHB.GraphQL.Client.Subscription.Integration.Tests/Chat/TestChat.cs index ebe51ae..f54e936 100644 --- a/tests/SAHB.GraphQL.Client.Subscription.Integration.Tests/Chat/TestChat.cs +++ b/tests/SAHB.GraphQL.Client.Subscription.Integration.Tests/Chat/TestChat.cs @@ -13,13 +13,13 @@ namespace SAHB.GraphQLClient.Subscription.Integration.Tests { - public class TestChat : IClassFixture> + public class TestChat { private readonly GraphQLWebApplicationFactory _factory; - public TestChat(GraphQLWebApplicationFactory factory) + public TestChat() { - _factory = factory; + _factory = new GraphQLWebApplicationFactory(); } [Fact] @@ -48,9 +48,9 @@ public async Task Test_Recieve_Message() // Act // Registrer operation - var operation = await graphQLSubscriptionClient.ExecuteOperation(); + var operation = await graphQLSubscriptionClient.ExecuteOperation(); - List> recievedData = new List>(); + List> recievedData = new List>(); operation.DataRecieved += (sender, e) => { recievedData.Add(e.ReceivedData); @@ -65,17 +65,233 @@ public async Task Test_Recieve_Message() sentAt = DateTime.Now.AddDays(-1) })).Execute(); + await Task.Delay(TimeSpan.FromSeconds(2)); + + // Assert + Assert.Single(recievedData); + } + + [Fact] + public async Task Test_Recieve_Multiple_Messages() + { + // Arrange + var tokenSource = new CancellationTokenSource(); + + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Create websocket GraphQL client + var wsClient = _factory.Server.CreateWebSocketClient(); + wsClient.ConfigureRequest = request => request.Headers.Add("Sec-WebSocket-Protocol", "graphql-ws"); + + var wsUri = new UriBuilder(_factory.Server.BaseAddress) + { + Scheme = "wss", + Path = "graphql" + }.Uri; + + var websocket = await wsClient.ConnectAsync(wsUri, tokenSource.Token); + + var graphQLSubscriptionClient = new GraphQLSubscriptionClient(websocket, tokenSource.Token, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + await graphQLSubscriptionClient.Initilize(); + + // Act + // Registrer operation + var operation = await graphQLSubscriptionClient.ExecuteOperation(); + + List> recievedData = new List>(); + operation.DataRecieved += (sender, e) => + { + recievedData.Add(e.ReceivedData); + }; await Task.Delay(TimeSpan.FromSeconds(1)); + // Send 2 messages + await graphQLClient.CreateMutation("http://localhost/graphql", arguments: new GraphQLQueryArgument("message", new MessageInputType + { + content = "Message 1", + fromId = "SAHB", + sentAt = DateTime.Now.AddDays(-1) + })).Execute(); + await graphQLClient.CreateMutation("http://localhost/graphql", arguments: new GraphQLQueryArgument("message", new MessageInputType + { + content = "Message 1", + fromId = "SAHB", + sentAt = DateTime.Now.AddDays(-1) + })).Execute(); + + await Task.Delay(TimeSpan.FromSeconds(2)); + // Assert + Assert.Equal(2, recievedData.Count); + } + + [Fact] + public async Task Test_Recieve_Message_By_User() + { + // Arrange + var tokenSource = new CancellationTokenSource(); + + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Create websocket GraphQL client + var wsClient = _factory.Server.CreateWebSocketClient(); + wsClient.ConfigureRequest = request => request.Headers.Add("Sec-WebSocket-Protocol", "graphql-ws"); + + var wsUri = new UriBuilder(_factory.Server.BaseAddress) + { + Scheme = "wss", + Path = "graphql" + }.Uri; + + var websocket = await wsClient.ConnectAsync(wsUri, tokenSource.Token); + + var graphQLSubscriptionClient = new GraphQLSubscriptionClient(websocket, tokenSource.Token, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + await graphQLSubscriptionClient.Initilize(); + + // Act + // Registrer operation + var operation = await graphQLSubscriptionClient.ExecuteOperation(new GraphQLQueryArgument("fromId", "SAHB")); + + List> recievedData = new List>(); + operation.DataRecieved += (sender, e) => + { + recievedData.Add(e.ReceivedData); + }; + await Task.Delay(TimeSpan.FromSeconds(1)); + + // Send 2 message from 2 users only one should be retrieved in subscription + await graphQLClient.CreateMutation("http://localhost/graphql", arguments: new GraphQLQueryArgument("message", new MessageInputType + { + content = "Message 1", + fromId = "SAHB", + sentAt = DateTime.Now.AddDays(-1) + })).Execute(); + + await graphQLClient.CreateMutation("http://localhost/graphql", arguments: new GraphQLQueryArgument("message", new MessageInputType + { + content = "Message 1", + fromId = "OtherUser", + sentAt = DateTime.Now.AddDays(-1) + })).Execute(); + + await Task.Delay(TimeSpan.FromSeconds(2)); + + // Assert + Assert.Single(recievedData); + } + + [Fact] + public async Task Test_Recieve_Multiple_Subscribers() + { + // Arrange + var tokenSource = new CancellationTokenSource(); + + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Create websocket GraphQL client + var wsClient = _factory.Server.CreateWebSocketClient(); + wsClient.ConfigureRequest = request => request.Headers.Add("Sec-WebSocket-Protocol", "graphql-ws"); + + var wsUri = new UriBuilder(_factory.Server.BaseAddress) + { + Scheme = "wss", + Path = "graphql" + }.Uri; + + var websocket = await wsClient.ConnectAsync(wsUri, tokenSource.Token); + + var graphQLSubscriptionClient = new GraphQLSubscriptionClient(websocket, tokenSource.Token, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + await graphQLSubscriptionClient.Initilize(); + + // Act + // Registrer operations + var operationByUser = await graphQLSubscriptionClient.ExecuteOperation(new GraphQLQueryArgument("fromId", "SAHB")); + + List> recievedDataByUser = new List>(); + operationByUser.DataRecieved += (sender, e) => + { + recievedDataByUser.Add(e.ReceivedData); + }; + + var operation = await graphQLSubscriptionClient.ExecuteOperation(); + + List> recievedData = new List>(); + operation.DataRecieved += (sender, e) => + { + recievedData.Add(e.ReceivedData); + }; + await Task.Delay(TimeSpan.FromSeconds(1)); + + // Send 2 message from 2 users only one should be retrieved in subscription + await graphQLClient.CreateMutation("http://localhost/graphql", arguments: new GraphQLQueryArgument("message", new MessageInputType + { + content = "Message 1", + fromId = "SAHB", + sentAt = DateTime.Now.AddDays(-1) + })).Execute(); + + await Task.Delay(TimeSpan.FromSeconds(1)); + + // Assert + Assert.Single(recievedDataByUser); Assert.Single(recievedData); } - public class MessageSubscription + [Fact] + public async Task Test_Complete() + { + // Arrange + var tokenSource = new CancellationTokenSource(); + + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Create websocket GraphQL client + var wsClient = _factory.Server.CreateWebSocketClient(); + wsClient.ConfigureRequest = request => request.Headers.Add("Sec-WebSocket-Protocol", "graphql-ws"); + + var wsUri = new UriBuilder(_factory.Server.BaseAddress) + { + Scheme = "wss", + Path = "graphql" + }.Uri; + + var websocket = await wsClient.ConnectAsync(wsUri, tokenSource.Token); + + var graphQLSubscriptionClient = new GraphQLSubscriptionClient(websocket, tokenSource.Token, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + await graphQLSubscriptionClient.Initilize(); + + // Act + List completedTimes = new List(); + + // Registrer operations and stop it + var operation = await graphQLSubscriptionClient.ExecuteOperation(); + operation.Completed += (sender, e) => + { + completedTimes.Add(true); + }; + await operation.Stop(); + + await Task.Delay(TimeSpan.FromSeconds(2)); + + // Assert + Assert.Single(completedTimes); + } + + public class MessageAddedSubscription { public Message MessageAdded { get; set; } } + public class MessageAddedByUserSubscription + { + [GraphQLArguments("id", "String", "fromId", IsRequired = true)] + public Message messageAddedByUser { get; set; } + } + public class Message { public Author From { get; set; } diff --git a/tests/SAHB.GraphQL.Client.Subscription.Integration.Tests/SAHB.GraphQL.Client.Subscription.Integration.Tests.csproj b/tests/SAHB.GraphQL.Client.Subscription.Integration.Tests/SAHB.GraphQL.Client.Subscription.Integration.Tests.csproj index faf2835..e611c03 100644 --- a/tests/SAHB.GraphQL.Client.Subscription.Integration.Tests/SAHB.GraphQL.Client.Subscription.Integration.Tests.csproj +++ b/tests/SAHB.GraphQL.Client.Subscription.Integration.Tests/SAHB.GraphQL.Client.Subscription.Integration.Tests.csproj @@ -7,8 +7,8 @@ - - + +