diff --git a/build/Program.fs b/build/Program.fs index 526b0adaf..05a0de5d0 100644 --- a/build/Program.fs +++ b/build/Program.fs @@ -70,9 +70,8 @@ Target.create RestoreTarget <| fun _ -> |> Seq.iter (fun pattern -> DotNet.restore DotNetCli.setRestoreOptions pattern) -let [] BuildTarget = "Build" -Target.create BuildTarget <| fun _ -> - "FSharp.Data.GraphQL.slnx" +let buildSolution (solution : string) = + solution |> DotNet.build (fun options -> { options with Common = options.Common.WithRedirectOutput true |> DotNetCli.setVersion @@ -88,6 +87,14 @@ Target.create BuildTarget <| fun _ -> } }) +let [] BuildTarget = "Build" +Target.create BuildTarget <| fun _ -> buildSolution "FSharp.Data.GraphQL.slnx" + +// The integration tests are not part of FSharp.Data.GraphQL.slnx and are neither restored nor built by BuildTarget. +// They reference the client provider assembly produced by BuildTarget, so this must run after it. +let [] BuildIntegrationTestsTarget = "BuildIntegrationTests" +Target.create BuildIntegrationTestsTarget <| fun _ -> buildSolution "FSharp.Data.GraphQL.Integration.slnx" + let startGraphQLServer (project : string) port (streamRef : DataRef) = CreateProcess.fromRawCommandLine "dotnet" $"run --project {project} --no-build --no-launch-profile --configuration {configurationString} --urls=http://localhost:%i{port}/" |> CreateProcess.withStandardInput (CreatePipe streamRef) @@ -98,9 +105,14 @@ let startGraphQLServer (project : string) port (streamRef : DataRef) = System.Threading.Thread.Sleep (2000) -let runTests (project : string) = - let projectName = Path.GetFileNameWithoutExtension project - let resultsFileName = $"{projectName}.trx" +let [] TestResultsDirectory = "test-results" + +let runTests (project : string) (resultsFileName : string) (filter : string voption) = + let resultsFilePath = TestResultsDirectory resultsFileName + // A stale results file from a previous run must not hide a run that produced none + // (checked first, because File.Delete throws when the results directory does not exist yet) + if File.Exists resultsFilePath then + File.Delete resultsFilePath DotNet.test (fun options -> @@ -108,9 +120,13 @@ let runTests (project : string) = options with NoBuild = true Logger = Some $"trx;LogFileName={resultsFileName}" - ResultsDirectory = Some "test-results" + ResultsDirectory = Some TestResultsDirectory Framework = Some DotNetMoniker Configuration = configuration + Common = { + options.Common with + CustomParams = filter |> ValueOption.map (fun filter -> $"--filter {filter}") |> ValueOption.toOption + } MSBuildParams = { options.MSBuildParams with DisableInternalBinLog = true @@ -126,6 +142,11 @@ let runTests (project : string) = |> _.WithCommon(DotNetCli.setVersion)) project + // `dotnet test --no-build` on a project that was never restored does not import the test SDK, + // so it runs nothing and still exits with 0. The missing results file is the only trace of that. + if not (File.Exists resultsFilePath) then + failwith $"'dotnet test {project}' produced no test results at '{resultsFilePath}'. Was the project restored and built?" + let integrationTestServerProjectPath = "tests" "FSharp.Data.GraphQL.IntegrationTests.Server" @@ -169,36 +190,18 @@ let integrationTestsProjectPath = let [] UpdateIntrospectionFileTarget = "UpdateIntrospectionFile" Target.create UpdateIntrospectionFileTarget <| fun _ -> - let projectName = Path.GetFileNameWithoutExtension integrationTestsProjectPath - let resultsFileName = $"{projectName}.trx" + runTests + integrationTestsProjectPath + "FSharp.Data.GraphQL.IntegrationTests.IntrospectionUpdate.trx" + (ValueSome "FullyQualifiedName~IntrospectionUpdateTests") - DotNet.test - (fun options -> - { - options with - NoBuild = true - Logger = Some $"trx;LogFileName={resultsFileName}" - ResultsDirectory = Some "test-results" - Framework = Some DotNetMoniker - Configuration = configuration - Common = { - options.Common with - CustomParams = Some "--filter FullyQualifiedName~IntrospectionUpdateTests" - } - MSBuildParams = { - options.MSBuildParams with - DisableInternalBinLog = true - Verbosity = Some Normal - Properties = [ - if embedAll then - ("DebugType", "embedded") - ("EmbedAllSources", "true") - ] - } - } - |> _.WithRedirectOutput(true) - |> _.WithCommon(DotNetCli.setVersion)) +// Runs the rest of the integration tests; the introspection update tests already ran in UpdateIntrospectionFileTarget +let [] RunIntegrationTestsTarget = "RunIntegrationTests" +Target.create RunIntegrationTestsTarget <| fun _ -> + runTests integrationTestsProjectPath + "FSharp.Data.GraphQL.IntegrationTests.trx" + (ValueSome "FullyQualifiedName!~IntrospectionUpdateTests") let unitTestsProjectPath = "tests" @@ -207,7 +210,7 @@ let unitTestsProjectPath = let [] RunUnitTestsTarget = "RunUnitTests" Target.create RunUnitTestsTarget <| fun _ -> - runTests unitTestsProjectPath + runTests unitTestsProjectPath "FSharp.Data.GraphQL.Tests.trx" ValueNone let prepareDocGen () = Shell.rm "docs/release-notes.md" @@ -384,7 +387,9 @@ Target.create "PackAndPush" ignore ==> RestoreTarget ==> BuildTarget ==> RunUnitTestsTarget +==> BuildIntegrationTestsTarget ==> UpdateIntrospectionFileTarget +==> RunIntegrationTestsTarget ==> "All" =?> (GenerateDocsTarget, Environment.environVar "GITHUB_ACTIONS" = "True") |> ignore diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/IntrospectionUpdateTests.fs b/tests/FSharp.Data.GraphQL.IntegrationTests/IntrospectionUpdateTests.fs index ce0e7eae2..c3931b295 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/IntrospectionUpdateTests.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/IntrospectionUpdateTests.fs @@ -20,9 +20,9 @@ let normalizeJsonDocument options (document : JsonDocument) = JsonDocument.Parse buffer let parseAndNormalizeJsonAsync ct options stream = task { - let! document = JsonDocument.ParseAsync (stream, cancellationToken = ct) - return normalizeJsonDocument options document - } + let! document = JsonDocument.ParseAsync (stream, cancellationToken = ct) + return normalizeJsonDocument options document +} let areSchemasEqual (document1 : JsonDocument) (document2 : JsonDocument) = let schema1 = document1.RootElement.GetProperty("data").GetProperty ("__schema") @@ -30,56 +30,56 @@ let areSchemasEqual (document1 : JsonDocument) (document2 : JsonDocument) = schema1.GetRawText () = schema2.GetRawText () let readDestinationDocumentAsync ct (stream : FileStream) = task { - try - let! document = JsonDocument.ParseAsync (stream, cancellationToken = ct) - return ValueSome document - with :? JsonException -> - return ValueNone - } + try + let! document = JsonDocument.ParseAsync (stream, cancellationToken = ct) + return ValueSome document + with :? JsonException -> + return ValueNone +} let updateIntrospectionFileAsync ct sourceStream = task { - use destinationStream = - new FileStream (introspectionFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read) + use destinationStream = + new FileStream (introspectionFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read) let options = JsonWriterOptions (Indented = true) - let! sourceDocument = parseAndNormalizeJsonAsync ct options sourceStream - destinationStream.Seek (0L, SeekOrigin.Begin) |> ignore - let! destinationDocument = readDestinationDocumentAsync ct destinationStream + let! sourceDocument = parseAndNormalizeJsonAsync ct options sourceStream + destinationStream.Seek (0L, SeekOrigin.Begin) |> ignore + let! destinationDocument = readDestinationDocumentAsync ct destinationStream - let shouldUpdate = - match destinationDocument with - | ValueNone -> true - | ValueSome document -> not (areSchemasEqual document sourceDocument) + let shouldUpdate = + match destinationDocument with + | ValueNone -> true + | ValueSome document -> not (areSchemasEqual document sourceDocument) - if shouldUpdate then - destinationStream.Seek (0L, SeekOrigin.Begin) |> ignore - destinationStream.SetLength 0 - use writer = new Utf8JsonWriter (destinationStream, options) - sourceDocument.WriteTo writer - writer.Flush () + if shouldUpdate then + destinationStream.Seek (0L, SeekOrigin.Begin) |> ignore + destinationStream.SetLength 0 + use writer = new Utf8JsonWriter (destinationStream, options) + sourceDocument.WriteTo writer + writer.Flush () - return shouldUpdate - } + return shouldUpdate +} [] let ``Get GraphQL introspection response returns schema`` () = task { - use httpClient = TestHosts.createIntegrationHttpClient () + use httpClient = TestHosts.createIntegrationHttpClient () let! response = httpClient.GetFromJsonAsync ("/", CancellationToken.None) let schema = response.GetProperty("data").GetProperty ("__schema") Assert.NotEqual (Unchecked.defaultof, schema) - let hasErrors, _ = response.TryGetProperty "errors" - Assert.False hasErrors - } + let hasErrors, _ = response.TryGetProperty "errors" + Assert.False hasErrors +} [] let ``Update integration introspection file when schema changes`` () = task { - use httpClient = TestHosts.createIntegrationHttpClient () + use httpClient = TestHosts.createIntegrationHttpClient () let! sourceStream = httpClient.GetStreamAsync ("/") - let! wasUpdated = updateIntrospectionFileAsync CancellationToken.None sourceStream + let! wasUpdated = updateIntrospectionFileAsync CancellationToken.None sourceStream Assert.True (File.Exists introspectionFilePath) - if wasUpdated then + if wasUpdated then let! sourceStreamSecondRun = httpClient.GetStreamAsync ("/") - use sourceStreamForVerification = sourceStreamSecondRun - let! wasUpdatedSecondRun = updateIntrospectionFileAsync CancellationToken.None sourceStreamForVerification - Assert.False wasUpdatedSecondRun - } + use sourceStreamForVerification = sourceStreamSecondRun + let! wasUpdatedSecondRun = updateIntrospectionFileAsync CancellationToken.None sourceStreamForVerification + Assert.False wasUpdatedSecondRun +} diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiRemoteProviderTests.fs b/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiRemoteProviderTests.fs index bc92cb8a7..f85d2775f 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiRemoteProviderTests.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiRemoteProviderTests.fs @@ -5,6 +5,9 @@ open Helpers open FSharp.Data.GraphQL open System.Threading.Tasks +// SwapiLocalProviderTests instantiates the same provider with the same static arguments. +// The F# compiler in SDK 10.0.3xx fails with FS0193 ("type X is not compatible with type X") when both files +// declare inline operations with the same name, so the operations here are named differently. type Provider = GraphQLProvider<"introspection.json"> let connection = TestHosts.createStarWarsConnection () @@ -15,7 +18,7 @@ type Episode = Provider.Types.Episode module SimpleOperation = let operation = - Provider.Operation<"""query Q { + Provider.Operation<"""query RemoteQ { hero (id: "1000") { name appearsIn @@ -43,7 +46,7 @@ hero (id: "1000") { } }"""> () - type Operation = Provider.Operations.Q + type Operation = Provider.Operations.RemoteQ let validateResult (result : Operation.OperationResult) = result.CustomData.ContainsKey ("documentId") |> equals true @@ -162,7 +165,7 @@ let ``Should be able to use pattern matching methods on an union type`` () = module MutationOperation = let operation = - Provider.Operation<"""mutation M { + Provider.Operation<"""mutation RemoteM { setMoon (id: "1", isMoon: true) { id name @@ -170,7 +173,7 @@ module MutationOperation = } }"""> () - type Operation = Provider.Operations.M + type Operation = Provider.Operations.RemoteM let validateResult (result : Operation.OperationResult) = result.CustomData.ContainsKey ("documentId") |> equals true