diff --git a/GraphQL.Client.sln b/GraphQL.Client.sln
index ab95f2ec..d6faf815 100644
--- a/GraphQL.Client.sln
+++ b/GraphQL.Client.sln
@@ -65,6 +65,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{89
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphQL.Client.Example", "examples\GraphQL.Client.Example\GraphQL.Client.Example.csproj", "{6B13B87D-1EF4-485F-BC5D-891E2F4DA6CD}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphQL.Client.TestHost", "src\GraphQL.Client.TestHost\GraphQL.Client.TestHost.csproj", "{01AE8466-3E48-4988-81F1-7F93F1531302}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -127,6 +129,10 @@ Global
{6B13B87D-1EF4-485F-BC5D-891E2F4DA6CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B13B87D-1EF4-485F-BC5D-891E2F4DA6CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6B13B87D-1EF4-485F-BC5D-891E2F4DA6CD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {01AE8466-3E48-4988-81F1-7F93F1531302}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {01AE8466-3E48-4988-81F1-7F93F1531302}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {01AE8466-3E48-4988-81F1-7F93F1531302}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {01AE8466-3E48-4988-81F1-7F93F1531302}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -146,6 +152,7 @@ Global
{0D307BAD-27AE-4A5D-8764-4AA2620B01E9} = {0B0EDB0F-FF67-4B78-A8DB-B5C23E1FEE8C}
{7FFFEC00-D751-4FFC-9FD4-E91858F9A1C5} = {47C98B55-08F1-4428-863E-2C5C876DEEFE}
{6B13B87D-1EF4-485F-BC5D-891E2F4DA6CD} = {89AD33AB-64F6-4F82-822F-21DF7A10CEC0}
+ {01AE8466-3E48-4988-81F1-7F93F1531302} = {47C98B55-08F1-4428-863E-2C5C876DEEFE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {387AC1AC-F90C-4EF8-955A-04D495C75AF4}
diff --git a/src/GraphQL.Client.Abstractions.Websocket/IWebSocketFactory.cs b/src/GraphQL.Client.Abstractions.Websocket/IWebSocketFactory.cs
new file mode 100644
index 00000000..8fd9273e
--- /dev/null
+++ b/src/GraphQL.Client.Abstractions.Websocket/IWebSocketFactory.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Net.WebSockets;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace GraphQL.Client.Abstractions.Websocket
+{
+ ///
+ /// creates and returns a configured and connected instance
+ ///
+ public interface IWebSocketFactory
+ {
+ Task ConnectAsync(Uri webSocketUri, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/GraphQL.Client.TestHost/GraphQL.Client.TestHost.csproj b/src/GraphQL.Client.TestHost/GraphQL.Client.TestHost.csproj
new file mode 100644
index 00000000..c8658076
--- /dev/null
+++ b/src/GraphQL.Client.TestHost/GraphQL.Client.TestHost.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net461;netstandard2.1
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/GraphQL.Client.TestHost/TestServerExtensions.cs b/src/GraphQL.Client.TestHost/TestServerExtensions.cs
new file mode 100644
index 00000000..5bc17379
--- /dev/null
+++ b/src/GraphQL.Client.TestHost/TestServerExtensions.cs
@@ -0,0 +1,13 @@
+using System;
+using GraphQL.Client.Abstractions.Websocket;
+using GraphQL.Client.Http;
+using Microsoft.AspNetCore.TestHost;
+
+namespace GraphQL.Client.TestHost
+{
+ public static class TestServerExtensions
+ {
+ public static GraphQLHttpClient CreateGraphQLHttpClient(this TestServer testServer, GraphQLHttpClientOptions options, IGraphQLWebsocketJsonSerializer serializer)
+ => new GraphQLHttpClient(options, serializer, testServer.CreateClient(), new TestServerWebSocketFactory(testServer));
+ }
+}
diff --git a/src/GraphQL.Client.TestHost/TestServerWebSocketFactory.cs b/src/GraphQL.Client.TestHost/TestServerWebSocketFactory.cs
new file mode 100644
index 00000000..a6ba4d52
--- /dev/null
+++ b/src/GraphQL.Client.TestHost/TestServerWebSocketFactory.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Net.WebSockets;
+using System.Threading;
+using System.Threading.Tasks;
+using GraphQL.Client.Abstractions.Websocket;
+using GraphQL.Client.Http.Websocket;
+using Microsoft.AspNetCore.TestHost;
+
+namespace GraphQL.Client.TestHost
+{
+ public class TestServerWebSocketFactory: IWebSocketFactory
+ {
+ private readonly WebSocketClient _webSocketClient;
+
+ public TestServerWebSocketFactory(TestServer testServer)
+ {
+ _webSocketClient = testServer.CreateWebSocketClient();
+ _webSocketClient.ConfigureRequest = r =>
+ {
+ r.Headers["Sec-WebSocket-Protocol"] = "graphql-ws";
+ };
+ }
+
+ public Task ConnectAsync(Uri webSocketUri, CancellationToken cancellationToken)
+ => _webSocketClient.ConnectAsync(webSocketUri, cancellationToken);
+ }
+}
diff --git a/src/GraphQL.Client/GraphQLHttpClient.cs b/src/GraphQL.Client/GraphQLHttpClient.cs
index 9d2aac47..a525246c 100644
--- a/src/GraphQL.Client/GraphQLHttpClient.cs
+++ b/src/GraphQL.Client/GraphQLHttpClient.cs
@@ -5,16 +5,20 @@
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
+using System.Net.WebSockets;
+using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using GraphQL.Client.Abstractions;
using GraphQL.Client.Abstractions.Websocket;
using GraphQL.Client.Http.Websocket;
-
+[assembly:InternalsVisibleTo("GraphQL.Client.TestHost")]
+[assembly:InternalsVisibleTo("GraphQL.Integration.Tests")]
namespace GraphQL.Client.Http
{
public class GraphQLHttpClient : IGraphQLClient
{
+ private readonly IWebSocketFactory _webSocketFactory;
private readonly Lazy _lazyHttpWebSocket;
private GraphQLHttpWebSocket GraphQlHttpWebSocket => _lazyHttpWebSocket.Value;
@@ -63,16 +67,17 @@ public GraphQLHttpClient(GraphQLHttpClientOptions options, IGraphQLWebsocketJson
_disposeHttpClient = true;
}
- public GraphQLHttpClient(GraphQLHttpClientOptions options, IGraphQLWebsocketJsonSerializer serializer, HttpClient httpClient)
+ public GraphQLHttpClient(GraphQLHttpClientOptions options, IGraphQLWebsocketJsonSerializer serializer, HttpClient httpClient, IWebSocketFactory? webSocketFactory = null)
{
Options = options ?? throw new ArgumentNullException(nameof(options));
JsonSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer), "please configure the JSON serializer you want to use");
HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
+ _webSocketFactory = webSocketFactory ?? WebSocketFactoryHelper.GetDefaultWebSocketFactory(options);
if (!HttpClient.DefaultRequestHeaders.UserAgent.Any())
HttpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(GetType().Assembly.GetName().Name, GetType().Assembly.GetName().Version.ToString()));
-
- _lazyHttpWebSocket = new Lazy(CreateGraphQLHttpWebSocket);
+
+ _lazyHttpWebSocket = new Lazy(()=>CreateGraphQLHttpWebSocket());
}
#endregion
@@ -171,7 +176,7 @@ private GraphQLHttpWebSocket CreateGraphQLHttpWebSocket()
if (!webSocketEndpoint.HasWebSocketScheme())
throw new InvalidOperationException($"uri \"{webSocketEndpoint}\" is not a websocket endpoint");
- return new GraphQLHttpWebSocket(webSocketEndpoint, this);
+ return new GraphQLHttpWebSocket(webSocketEndpoint, this, _webSocketFactory);
}
#endregion
diff --git a/src/GraphQL.Client/Websocket/ClientWebSocketFactory.cs b/src/GraphQL.Client/Websocket/ClientWebSocketFactory.cs
new file mode 100644
index 00000000..d2222644
--- /dev/null
+++ b/src/GraphQL.Client/Websocket/ClientWebSocketFactory.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Diagnostics;
+using System.Net.Http;
+using System.Net.WebSockets;
+using System.Threading;
+using System.Threading.Tasks;
+using GraphQL.Client.Abstractions.Websocket;
+
+namespace GraphQL.Client.Http.Websocket
+{
+#if NETSTANDARD
+ ///
+ /// Default web socket factory for netstandard2.0
+ ///
+ public class ClientWebSocketFactory : IWebSocketFactory
+ {
+ private readonly GraphQLHttpClientOptions _options;
+
+ public ClientWebSocketFactory(GraphQLHttpClientOptions options)
+ {
+ _options = options;
+ }
+
+ public async Task ConnectAsync(Uri webSocketUri, CancellationToken cancellationToken)
+ {
+ var webSocket = new ClientWebSocket();
+ webSocket.Options.AddSubProtocol("graphql-ws");
+
+ // the following properties are not supported in Blazor WebAssembly and throw a PlatformNotSupportedException error when accessed
+ try
+ {
+ webSocket.Options.ClientCertificates = ((HttpClientHandler) _options.HttpMessageHandler).ClientCertificates;
+ }
+ catch (NotImplementedException)
+ {
+ Debug.WriteLine("property 'ClientWebSocketOptions.ClientCertificates' not implemented by current platform");
+ }
+ catch (PlatformNotSupportedException)
+ {
+ Debug.WriteLine("property 'ClientWebSocketOptions.ClientCertificates' not supported by current platform");
+ }
+
+ try
+ {
+ webSocket.Options.UseDefaultCredentials =
+ ((HttpClientHandler) _options.HttpMessageHandler).UseDefaultCredentials;
+ }
+ catch (NotImplementedException)
+ {
+ Debug.WriteLine("property 'ClientWebSocketOptions.UseDefaultCredentials' not implemented by current platform");
+ }
+ catch (PlatformNotSupportedException)
+ {
+ Debug.WriteLine("Property 'ClientWebSocketOptions.UseDefaultCredentials' not supported by current platform");
+ }
+
+ _options.ConfigureWebsocketOptions(webSocket.Options);
+
+ Debug.WriteLine($"opening websocket {webSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})");
+ await webSocket.ConnectAsync(webSocketUri, cancellationToken);
+ return webSocket;
+ }
+ }
+#endif
+}
diff --git a/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs b/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs
index ec4ac4b7..7eaaadd8 100644
--- a/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs
+++ b/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs
@@ -30,6 +30,7 @@ internal class GraphQLHttpWebSocket : IDisposable
private readonly BehaviorSubject _stateSubject =
new BehaviorSubject(GraphQLWebsocketConnectionState.Disconnected);
private readonly IDisposable _requestSubscription;
+ private readonly IWebSocketFactory _webSocketFactory;
private int _connectionAttempt = 0;
private IConnectableObservable _incomingMessages;
@@ -39,11 +40,7 @@ internal class GraphQLHttpWebSocket : IDisposable
private Task _initializeWebSocketTask = Task.CompletedTask;
private readonly object _initializeLock = new object();
-#if NETFRAMEWORK
- private WebSocket _clientWebSocket = null;
-#else
- private ClientWebSocket _clientWebSocket = null;
-#endif
+ private WebSocket _clientWebSocket = null;
#endregion
@@ -71,11 +68,12 @@ internal class GraphQLHttpWebSocket : IDisposable
#endregion
- public GraphQLHttpWebSocket(Uri webSocketUri, GraphQLHttpClient client)
+ public GraphQLHttpWebSocket(Uri webSocketUri, GraphQLHttpClient client, IWebSocketFactory webSocketFactory)
{
_internalCancellationToken = _internalCancellationTokenSource.Token;
_webSocketUri = webSocketUri;
_client = client;
+ _webSocketFactory = webSocketFactory;
_buffer = new ArraySegment(new byte[8192]);
IncomingMessageStream = GetMessageStream();
@@ -182,7 +180,7 @@ public IObservable> CreateSubscriptionStream ReceiveWebsocketMessagesAsync()
return response;
case WebSocketMessageType.Close:
- var closeResponse = await _client.JsonSerializer.DeserializeToWebsocketResponseWrapperAsync(ms);
- closeResponse.MessageBytes = ms.ToArray();
Debug.WriteLine($"Connection closed by the server.");
throw new Exception("Connection closed by the server.");
diff --git a/src/GraphQL.Client/Websocket/NetFrameworkWebSocketFactory.cs b/src/GraphQL.Client/Websocket/NetFrameworkWebSocketFactory.cs
new file mode 100644
index 00000000..a867a1fd
--- /dev/null
+++ b/src/GraphQL.Client/Websocket/NetFrameworkWebSocketFactory.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Diagnostics;
+using System.Net.Http;
+using System.Net.WebSockets;
+using System.Threading;
+using System.Threading.Tasks;
+using GraphQL.Client.Abstractions.Websocket;
+
+namespace GraphQL.Client.Http.Websocket
+{
+
+#if NETFRAMEWORK
+ ///
+ /// Default web socket factory for net4.6.1 (including support for Windows 7 and Windows Server 2008)
+ ///
+ public class NetFrameworkWebSocketFactory : IWebSocketFactory
+ {
+ private readonly GraphQLHttpClientOptions _options;
+
+ public NetFrameworkWebSocketFactory(GraphQLHttpClientOptions options)
+ {
+ _options = options;
+ }
+
+ public async Task ConnectAsync(Uri webSocketUri, CancellationToken cancellationToken)
+ {
+ // fix websocket not supported on win 7 using
+ // https://github.com/PingmanTools/System.Net.WebSockets.Client.Managed
+ var webSocket = SystemClientWebSocket.CreateClientWebSocket();
+ switch (webSocket) {
+ case ClientWebSocket nativeWebSocket:
+ nativeWebSocket.Options.AddSubProtocol("graphql-ws");
+ nativeWebSocket.Options.ClientCertificates = ((HttpClientHandler)_options.HttpMessageHandler).ClientCertificates;
+ nativeWebSocket.Options.UseDefaultCredentials = ((HttpClientHandler)_options.HttpMessageHandler).UseDefaultCredentials;
+ _options.ConfigureWebsocketOptions(nativeWebSocket.Options);
+ break;
+ case System.Net.WebSockets.Managed.ClientWebSocket managedWebSocket:
+ managedWebSocket.Options.AddSubProtocol("graphql-ws");
+ managedWebSocket.Options.ClientCertificates = ((HttpClientHandler)_options.HttpMessageHandler).ClientCertificates;
+ managedWebSocket.Options.UseDefaultCredentials = ((HttpClientHandler)_options.HttpMessageHandler).UseDefaultCredentials;
+ break;
+ default:
+ throw new NotSupportedException($"unknown websocket type {webSocket.GetType().Name}");
+ }
+
+ Debug.WriteLine($"opening websocket {webSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})");
+ await webSocket.ConnectAsync(webSocketUri, cancellationToken);
+ return webSocket;
+ }
+ }
+#endif
+}
diff --git a/src/GraphQL.Client/Websocket/WebSocketFactoryHelper.cs b/src/GraphQL.Client/Websocket/WebSocketFactoryHelper.cs
new file mode 100644
index 00000000..c59d3fd8
--- /dev/null
+++ b/src/GraphQL.Client/Websocket/WebSocketFactoryHelper.cs
@@ -0,0 +1,16 @@
+using GraphQL.Client.Abstractions.Websocket;
+
+namespace GraphQL.Client.Http.Websocket
+{
+ public static class WebSocketFactoryHelper
+ {
+ public static IWebSocketFactory GetDefaultWebSocketFactory(GraphQLHttpClientOptions options)
+ {
+#if NETFRAMEWORK
+ return new NetFrameworkWebSocketFactory(options);
+#else
+ return new ClientWebSocketFactory(options);
+#endif
+ }
+ }
+}
diff --git a/tests/GraphQL.Integration.Tests/GraphQL.Integration.Tests.csproj b/tests/GraphQL.Integration.Tests/GraphQL.Integration.Tests.csproj
index 6aca8a5b..facb750c 100644
--- a/tests/GraphQL.Integration.Tests/GraphQL.Integration.Tests.csproj
+++ b/tests/GraphQL.Integration.Tests/GraphQL.Integration.Tests.csproj
@@ -7,6 +7,7 @@
+
@@ -14,6 +15,7 @@
+
diff --git a/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs b/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs
index 25773b47..f13c67cc 100644
--- a/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs
+++ b/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs
@@ -14,7 +14,7 @@ public abstract class IntegrationServerTestFixture
{
public int Port { get; private set; }
- public IWebHost Server { get; private set; }
+ public IWebHost Server { get; protected set; }
public abstract IGraphQLWebsocketJsonSerializer Serializer { get; }
@@ -23,7 +23,7 @@ public IntegrationServerTestFixture()
Port = NetworkHelpers.GetFreeTcpPortNumber();
}
- public async Task CreateServer()
+ public virtual async Task CreateServer()
{
if (Server != null)
return;
@@ -46,21 +46,11 @@ public GraphQLHttpClient GetStarWarsClient(bool requestsViaWebsocket = false)
public GraphQLHttpClient GetChatClient(bool requestsViaWebsocket = false)
=> GetGraphQLClient(Common.CHAT_ENDPOINT, requestsViaWebsocket);
- private GraphQLHttpClient GetGraphQLClient(string endpoint, bool requestsViaWebsocket = false)
+ protected virtual GraphQLHttpClient GetGraphQLClient(string endpoint, bool requestsViaWebsocket = false)
{
if (Serializer == null)
throw new InvalidOperationException("JSON serializer not configured");
return WebHostHelpers.GetGraphQLClient(Port, endpoint, requestsViaWebsocket, Serializer);
}
}
-
- public class NewtonsoftIntegrationServerTestFixture : IntegrationServerTestFixture
- {
- public override IGraphQLWebsocketJsonSerializer Serializer { get; } = new NewtonsoftJsonSerializer();
- }
-
- public class SystemTextJsonIntegrationServerTestFixture : IntegrationServerTestFixture
- {
- public override IGraphQLWebsocketJsonSerializer Serializer { get; } = new SystemTextJsonSerializer();
- }
}
diff --git a/tests/GraphQL.Integration.Tests/Helpers/NewtonsoftIntegrationServerTestFixture.cs b/tests/GraphQL.Integration.Tests/Helpers/NewtonsoftIntegrationServerTestFixture.cs
new file mode 100644
index 00000000..317a295f
--- /dev/null
+++ b/tests/GraphQL.Integration.Tests/Helpers/NewtonsoftIntegrationServerTestFixture.cs
@@ -0,0 +1,10 @@
+using GraphQL.Client.Abstractions.Websocket;
+using GraphQL.Client.Serializer.Newtonsoft;
+
+namespace GraphQL.Integration.Tests.Helpers
+{
+ public class NewtonsoftIntegrationServerTestFixture : IntegrationServerTestFixture
+ {
+ public override IGraphQLWebsocketJsonSerializer Serializer { get; } = new NewtonsoftJsonSerializer();
+ }
+}
\ No newline at end of file
diff --git a/tests/GraphQL.Integration.Tests/Helpers/SystemTextJsonIntegrationServerTestFixture.cs b/tests/GraphQL.Integration.Tests/Helpers/SystemTextJsonIntegrationServerTestFixture.cs
new file mode 100644
index 00000000..a971825c
--- /dev/null
+++ b/tests/GraphQL.Integration.Tests/Helpers/SystemTextJsonIntegrationServerTestFixture.cs
@@ -0,0 +1,10 @@
+using GraphQL.Client.Abstractions.Websocket;
+using GraphQL.Client.Serializer.SystemTextJson;
+
+namespace GraphQL.Integration.Tests.Helpers
+{
+ public class SystemTextJsonIntegrationServerTestFixture : IntegrationServerTestFixture
+ {
+ public override IGraphQLWebsocketJsonSerializer Serializer { get; } = new SystemTextJsonSerializer();
+ }
+}
\ No newline at end of file
diff --git a/tests/GraphQL.Integration.Tests/Helpers/TestServerTestFixture.cs b/tests/GraphQL.Integration.Tests/Helpers/TestServerTestFixture.cs
new file mode 100644
index 00000000..c135caec
--- /dev/null
+++ b/tests/GraphQL.Integration.Tests/Helpers/TestServerTestFixture.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Net.Http;
+using System.Threading.Tasks;
+using GraphQL.Client.Http;
+using GraphQL.Client.TestHost;
+using IntegrationTestServer;
+using MartinCostello.Logging.XUnit;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.Logging;
+using Xunit.Abstractions;
+
+namespace GraphQL.Integration.Tests.Helpers
+{
+ public abstract class TestServerTestFixture : IntegrationServerTestFixture
+ {
+ private TestServer _testServer;
+ public ITestOutputHelper Output { get; set; }
+ public override async Task CreateServer()
+ {
+ var host =
+ new WebHostBuilder()
+ .UseStartup()
+ .ConfigureLogging((ctx, logging) =>
+ {
+ logging.AddProvider(new XUnitLoggerProvider(Output, new XUnitLoggerOptions()));
+ logging.SetMinimumLevel(LogLevel.Trace);
+ });
+
+ _testServer = new TestServer(host);
+ Server = _testServer.Host;
+ await _testServer.Host.StartAsync();
+ }
+
+ protected override GraphQLHttpClient GetGraphQLClient(string endpoint, bool requestsViaWebsocket = false)
+ {
+ if (Serializer == null)
+ throw new InvalidOperationException("JSON serializer not configured");
+
+ return _testServer.CreateGraphQLHttpClient(new GraphQLHttpClientOptions
+ {
+ EndPoint = new UriBuilder(_testServer.BaseAddress) { Path = endpoint }.Uri,
+ UseWebSocketForQueriesAndMutations = requestsViaWebsocket
+ },
+ Serializer);
+ }
+ }
+}
diff --git a/tests/GraphQL.Integration.Tests/Helpers/TestServerTestNewtonsoftFixture.cs b/tests/GraphQL.Integration.Tests/Helpers/TestServerTestNewtonsoftFixture.cs
new file mode 100644
index 00000000..aee9df82
--- /dev/null
+++ b/tests/GraphQL.Integration.Tests/Helpers/TestServerTestNewtonsoftFixture.cs
@@ -0,0 +1,10 @@
+using GraphQL.Client.Abstractions.Websocket;
+using GraphQL.Client.Serializer.Newtonsoft;
+
+namespace GraphQL.Integration.Tests.Helpers
+{
+ public class TestServerTestNewtonsoftFixture : TestServerTestFixture
+ {
+ public override IGraphQLWebsocketJsonSerializer Serializer { get; } = new NewtonsoftJsonSerializer();
+ }
+}
\ No newline at end of file
diff --git a/tests/GraphQL.Integration.Tests/Helpers/TestServerTestSystemTextFixture.cs b/tests/GraphQL.Integration.Tests/Helpers/TestServerTestSystemTextFixture.cs
new file mode 100644
index 00000000..6a208b9c
--- /dev/null
+++ b/tests/GraphQL.Integration.Tests/Helpers/TestServerTestSystemTextFixture.cs
@@ -0,0 +1,10 @@
+using GraphQL.Client.Abstractions.Websocket;
+using GraphQL.Client.Serializer.SystemTextJson;
+
+namespace GraphQL.Integration.Tests.Helpers
+{
+ public class TestServerTestSystemTextFixture : TestServerTestFixture
+ {
+ public override IGraphQLWebsocketJsonSerializer Serializer { get; } = new SystemTextJsonSerializer();
+ }
+}
\ No newline at end of file
diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs
index 4bedde89..71cc610a 100644
--- a/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs
+++ b/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs
@@ -1,16 +1,12 @@
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using FluentAssertions.Execution;
-using FluentAssertions.Extensions;
using GraphQL.Client.Abstractions;
-using GraphQL.Client.Abstractions.Websocket;
using GraphQL.Client.Http;
using GraphQL.Client.Tests.Common.Chat;
using GraphQL.Client.Tests.Common.Chat.Schema;
@@ -81,7 +77,7 @@ public async void CanUseWebSocketScheme()
response.Errors.Should().BeNullOrEmpty();
response.Data.AddMessage.Content.Should().Be(message);
}
-
+
[Fact]
public async void CanUseDedicatedWebSocketEndpoint()
{
@@ -94,7 +90,7 @@ public async void CanUseDedicatedWebSocketEndpoint()
response.Errors.Should().BeNullOrEmpty();
response.Data.AddMessage.Content.Should().Be(message);
}
-
+
[Fact]
public async void CanUseDedicatedWebSocketEndpointWithoutHttpEndpoint()
{
@@ -161,7 +157,7 @@ public async void CanHandleRequestErrorViaWebsocket()
}
}";
- private readonly GraphQLRequest _subscriptionRequest = new GraphQLRequest(SUBSCRIPTION_QUERY);
+ protected readonly GraphQLRequest _subscriptionRequest = new GraphQLRequest(SUBSCRIPTION_QUERY);
[Fact]
@@ -360,79 +356,6 @@ public async void CanConnectTwoSubscriptionsSimultaneously()
}
- [Fact]
- public async void CanHandleConnectionTimeout()
- {
- var errorMonitor = new CallbackMonitor();
- var reconnectBlocker = new ManualResetEventSlim(false);
-
- var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected();
- // configure back-off strategy to allow it to be controlled from within the unit test
- ChatClient.Options.BackOffStrategy = i =>
- {
- Debug.WriteLine("back-off strategy: waiting on reconnect blocker");
- reconnectBlocker.Wait();
- Debug.WriteLine("back-off strategy: reconnecting...");
- return TimeSpan.Zero;
- };
-
- var websocketStates = new ConcurrentQueue();
-
- using (ChatClient.WebsocketConnectionState.Subscribe(websocketStates.Enqueue))
- {
- websocketStates.Should().ContainSingle(state => state == GraphQLWebsocketConnectionState.Disconnected);
-
- Debug.WriteLine($"Test method thread id: {Thread.CurrentThread.ManagedThreadId}");
- Debug.WriteLine("creating subscription stream");
- var observable = ChatClient.CreateSubscriptionStream(_subscriptionRequest, errorMonitor.Invoke);
-
- Debug.WriteLine("subscribing...");
- var observer = observable.Observe();
- callbackMonitor.Should().HaveBeenInvokedWithPayload();
-
- websocketStates.Should().ContainInOrder(
- GraphQLWebsocketConnectionState.Disconnected,
- GraphQLWebsocketConnectionState.Connecting,
- GraphQLWebsocketConnectionState.Connected);
- // clear the collection so the next tests on the collection work as expected
- websocketStates.Clear();
-
- await observer.Should().PushAsync(1);
- observer.RecordedMessages.Last().Data.MessageAdded.Content.Should().Be(InitialMessage.Content);
-
- const string message1 = "Hello World";
- var response = await ChatClient.AddMessageAsync(message1);
- response.Data.AddMessage.Content.Should().Be(message1);
- await observer.Should().PushAsync(2);
- observer.RecordedMessages.Last().Data.MessageAdded.Content.Should().Be(message1);
-
- Debug.WriteLine("stopping web host...");
- await Fixture.ShutdownServer();
- Debug.WriteLine("web host stopped");
-
- errorMonitor.Should().HaveBeenInvokedWithPayload(10.Seconds())
- .Which.Should().BeOfType();
- websocketStates.Should().Contain(GraphQLWebsocketConnectionState.Disconnected);
-
- Debug.WriteLine("restarting web host...");
- await InitializeAsync();
- Debug.WriteLine("web host started");
- reconnectBlocker.Set();
- callbackMonitor.Should().HaveBeenInvokedWithPayload(3.Seconds());
- await observer.Should().PushAsync(3);
- observer.RecordedMessages.Last().Data.MessageAdded.Content.Should().Be(InitialMessage.Content);
-
- websocketStates.Should().ContainInOrder(
- GraphQLWebsocketConnectionState.Disconnected,
- GraphQLWebsocketConnectionState.Connecting,
- GraphQLWebsocketConnectionState.Connected);
-
- // disposing the client should complete the subscription
- ChatClient.Dispose();
- await observer.Should().CompleteAsync(5.Seconds());
- }
- }
-
[Fact]
public async void CanHandleSubscriptionError()
{
diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/BaseWithTimeout.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/BaseWithTimeout.cs
new file mode 100644
index 00000000..37b70d0d
--- /dev/null
+++ b/tests/GraphQL.Integration.Tests/WebsocketTests/BaseWithTimeout.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.Linq;
+using System.Net.WebSockets;
+using System.Threading;
+using FluentAssertions;
+using FluentAssertions.Extensions;
+using GraphQL.Client.Abstractions.Websocket;
+using GraphQL.Client.Tests.Common.Chat;
+using GraphQL.Client.Tests.Common.FluentAssertions.Reactive;
+using GraphQL.Client.Tests.Common.Helpers;
+using GraphQL.Integration.Tests.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace GraphQL.Integration.Tests.WebsocketTests
+{
+ public abstract class BaseWithTimeout:Base
+ {
+
+ protected BaseWithTimeout(ITestOutputHelper output, IntegrationServerTestFixture fixture) : base(output, fixture)
+ {
+ }
+
+ [Fact]
+ public async void CanHandleConnectionTimeout()
+ {
+ var errorMonitor = new CallbackMonitor();
+ var reconnectBlocker = new ManualResetEventSlim(false);
+
+ var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected();
+ // configure back-off strategy to allow it to be controlled from within the unit test
+ ChatClient.Options.BackOffStrategy = i =>
+ {
+ Debug.WriteLine("back-off strategy: waiting on reconnect blocker");
+ reconnectBlocker.Wait();
+ Debug.WriteLine("back-off strategy: reconnecting...");
+ return TimeSpan.Zero;
+ };
+
+ var websocketStates = new ConcurrentQueue();
+
+ using (ChatClient.WebsocketConnectionState.Subscribe(websocketStates.Enqueue))
+ {
+ websocketStates.Should().ContainSingle(state => state == GraphQLWebsocketConnectionState.Disconnected);
+
+ Debug.WriteLine($"Test method thread id: {Thread.CurrentThread.ManagedThreadId}");
+ Debug.WriteLine("creating subscription stream");
+ var observable = ChatClient.CreateSubscriptionStream(_subscriptionRequest, errorMonitor.Invoke);
+
+ Debug.WriteLine("subscribing...");
+ var observer = observable.Observe();
+ callbackMonitor.Should().HaveBeenInvokedWithPayload();
+
+ websocketStates.Should().ContainInOrder(
+ GraphQLWebsocketConnectionState.Disconnected,
+ GraphQLWebsocketConnectionState.Connecting,
+ GraphQLWebsocketConnectionState.Connected);
+ // clear the collection so the next tests on the collection work as expected
+ websocketStates.Clear();
+
+ await observer.Should().PushAsync(1);
+ observer.RecordedMessages.Last().Data.MessageAdded.Content.Should().Be(InitialMessage.Content);
+
+ const string message1 = "Hello World";
+ var response = await ChatClient.AddMessageAsync(message1);
+ response.Data.AddMessage.Content.Should().Be(message1);
+ await observer.Should().PushAsync(2);
+ observer.RecordedMessages.Last().Data.MessageAdded.Content.Should().Be(message1);
+
+ Debug.WriteLine("stopping web host...");
+ await Fixture.ShutdownServer();
+ Debug.WriteLine("web host stopped");
+
+ errorMonitor.Should().HaveBeenInvokedWithPayload(100.Seconds())
+ .Which.Should().BeOfType();
+ websocketStates.Should().Contain(GraphQLWebsocketConnectionState.Disconnected);
+
+ Debug.WriteLine("restarting web host...");
+ await InitializeAsync();
+ Debug.WriteLine("web host started");
+ reconnectBlocker.Set();
+ callbackMonitor.Should().HaveBeenInvokedWithPayload(3.Seconds());
+ await observer.Should().PushAsync(3);
+ observer.RecordedMessages.Last().Data.MessageAdded.Content.Should().Be(InitialMessage.Content);
+
+ websocketStates.Should().ContainInOrder(
+ GraphQLWebsocketConnectionState.Disconnected,
+ GraphQLWebsocketConnectionState.Connecting,
+ GraphQLWebsocketConnectionState.Connected);
+
+ // disposing the client should complete the subscription
+ ChatClient.Dispose();
+ await observer.Should().CompleteAsync(5.Seconds());
+ }
+ }
+ }
+}
diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs
index d77ca14c..9fb7a5f8 100644
--- a/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs
+++ b/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs
@@ -4,7 +4,7 @@
namespace GraphQL.Integration.Tests.WebsocketTests
{
- public class Newtonsoft : Base, IClassFixture
+ public class Newtonsoft : BaseWithTimeout, IClassFixture
{
public Newtonsoft(ITestOutputHelper output, NewtonsoftIntegrationServerTestFixture fixture) : base(output, fixture)
{
diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/SystemTextJson.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/SystemTextJson.cs
index ab4659a4..1cf9a44d 100644
--- a/tests/GraphQL.Integration.Tests/WebsocketTests/SystemTextJson.cs
+++ b/tests/GraphQL.Integration.Tests/WebsocketTests/SystemTextJson.cs
@@ -4,7 +4,7 @@
namespace GraphQL.Integration.Tests.WebsocketTests
{
- public class SystemTextJson : Base, IClassFixture
+ public class SystemTextJson : BaseWithTimeout, IClassFixture
{
public SystemTextJson(ITestOutputHelper output, SystemTextJsonIntegrationServerTestFixture fixture) : base(output, fixture)
{
diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/TestServerNewtonsoft.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/TestServerNewtonsoft.cs
new file mode 100644
index 00000000..c694d559
--- /dev/null
+++ b/tests/GraphQL.Integration.Tests/WebsocketTests/TestServerNewtonsoft.cs
@@ -0,0 +1,14 @@
+using GraphQL.Integration.Tests.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace GraphQL.Integration.Tests.WebsocketTests
+{
+ public class TestServerNewtonsoft : Base, IClassFixture
+ {
+ public TestServerNewtonsoft(ITestOutputHelper output, TestServerTestNewtonsoftFixture fixture) : base(output, fixture)
+ {
+ fixture.Output = output;
+ }
+ }
+}
diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/TestServerSystemTextJson.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/TestServerSystemTextJson.cs
new file mode 100644
index 00000000..b6cd8395
--- /dev/null
+++ b/tests/GraphQL.Integration.Tests/WebsocketTests/TestServerSystemTextJson.cs
@@ -0,0 +1,14 @@
+using GraphQL.Integration.Tests.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace GraphQL.Integration.Tests.WebsocketTests
+{
+ public class TestServerSystemTextJson : Base, IClassFixture
+ {
+ public TestServerSystemTextJson(ITestOutputHelper output, TestServerTestSystemTextFixture fixture) : base(output, fixture)
+ {
+ fixture.Output = output;
+ }
+ }
+}