diff --git a/.github/workflows/branches-ubuntu.yml b/.github/workflows/branches-ubuntu.yml new file mode 100644 index 00000000..488f6596 --- /dev/null +++ b/.github/workflows/branches-ubuntu.yml @@ -0,0 +1,70 @@ +name: Branch workflow (Ubuntu) +on: + push: + branches-ignore: + - develop + - 'release/**' + - 'releases/**' +env: + DOTNET_CLI_TELEMETRY_OPTOUT: true +jobs: + generateVersionInfo: + name: GenerateVersionInfo + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Restore dotnet tools + run: dotnet tool restore + - name: Fetch complete repository + run: git fetch + - name: Generate version info from git history + run: dotnet gitversion /output json > gitversion.json + - name: Upload version info file + uses: actions/upload-artifact@v1 + with: + name: gitversion + path: gitversion.json + + build: + name: Build + needs: generateVersionInfo + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Download version info file + uses: actions/download-artifact@v1 + with: + name: gitversion + path: ./ + - name: Inject version info into environment + run: jq -r 'to_entries|map("::set-env name=GitVersion_\(.key)::\(.value|tostring)")|.[]' gitversion.json + - name: Build solution + run: echo "Current version is \"$GitVersion_SemVer\"" && dotnet build -c Release + - name: Create NuGet packages + run: dotnet pack -c Release --no-build -o nupkg + - name: Upload nuget packages + uses: actions/upload-artifact@v1 + with: + name: nupkg + path: nupkg + + test: + name: Test + needs: [build, generateVersionInfo] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Download version info file + uses: actions/download-artifact@v1 + with: + name: gitversion + path: ./ + - name: Inject version info into environment + run: jq -r 'to_entries|map("::set-env name=GitVersion_\(.key)::\(.value|tostring)")|.[]' gitversion.json + - name: Run tests + run: echo "Current version is \"$GitVersion_SemVer\"" && dotnet test -c Release diff --git a/.github/workflows/branches.yml b/.github/workflows/branches-windows.yml similarity index 97% rename from .github/workflows/branches.yml rename to .github/workflows/branches-windows.yml index a896d523..430b2121 100644 --- a/.github/workflows/branches.yml +++ b/.github/workflows/branches-windows.yml @@ -1,10 +1,8 @@ name: Branch workflow -on: +on: push: branches-ignore: - - develop - - 'release/**' - - 'releases/**' + - '**' jobs: generateVersionInfo: name: GenerateVersionInfo diff --git a/.github/workflows/main-ubuntu.yml b/.github/workflows/main-ubuntu.yml new file mode 100644 index 00000000..bb58dcff --- /dev/null +++ b/.github/workflows/main-ubuntu.yml @@ -0,0 +1,92 @@ +name: Main workflow (Ubuntu) +on: + push: + branches: + - develop + - 'release/**' + - 'releases/**' + tags: + - v* + - V* +env: + DOTNET_CLI_TELEMETRY_OPTOUT: true +jobs: + generateVersionInfo: + name: GenerateVersionInfo + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Restore dotnet tools + run: dotnet tool restore + - name: Fetch complete repository + run: git fetch + - name: Generate version info from git history + run: dotnet gitversion /output json > gitversion.json + - name: Upload version info file + uses: actions/upload-artifact@v1 + with: + name: gitversion + path: gitversion.json + + build: + name: Build + needs: generateVersionInfo + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Download version info file + uses: actions/download-artifact@v1 + with: + name: gitversion + path: ./ + - name: Inject version info into environment + run: jq -r 'to_entries|map("::set-env name=GitVersion_\(.key)::\(.value|tostring)")|.[]' gitversion.json + - name: Build solution + run: echo "Current version is \"$GitVersion_SemVer\"" && dotnet build -c Release + - name: Create NuGet packages + run: dotnet pack -c Release --no-build -o nupkg + - name: Upload nuget packages + uses: actions/upload-artifact@v1 + with: + name: nupkg + path: nupkg + + test: + name: Test + needs: [build, generateVersionInfo] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Download version info file + uses: actions/download-artifact@v1 + with: + name: gitversion + path: ./ + - name: Inject version info into environment + run: jq -r 'to_entries|map("::set-env name=GitVersion_\(.key)::\(.value|tostring)")|.[]' gitversion.json + - name: Run tests + run: echo "Current version is \"$GitVersion_SemVer\"" && dotnet test -c Release + + publish: + name: Publish + needs: [test] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Download nuget packages + uses: actions/download-artifact@v1 + with: + name: nupkg + - name: Publish the package to GPR + # using workaround with CURL because of non-functioning upload via dotnet nuget (https://stackoverflow.com/a/58943251) + run: | + for f in ./nupkg/*.nupkg + do + curl -vX PUT -u "graphql-dotnet:${{secrets.GITHUB_TOKEN}}" -F package=@$f https://nuget.pkg.github.com/graphql-dotnet/ + done diff --git a/.github/workflows/main.yml b/.github/workflows/main-windows.yml similarity index 96% rename from .github/workflows/main.yml rename to .github/workflows/main-windows.yml index 2ed05127..8ab30600 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main-windows.yml @@ -1,13 +1,8 @@ name: Main workflow -on: +on: push: - branches: - - develop - - 'release/**' - - 'releases/**' - tags: - - v* - - V* + branches-ignore: + - '**' jobs: generateVersionInfo: name: GenerateVersionInfo diff --git a/GitVersion.yml b/GitVersion.yml index 4b1b821f..2210324b 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -1,3 +1 @@ -branches: - release: - mode: ContinuousDeployment +mode: ContinuousDeployment diff --git a/GraphQL.Client.sln b/GraphQL.Client.sln index 510fa0b0..e1943dde 100644 --- a/GraphQL.Client.sln +++ b/GraphQL.Client.sln @@ -32,12 +32,10 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphQL.Server.Test", "tests\GraphQL.Server.Test\GraphQL.Server.Test.csproj", "{E95A1258-F666-4D4E-9101-E0C46F6A3CB3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{C42106CF-F685-4F29-BC18-A70616BD68A0}" - ProjectSection(SolutionItems) = preProject - .github\FUNDING.yml = .github\FUNDING.yml - EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{05CAF9B2-981E-40C0-AE31-5FA56E351F12}" ProjectSection(SolutionItems) = preProject + .github\workflows\branches-ubuntu.yml = .github\workflows\branches-ubuntu.yml .github\workflows\branches.yml = .github\workflows\branches.yml .github\workflows\main.yml = .github\workflows\main.yml EndProjectSection @@ -68,7 +66,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphQL.Client.Serializer.T EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphQL.Client.Tests.Common", "tests\GraphQL.Client.Tests.Common\GraphQL.Client.Tests.Common.csproj", "{0D307BAD-27AE-4A5D-8764-4AA2620B01E9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphQL.Client.Serializer.SystemTextJson", "src\GraphQL.Client.Serializer.SystemTextJson\GraphQL.Client.Serializer.SystemTextJson.csproj", "{7FFFEC00-D751-4FFC-9FD4-E91858F9A1C5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphQL.Client.Serializer.SystemTextJson", "src\GraphQL.Client.Serializer.SystemTextJson\GraphQL.Client.Serializer.SystemTextJson.csproj", "{7FFFEC00-D751-4FFC-9FD4-E91858F9A1C5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -138,6 +136,7 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {E95A1258-F666-4D4E-9101-E0C46F6A3CB3} = {0B0EDB0F-FF67-4B78-A8DB-B5C23E1FEE8C} + {05CAF9B2-981E-40C0-AE31-5FA56E351F12} = {C42106CF-F685-4F29-BC18-A70616BD68A0} {95D78D57-3232-491D-BAD6-F373D76EA34D} = {D61415CA-D822-43DD-9AE7-993B8B60E855} {87FC440E-6A4D-47D8-9EB2-416FC31CC4A6} = {47C98B55-08F1-4428-863E-2C5C876DEEFE} {C212983F-67DB-44EB-BFB0-5DA75A86DF55} = {0B0EDB0F-FF67-4B78-A8DB-B5C23E1FEE8C} diff --git a/src/GraphQL.Client/GraphQL.Client.csproj b/src/GraphQL.Client/GraphQL.Client.csproj index 31fa21ab..3b11e942 100644 --- a/src/GraphQL.Client/GraphQL.Client.csproj +++ b/src/GraphQL.Client/GraphQL.Client.csproj @@ -26,6 +26,7 @@ + @@ -38,4 +39,7 @@ + + + diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs b/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs index 1b8d8030..43bcbe18 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs @@ -7,7 +7,7 @@ using FluentAssertions.Primitives; namespace GraphQL.Client.Tests.Common.Helpers { - public class ObservableTester : IDisposable { + public class ObservableTester : IDisposable { private readonly IDisposable subscription; private readonly ManualResetEventSlim updateReceived = new ManualResetEventSlim(); private readonly ManualResetEventSlim completed = new ManualResetEventSlim(); @@ -25,7 +25,7 @@ public class ObservableTester : IDisposable { /// /// The last payload which was received. /// - public TPayload LastPayload { get; private set; } + public TSubscriptionPayload LastPayload { get; private set; } public Exception Error { get; private set; } @@ -33,7 +33,7 @@ public class ObservableTester : IDisposable { /// Creates a new which subscribes to the supplied /// /// the under test - public ObservableTester(IObservable observable) { + public ObservableTester(IObservable observable) { subscription = observable.ObserveOn(TaskPoolScheduler.Default).Subscribe( obj => { LastPayload = obj; @@ -59,8 +59,8 @@ public void Dispose() { subscription?.Dispose(); } - public SubscriptionAssertions Should() { - return new SubscriptionAssertions(this); + public SubscriptionAssertions Should() { + return new SubscriptionAssertions(this); } public class SubscriptionAssertions : ReferenceTypeAssertions, SubscriptionAssertions> { diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs index b17ba3c0..6112a00c 100644 --- a/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs +++ b/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs @@ -1,8 +1,8 @@ using System; +using System.Collections.Concurrent; using System.Diagnostics; using System.Net.WebSockets; using System.Threading; -using System.Threading.Tasks; using FluentAssertions; using GraphQL.Client.Abstractions; using GraphQL.Client.Abstractions.Websocket; @@ -247,49 +247,55 @@ public async void CanHandleConnectionTimeout() { return TimeSpan.Zero; }; - var statusMonitor = client.WebsocketConnectionState.Monitor(); - statusMonitor.Should().HaveReceivedPayload().Which.Should() - .Be(GraphQLWebsocketConnectionState.Disconnected); - - Debug.WriteLine("creating subscription stream"); - IObservable> observable = client.CreateSubscriptionStream(SubscriptionRequest, errorMonitor.Invoke); - - Debug.WriteLine("subscribing..."); - var tester = observable.Monitor(); - statusMonitor.Should().HaveReceivedPayload().Which.Should() - .Be(GraphQLWebsocketConnectionState.Connecting); - statusMonitor.Should().HaveReceivedPayload().Which.Should() - .Be(GraphQLWebsocketConnectionState.Connected); - callbackMonitor.Should().HaveBeenInvokedWithPayload(); - const string message1 = "Hello World"; - - var response = await client.AddMessageAsync(message1).ConfigureAwait(false); - response.Data.AddMessage.Content.Should().Be(message1); - tester.Should().HaveReceivedPayload() - .Which.Data.MessageAdded.Content.Should().Be(message1); - - Debug.WriteLine("stopping web host..."); - await server.StopAsync(CancellationToken.None).ConfigureAwait(false); - server.Dispose(); - Debug.WriteLine("web host stopped..."); - - errorMonitor.Should().HaveBeenInvokedWithPayload(TimeSpan.FromSeconds(10)) - .Which.Should().BeOfType(); - statusMonitor.Should().HaveReceivedPayload().Which.Should() - .Be(GraphQLWebsocketConnectionState.Disconnected); - - server = CreateServer(port); - reconnectBlocker.Set(); - statusMonitor.Should().HaveReceivedPayload(TimeSpan.FromSeconds(10)).Which.Should() - .Be(GraphQLWebsocketConnectionState.Connecting); - statusMonitor.Should().HaveReceivedPayload(TimeSpan.FromSeconds(10)).Which.Should() - .Be(GraphQLWebsocketConnectionState.Connected); - callbackMonitor.Should().HaveBeenInvokedWithPayload(); - - // disposing the client should complete the subscription - client.Dispose(); - tester.Should().HaveCompleted(TimeSpan.FromSeconds(5)); - server.Dispose(); + var websocketStates = new ConcurrentQueue(); + + using (client.WebsocketConnectionState.Subscribe(websocketStates.Enqueue)) { + websocketStates.Should().ContainSingle(state => state == GraphQLWebsocketConnectionState.Disconnected); + + Debug.WriteLine("creating subscription stream"); + IObservable> observable = + client.CreateSubscriptionStream(SubscriptionRequest, + errorMonitor.Invoke); + + Debug.WriteLine("subscribing..."); + var tester = observable.Monitor(); + 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(); + + const string message1 = "Hello World"; + var response = await client.AddMessageAsync(message1).ConfigureAwait(false); + response.Data.AddMessage.Content.Should().Be(message1); + tester.Should().HaveReceivedPayload() + .Which.Data.MessageAdded.Content.Should().Be(message1); + + Debug.WriteLine("stopping web host..."); + await server.StopAsync(CancellationToken.None).ConfigureAwait(false); + server.Dispose(); + Debug.WriteLine("web host stopped..."); + + errorMonitor.Should().HaveBeenInvokedWithPayload(TimeSpan.FromSeconds(10)) + .Which.Should().BeOfType(); + websocketStates.Should().Contain(GraphQLWebsocketConnectionState.Disconnected); + + server = CreateServer(port); + reconnectBlocker.Set(); + callbackMonitor.Should().HaveBeenInvokedWithPayload(); + websocketStates.Should().ContainInOrder( + GraphQLWebsocketConnectionState.Disconnected, + GraphQLWebsocketConnectionState.Connecting, + GraphQLWebsocketConnectionState.Connected); + + // disposing the client should complete the subscription + client.Dispose(); + tester.Should().HaveCompleted(TimeSpan.FromSeconds(5)); + server.Dispose(); + } }