Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Named the persisted query constants and the media type in the tests
The tests take the error message, its code, the cache directive and the
query string parameter from `PersistedQueries` and the media type from
`MediaTypeNames` instead of spelling them out again, and assert with
`Assert.Equal` and `Assert.Single` instead of `Assert.True` on a comparison.
The error extensions are built with `kvpObj` and without an upcast.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
  • Loading branch information
xperiandri and claude committed Oct 10, 2026
commit e5c058e3b02bef59a58561230c4564d58e343dd5
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ module internal PersistedQueries =
/// The error Apollo Server answers with. Shared by every request, which is safe because both the record and its
/// immutable extensions dictionary cannot be changed.
let notSupportedError =
let extensions = ImmutableDictionary.CreateRange (StringComparer.Ordinal, [ KeyValuePair ("code", box NotSupportedCode) ])
GQLProblemDetails.Create (NotSupportedMessage, extensions :> IReadOnlyDictionary<string, obj>)
let extensions = ImmutableDictionary.CreateRange (StringComparer.Ordinal, [ kvpObj "code" NotSupportedCode ])
GQLProblemDetails.Create (NotSupportedMessage, extensions)

/// <summary>
/// Whether the <c>extensions</c> of a request ask for an Apollo automatic persisted query: whether their
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module FSharp.Data.GraphQL.Tests.AspNetCore.RequestHandlerExtensibilityTests
open System
open System.IO
open System.Net.Http
open System.Net.Mime
open System.Text
open System.Text.Json
open System.Threading
Expand Down Expand Up @@ -75,7 +76,7 @@ let private createHandlerFor<'Handler when 'Handler :> GraphQLRequestHandler<Roo
struct (handler, (handler :?> 'Handler), (ctx :> HttpContext), (scope :> IDisposable))

let private setJsonBody (json : string) (request : HttpRequest) =
request.ContentType <- "application/json"
request.ContentType <- MediaTypeNames.Application.Json
let bytes = Encoding.UTF8.GetBytes json
request.Body <- new MemoryStream (bytes)
request.ContentLength <- int64 bytes.Length
Expand Down Expand Up @@ -211,22 +212,20 @@ let private assertPersistedQueryNotSupported (recorder : RecordingHandler) (ctx

let! struct (statusCode, responseBody) = executeOutcome ctx outcome

Assert.True (
(statusCode = StatusCodes.Status200OK),
$"Expected HTTP 200, as Apollo Server answers an unsupported persisted query, but got HTTP %d{statusCode} with body:\n%s{responseBody}"
)
// HTTP 200, as Apollo Server answers an unsupported persisted query
Assert.Equal (StatusCodes.Status200OK, statusCode)
use document = JsonDocument.Parse responseBody
let root = document.RootElement
// Apollo Client 4 falls back to the full query only for a result without other top-level members than data, errors and
// extensions, so the answer has the errors alone, as Apollo Server's
let members = root.EnumerateObject () |> Seq.map _.Name |> List.ofSeq
Assert.True ((members = [ "errors" ]), $"Expected the response to have the member 'errors' alone, but got:\n%s{responseBody}")
Assert.Equal<string list> ([ "errors" ], members)
let errors = root |> wantMember "errors" responseBody
let error = Assert.Single (errors.EnumerateArray ())
Assert.Equal ("PersistedQueryNotSupported", (error |> wantMember "message" responseBody).GetString ())
Assert.Equal (PersistedQueries.NotSupportedMessage, (error |> wantMember "message" responseBody).GetString ())
let code = error |> wantMember "extensions" responseBody |> wantMember "code" responseBody
Assert.Equal ("PERSISTED_QUERY_NOT_SUPPORTED", code.GetString ())
Assert.Equal ("private, no-cache, must-revalidate", ctx.Response.Headers.CacheControl.ToString ())
Assert.Equal (PersistedQueries.NotSupportedCode, code.GetString ())
Assert.Equal (PersistedQueries.notSupportedCacheControl.ToString (), ctx.Response.Headers.CacheControl.ToString ())
}

[<Fact>]
Expand All @@ -252,7 +251,7 @@ let ``GET with only a persisted query hash is answered with PersistedQueryNotSup
|> setQueryString [
"operationName", persistedQueryName
"variables", "{}"
"extensions", persistedQueryExtensions
PersistedQueries.ExtensionsParameterName, persistedQueryExtensions
])
use _ = scope

Expand Down Expand Up @@ -301,11 +300,8 @@ let ``Multipart POST operation is executed as before`` () : Task = task {

let! outcome = handler.HandleAsync ()

Assert.True (
(recorder.OperationCalls.Count = 1),
$"Expected the operation of a multipart request to be executed once, but ExecuteOperation was called %d{recorder.OperationCalls.Count} time(s); outcome: %A{outcome}"
)
Assert.Equal (query, recorder.OperationCalls[0].Query)
let call = Assert.Single recorder.OperationCalls
Assert.Equal (query, call.Query)
Assert.Equal (0, recorder.IntrospectionCalls.Count)
assertOkResponse outcome
}
Expand All @@ -319,7 +315,7 @@ let ``GET with both a query and a persisted query hash is answered with Persiste
|> setQueryString [
"query", persistedQueryText
"operationName", persistedQueryName
"extensions", persistedQueryExtensions
PersistedQueries.ExtensionsParameterName, persistedQueryExtensions
])
use _ = scope

Expand Down Expand Up @@ -349,11 +345,8 @@ let ``POST operation whose extensions do not ask for a persisted query is execut

let! outcome = handler.HandleAsync ()

Assert.True (
(recorder.OperationCalls.Count = 1),
$"Expected the operation to be executed once despite extensions %s{extensions}, but ExecuteOperation was called %d{recorder.OperationCalls.Count} time(s)"
)
Assert.Equal (query, recorder.OperationCalls[0].Query)
let call = Assert.Single recorder.OperationCalls
Assert.Equal (query, call.Query)
Assert.Equal (0, recorder.IntrospectionCalls.Count)
assertOkResponse outcome
}
Expand Down Expand Up @@ -396,16 +389,13 @@ let ``GET whose extensions do not ask for a persisted query is still answered wi
let struct (handler, recorder, _, scope) =
createHandlerFor<RecordingHandler> (fun request ->
request.Method <- HttpMethods.Get
request |> setQueryString [ "extensions", extensions ])
request |> setQueryString [ PersistedQueries.ExtensionsParameterName, extensions ])
use _ = scope

let! outcome = handler.HandleAsync ()

Assert.True (
(recorder.IntrospectionCalls.Count = 1),
$"Expected the introspection query to be executed once, but ExecuteIntrospectionQuery was called %d{recorder.IntrospectionCalls.Count} time(s)"
)
Assert.Equal (ValueNone, recorder.IntrospectionCalls[0])
let call = Assert.Single recorder.IntrospectionCalls
Assert.Equal (ValueNone, call)
Assert.Equal (0, recorder.OperationCalls.Count)
assertOkResponse outcome
}
Loading