Skip to content

Commit

Permalink
Validate behavior during execution
Browse files Browse the repository at this point in the history
  • Loading branch information
rmosolgo committed Sep 7, 2022
1 parent 8955413 commit f5aa4bc
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 6 deletions.
4 changes: 4 additions & 0 deletions lib/graphql/static_validation/literal_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ def required_input_fields_are_present(type, ast_node)
arg_type = @warden.get_argument(type, name).type
recursively_validate(GraphQL::Language::Nodes::NullValue.new(name: name), arg_type)
end

if type.one_of? && ast_node.arguments.size != 1
results << Query::InputValidationResult.from_problem("`#{type.graphql_name}` is a OneOf type, so only one argument may be given (instead of #{ast_node.arguments.size})")
end
merge_results(results)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def on_variable_definition(node, parent)
problems = validation_result.problems
first_problem = problems && problems.first
if first_problem
error_message = first_problem["message"]
error_message = first_problem["explanation"]
end

error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
Expand Down
86 changes: 84 additions & 2 deletions spec/graphql/schema/directive/one_of_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ def one_of_field(one_of_arg:)
graphql_name "OneOfInputObject"
directive GraphQL::Schema::Directive::OneOf

argument :int, GraphQL::Types::Int
argument :string, GraphQL::Types::String
argument :int, GraphQL::Types::Int, required: false
argument :string, GraphQL::Types::String, required: false
end
end

Expand Down Expand Up @@ -75,4 +75,86 @@ def one_of_field(one_of_arg:)
end
end
end

describe "execution" do
let(:query) do
<<~GRAPHQL
query TestQuery($oneOfInputObject: OneOfInputObject!) {
oneOfField(oneOfArg: $oneOfInputObject) {
string
int
}
}
GRAPHQL
end

it "accepts a default value with exactly one non-null key" do
query = <<~GRAPHQL
query TestQuery($oneOfInputObject: OneOfInputObject = { string: "default" }) {
oneOfField(oneOfArg: $oneOfInputObject) {
string
int
}
}
GRAPHQL

result = schema.execute(query)

assert_equal({ "string" => "default", "int" => nil }, result["data"]["oneOfField"])
end

it "rejects a default value with multiple non-null keys" do
query = <<~GRAPHQL
query TestQuery($oneOfInputObject: OneOfInputObject = { string: "default", int: 2 }) {
oneOfField(oneOfArg: $oneOfInputObject) {
string
int
}
}
GRAPHQL

result = schema.execute(query)

expected_errors = ["`OneOfInputObject` is a OneOf type, so only one argument may be given (instead of 2)"]
assert_equal(expected_errors, result["errors"].map { |e| e["message"] })
end

it "rejects a default value with multiple nullable keys" do
query = <<~GRAPHQL
query TestQuery($oneOfInputObject: OneOfInputObject = { string: "default", int: null }) {
oneOfField(oneOfArg: $oneOfInputObject) {
string
int
}
}
GRAPHQL

result = schema.execute(query)

expected_errors = ["`OneOfInputObject` is a OneOf type, so only one argument may be given (instead of 2)"]
assert_equal(expected_errors, result["errors"].map { |e| e["message"] })
end

it "accepts a variable with exactly one non-null key" do
string_result = schema.execute(query, variables: { oneOfInputObject: { string: "abc" } })
int_result = schema.execute(query, variables: { oneOfInputObject: { int: 2 } })

assert_equal({ "string" => "abc", "int" => nil }, string_result["data"]["oneOfField"])
assert_equal({ "string" => nil, "int" => 2 }, int_result["data"]["oneOfField"])
end

it "rejects a variable with exactly one null key" do
result = schema.execute(query, variables: { oneOfInputObject: { string: nil } })

expected_errors = ["'OneOfInputObject' requires exactly one argument, but 'string' was `null`."]
assert_equal(expected_errors, result["errors"].map { |e| e["extensions"]["problems"].map { |pr| pr["explanation"] } }.flatten)
end

it "rejects a variable with multiple non-null keys" do
result = schema.execute(query, variables: { oneOfInputObject: { string: "abc", int: 2 } })

expected_errors = ["'OneOfInputObject' requires exactly one argument, but 2 were provided."]
assert_equal(expected_errors, result["errors"].map { |e| e["extensions"]["problems"].map { |pr| pr["explanation"] } }.flatten)
end
end
end
2 changes: 1 addition & 1 deletion spec/graphql/schema/warden_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,7 @@ class Query < GraphQL::Schema::Object
query getPhonemes($manners: [Manner!] = [STOP, TRILL]){ phonemes(manners: $manners) { symbol } }
|
res = MaskHelpers.query_with_mask(query_string, mask)
expected_errors = ["Default value for $manners doesn't match type [Manner!]"]
expected_errors = ["Expected \"TRILL\" to be one of: STOP, AFFRICATE, FRICATIVE, APPROXIMANT, VOWEL"]
assert_equal expected_errors, error_messages(res)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@
it "finds default values that don't match their types" do
expected = [
{
"message"=>"Default value for $badInt doesn't match type Int",
"message"=>"Could not coerce value \"abc\" to Int",
"locations"=>[{"line"=>5, "column"=>7}],
"path"=>["query getCheese"],
"extensions"=>{"code"=>"defaultValueInvalidType", "variableName"=>"badInt", "typeName"=>"Int"}
},
{
"message"=>"Default value for $badInput doesn't match type DairyProductInput",
"message"=>"Could not coerce value true to Float",
"locations"=>[{"line"=>7, "column"=>7}],
"path"=>["query getCheese"],
"extensions"=>{"code"=>"defaultValueInvalidType", "variableName"=>"badInput", "typeName"=>"DairyProductInput"}
Expand Down

0 comments on commit f5aa4bc

Please sign in to comment.