Skip to content

Commit

Permalink
Added tests for input records list and fixed missing ExecuteInput o…
Browse files Browse the repository at this point in the history
…n input objects (#509)

* Added tests for input records list
* Fixed crash of input objects list creation from variables

---------

Co-authored-by: Andrii Chebukin <XperiAndri@Outlook.com>
  • Loading branch information
VectorTetra and xperiandri authored Jan 2, 2025
1 parent c184046 commit 859c99c
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/FSharp.Data.GraphQL.Server/Values.fs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,13 @@ let rec internal compileByType
| _ -> Ok null

| List (Input innerDef) ->
match innerDef with
| InputObject inputObjDef
| Nullable (InputObject inputObjDef) ->
let inner = compileByType inputObjectPath inputSource (inputDef, innerDef)
inputObjDef.ExecuteInput <- inner
| _ -> ()

let isArray = inputDef.Type.IsArray
// TODO: Improve creation of inner
let inner index = compileByType ((box index) :: inputObjectPath) inputSource (innerDef, innerDef)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
<Compile Include="Variables and Inputs\OptionalsNormalizationTests.fs" />
<Compile Include="Variables and Inputs\SkippablesNormalizationTests.fs" />
<Compile Include="Variables and Inputs\InputRecordTests.fs" />
<Compile Include="Variables and Inputs\InputRecordListTests.fs" />
<Compile Include="Variables and Inputs\InputObjectValidatorTests.fs" />
<Compile Include="Variables and Inputs\InputScalarAndAutoFieldScalarTests.fs" />
<Compile Include="Variables and Inputs\InputComplexTests.fs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// The MIT License (MIT)

module FSharp.Data.GraphQL.Tests.InputRecordListTests

#nowarn "25"

open Xunit
open System.Collections.Immutable
open System.Text.Json
open System.Text.Json.Serialization

open FSharp.Data.GraphQL
open FSharp.Data.GraphQL.Types
open FSharp.Data.GraphQL.Parser
open FSharp.Data.GraphQL.Tests.InputRecordTests

let schema verify =
let schema =
Schema (
query =
Define.Object (
"Query",
[ Define.Field (
"recordInputs",
StringType,
[ Define.Input ("records", ListOf InputRecordType)
Define.Input ("recordsOptional", Nullable (ListOf (Nullable InputRecordOptionalType)))
Define.Input ("recordsNested", ListOf (Nullable InputRecordNestedType)) ],
(fun ctx name ->
let _ = ctx.Arg<InputRecord list> "records"
let _ = ctx.TryArg<list<InputRecordOptional option>> "recordsOptional"
let recordNested =
ctx.Arg<list<InputRecordNested option>> "recordsNested"
|> List.tryHead
|> Option.flatten
match verify, recordNested with
| Nothing, _ -> ()
| AllInclude, Some recordNested -> recordNested.s |> ValueOption.iter _.VerifyAllInclude
| AllSkip, Some recordNested -> recordNested.s |> ValueOption.iter _.VerifyAllSkip
| SkipAndIncludeNull, Some recordNested -> recordNested.s |> ValueOption.iter _.VerifySkipAndIncludeNull
| _ -> ()
stringifyInput ctx name
)
) // TODO: add all args stringificaiton
Define.Field (
"objectInputs",
StringType,
[ Define.Input ("object", InputObjectType)
Define.Input ("objectOptional", Nullable InputObjectOptionalType) ],
stringifyInput
) ] // TODO: add all args stringificaiton
)
)

Executor schema

[<Fact>]
let ``Execute handles creation of inline empty input records list`` () =
let query =
"""{
recordInputs(
records: [],
recordsOptional: [],
recordsNested: []
)
}"""
let result = sync <| (schema AllInclude).AsyncExecute(parse query)
ensureDirect result <| fun data errors -> empty errors

[<Fact>]
let ``Execute handles creation of inline input records list with all fields`` () =
let query =
"""{
recordInputs(
records: [{ a: "a", b: "b", c: "c" }],
recordsOptional: [{ a: "a", b: "b", c: "c" }],
recordsNested: [{
a: { a: "a", b: "b", c: "c" },
b: { a: "a", b: "b", c: "c" },
c: { a: "a", b: "b", c: "c" },
s: { a: "a", b: "b", c: "c" },
l: [{ a: "a", b: "b", c: "c" }]
}]
)
}"""
let result = sync <| (schema AllInclude).AsyncExecute(parse query)
ensureDirect result <| fun data errors -> empty errors

[<Fact>]
let ``Execute handles creation of inline input records list with optional null fields`` () =
let query =
"""{
recordInputs(
records: [{ a: "a", b: "b", c: "c" }],
recordsOptional: [null],
recordsNested: [{ a: { a: "a", b: "b", c: "c" }, b: null, c: null, s: null, l: [] }]
)
}"""
let result = sync <| (schema Nothing).AsyncExecute(parse query)
ensureDirect result <| fun data errors -> empty errors

[<Fact>]
let ``Execute handles creation of inline input records list with mandatory only fields`` () =
let query =
"""{
recordInputs(
records: [{ a: "a", b: "b", c: "c" }],
recordsNested: [{ a: { a: "a", b: "b", c: "c" }, l: [{ a: "a", b: "b", c: "c" }] }]
)
}"""
let result = sync <| (schema Nothing).AsyncExecute(parse query)
ensureDirect result <| fun data errors -> empty errors

let variablesWithAllInputs (record, optRecord, skippable) =
$"""
{{
"records":[%s{record}],
"optRecords":[%s{optRecord}],
"nestedRecords":[ {{ "a": {record}, "b": {optRecord}, "c": {optRecord}, "s": {skippable}, "l": [{record}] }}]
}}
"""

let paramsWithValues variables =
JsonDocument
.Parse(variables : string)
.RootElement.Deserialize<ImmutableDictionary<string, JsonElement>> (serializerOptions)

[<Fact>]
let ``Execute handles creation of input records list from variables with all fields`` () =
let query =
"""query (
$records: [InputRecord!]!,
$optRecords: [InputRecordOptional],
$nestedRecords: [InputRecordNested]!
) {
recordInputs(
records: $records,
recordsOptional: $optRecords,
recordsNested: $nestedRecords
)
}"""
let testInputObject = """{"a":"a","b":"b","c":"c"}"""
let params' =
variablesWithAllInputs(testInputObject, testInputObject, testInputObject) |> paramsWithValues
let result = sync <| (schema AllInclude).AsyncExecute(parse query, variables = params')
//let expected = NameValueLookup.ofList [ "recordInputs", upcast testInputObject ]
ensureDirect result <| fun data errors ->
empty errors
//data |> equals (upcast expected)

[<Fact>]
let ``Execute handles creation of input records list from variables with optional null fields`` () =
let query =
"""query (
$records: [InputRecord!]!,
$optRecords: [InputRecordOptional],
$nestedRecords: [InputRecordNested]!
) {
recordInputs(
records: $records,
recordsOptional: $optRecords,
recordsNested: $nestedRecords
)
}"""
let testInputObject = """{"a":"a","b":"b","c":"c"}"""
let testInputSkippable = """{ "a": null, "b": null, "c": null }"""
let params' = variablesWithAllInputs(testInputObject, "null", testInputSkippable) |> paramsWithValues
let result = sync <| (schema SkipAndIncludeNull).AsyncExecute(parse query, variables = params')
ensureDirect result <| fun data errors -> empty errors

[<Fact>]
let ``Execute handles creation of input records from variables with mandatory only fields`` () =
let variablesWithAllInputs (record) =
$"""
{{
"record":%s{record},
"list":[%s{record}]
}}
"""

let query =
"""query ($record: InputRecord!, $list: [InputRecord!]!){
recordInputs(
records: [$record],
recordsNested: [{ a: $record, l: $list }]
)
}"""
let testInputObject = """{"a":"a","b":"b","c":"c"}"""
let params' = variablesWithAllInputs testInputObject |> paramsWithValues
let result = sync <| (schema AllSkip).AsyncExecute(parse query, variables = params')
ensureDirect result <| fun data errors -> empty errors

0 comments on commit 859c99c

Please sign in to comment.