From 62e85df83bb9f6328d6c826566e409efab58ddd7 Mon Sep 17 00:00:00 2001 From: taitus Date: Wed, 30 Oct 2024 14:03:16 +0100 Subject: [PATCH 1/4] Correctly check that the value of data is nil The way we access "data" always returned nil --- spec/controllers/graphql_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb index eeed8c578..a2765bd8a 100644 --- a/spec/controllers/graphql_controller_spec.rb +++ b/spec/controllers/graphql_controller_spec.rb @@ -3,7 +3,7 @@ require "rails_helper" # Useful resource: http://graphql.org/learn/serving-over-http/ def parser_error_raised?(response) - data_is_empty = response["data"].nil? + data_is_empty = response.parsed_body["data"].nil? error_is_present = (JSON.parse(response.body)["errors"].first["message"] =~ /^Parse error on/) data_is_empty && error_is_present end From e7f6e39679d8968cc5fea52f6690c22635d4acaf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:02:22 +0000 Subject: [PATCH 2/4] Bump graphql from 2.0.31 to 2.3.18 Note: The parser error message format changed in GraphQL 2.2.0 due to the introduction of a new optimized lexer and a hand-written parser (PR 4718). This commit updates the `parser_error_raised?` method in the GraphqlController tests to correctly detect errors using the new message format. The previous pattern was checking for "Parse error on", but with the new version, the error message now contains "Expected one of". This change ensures that the tests for malformed queries continue to pass as expected. Bumps [graphql](https://github.com/rmosolgo/graphql-ruby) from 2.0.31 to 2.3.18. - [Release notes](https://github.com/rmosolgo/graphql-ruby/releases) - [Changelog](https://github.com/rmosolgo/graphql-ruby/blob/master/CHANGELOG.md) - [Commits](https://github.com/rmosolgo/graphql-ruby/compare/v2.0.31...v2.3.18) --- updated-dependencies: - dependency-name: graphql dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Gemfile | 2 +- Gemfile.lock | 6 ++++-- spec/controllers/graphql_controller_spec.rb | 12 ++++-------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Gemfile b/Gemfile index f3e606976..6ff4bf739 100644 --- a/Gemfile +++ b/Gemfile @@ -27,7 +27,7 @@ gem "foundation_rails_helper", "~> 4.0.1" gem "globalize", "~> 6.3.0" gem "globalize-accessors", "~> 0.3.0" gem "graphiql-rails", "~> 1.8.0" -gem "graphql", "~> 2.0.0" +gem "graphql", "~> 2.3.18" gem "groupdate", "~> 6.5.1" gem "image_processing", "~> 1.13.0" gem "invisible_captcha", "~> 2.3.0" diff --git a/Gemfile.lock b/Gemfile.lock index 2d2a4b344..88fcfb614 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -228,6 +228,7 @@ GEM faraday-net_http (3.3.0) net-http ffi (1.17.0) + fiber-storage (1.0.0) file_validators (3.0.0) activemodel (>= 3.2) mime-types (>= 1.0) @@ -255,8 +256,9 @@ GEM graphiql-rails (1.8.0) railties sprockets-rails - graphql (2.0.31) + graphql (2.3.18) base64 + fiber-storage groupdate (6.5.1) activesupport (>= 7) gyoku (1.4.0) @@ -740,7 +742,7 @@ DEPENDENCIES globalize (~> 6.3.0) globalize-accessors (~> 0.3.0) graphiql-rails (~> 1.8.0) - graphql (~> 2.0.0) + graphql (~> 2.3.18) groupdate (~> 6.5.1) i18n-tasks (~> 0.9.37) image_processing (~> 1.13.0) diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb index a2765bd8a..c542593dc 100644 --- a/spec/controllers/graphql_controller_spec.rb +++ b/spec/controllers/graphql_controller_spec.rb @@ -2,12 +2,6 @@ require "rails_helper" # Useful resource: http://graphql.org/learn/serving-over-http/ -def parser_error_raised?(response) - data_is_empty = response.parsed_body["data"].nil? - error_is_present = (JSON.parse(response.body)["errors"].first["message"] =~ /^Parse error on/) - data_is_empty && error_is_present -end - describe GraphqlController, type: :request do let(:proposal) { create(:proposal) } @@ -23,7 +17,8 @@ describe GraphqlController, type: :request do get "/graphql", params: { query: "Malformed query string" } expect(response).to have_http_status(:ok) - expect(parser_error_raised?(response)).to be_truthy + expect(response.parsed_body["data"]).to be nil + expect(response.parsed_body["errors"]).to be_present end specify "without query string" do @@ -58,7 +53,8 @@ describe GraphqlController, type: :request do post "/graphql", params: { query: "Malformed query string" }.to_json, headers: json_headers expect(response).to have_http_status(:ok) - expect(parser_error_raised?(response)).to be_truthy + expect(response.parsed_body["data"]).to be nil + expect(response.parsed_body["errors"]).to be_present end it "without query string" do From 78d36dd563e9e56744327865b776bfa9d14610ae Mon Sep 17 00:00:00 2001 From: taitus Date: Thu, 17 Oct 2024 19:15:21 +0200 Subject: [PATCH 3/4] Start using run_graphql_field in specific field tests - Introduced `run_graphql_field` in tests that focus on resolving specific fields, leveraging the method added in GraphQL 2.2.0. - Continued using `execute` for broader cases where it is still necessary to test entire GraphQL queries. --- spec/graphql/types/query_type_spec.rb | 46 +++++++++++++--------- spec/graphql/types/user_type_spec.rb | 15 +++---- spec/spec_helper.rb | 1 + spec/support/common_actions/graphql_api.rb | 8 ---- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb index 6e050fd0c..9c50e04bc 100644 --- a/spec/graphql/types/query_type_spec.rb +++ b/spec/graphql/types/query_type_spec.rb @@ -5,18 +5,18 @@ describe Types::QueryType do let(:proposal) { create(:proposal, author: user) } it "returns fields of Int type" do - response = execute("{ proposal(id: #{proposal.id}) { cached_votes_up } }") - expect(dig(response, "data.proposal.cached_votes_up")).to eq(proposal.cached_votes_up) + response = run_graphql_field("Proposal.cached_votes_up", proposal) + expect(response).to eq(proposal.cached_votes_up) end it "returns fields of String type" do - response = execute("{ proposal(id: #{proposal.id}) { title } }") - expect(dig(response, "data.proposal.title")).to eq(proposal.title) + response = run_graphql_field("Proposal.title", proposal) + expect(response).to eq(proposal.title) end it "returns belongs_to associations" do - response = execute("{ proposal(id: #{proposal.id}) { public_author { username } } }") - expect(dig(response, "data.proposal.public_author.username")).to eq(proposal.public_author.username) + response = run_graphql_field("Proposal.public_author.username", proposal) + expect(response).to eq(proposal.public_author.username) end it "returns has_many associations" do @@ -32,36 +32,46 @@ describe Types::QueryType do end it "hides confidential fields of Int type" do - response = execute("{ user(id: #{user.id}) { failed_census_calls_count } }") - expect(hidden_field?(response, "failed_census_calls_count")).to be_truthy + expect do + run_graphql_field("User.failed_census_calls_count", user) + end.to raise_error GraphQL::Testing::Helpers::FieldNotDefinedError end it "hides confidential fields of String type" do - response = execute("{ user(id: #{user.id}) { encrypted_password } }") - expect(hidden_field?(response, "encrypted_password")).to be_truthy + expect do + run_graphql_field("User.encrypted_password", user) + end.to raise_error GraphQL::Testing::Helpers::FieldNotDefinedError end it "hides confidential has_one associations" do user.administrator = create(:administrator) - response = execute("{ user(id: #{user.id}) { administrator { id } } }") - expect(hidden_field?(response, "administrator")).to be_truthy + + expect do + run_graphql_field("User.administrator.id", user) + end.to raise_error GraphQL::Testing::Helpers::FieldNotDefinedError, /no field named `administrator`/ end it "hides confidential belongs_to associations" do create(:failed_census_call, user: user) - response = execute("{ user(id: #{user.id}) { failed_census_calls { id } } }") - expect(hidden_field?(response, "failed_census_calls")).to be_truthy + + expect do + run_graphql_field("User.failed_census_calls.id", user) + end.to raise_error GraphQL::Testing::Helpers::FieldNotDefinedError, /no field named `failed_census_calls`/ end it "hides confidential has_many associations" do create(:direct_message, sender: user) - response = execute("{ user(id: #{user.id}) { direct_messages_sent { id } } }") - expect(hidden_field?(response, "direct_messages_sent")).to be_truthy + + expect do + run_graphql_field("User.direct_messages_sent.id", user) + end.to raise_error GraphQL::Testing::Helpers::FieldNotDefinedError, + /no field named `direct_messages_sent`/ end it "hides confidential fields inside deeply nested queries" do - response = execute("{ proposals(first: 1) { edges { node { public_author { encrypted_password } } } } }") - expect(hidden_field?(response, "encrypted_password")).to be_truthy + expect do + run_graphql_field("Proposal.public_author.encrypted_password", proposal) + end.to raise_error GraphQL::Testing::Helpers::FieldNotDefinedError, /no field named `encrypted_password`/ end describe "#comments" do diff --git a/spec/graphql/types/user_type_spec.rb b/spec/graphql/types/user_type_spec.rb index e81baaf90..3b8f60716 100644 --- a/spec/graphql/types/user_type_spec.rb +++ b/spec/graphql/types/user_type_spec.rb @@ -7,28 +7,25 @@ describe Types::UserType do it "does not link debates" do create(:debate, author: user) - response = execute("{ user(id: #{user.id}) { public_debates { edges { node { title } } } } }") - received_debates = dig(response, "data.user.public_debates.edges") + response = run_graphql_field("User.public_debates", user) - expect(received_debates).to eq [] + expect(response.items).to eq [] end it "does not link proposals" do create(:proposal, author: user) - response = execute("{ user(id: #{user.id}) { public_proposals { edges { node { title } } } } }") - received_proposals = dig(response, "data.user.public_proposals.edges") + response = run_graphql_field("User.public_proposals", user) - expect(received_proposals).to eq [] + expect(response.items).to eq [] end it "does not link comments" do create(:comment, author: user) - response = execute("{ user(id: #{user.id}) { public_comments { edges { node { body } } } } }") - received_comments = dig(response, "data.user.public_comments.edges") + response = run_graphql_field("User.public_comments", user) - expect(received_comments).to eq [] + expect(response.items).to eq [] end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2ee4b259b..c7d2e2079 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -21,6 +21,7 @@ RSpec.configure do |config| config.include(EmailSpec::Matchers) config.include(CommonActions) config.include(ActiveSupport::Testing::TimeHelpers) + config.include GraphQL::Testing::Helpers.for(ConsulSchema) config.define_derived_metadata(file_path: Regexp.new("/spec/components/")) do |metadata| metadata[:type] = :component diff --git a/spec/support/common_actions/graphql_api.rb b/spec/support/common_actions/graphql_api.rb index 6deceb138..add8cc773 100644 --- a/spec/support/common_actions/graphql_api.rb +++ b/spec/support/common_actions/graphql_api.rb @@ -7,14 +7,6 @@ module GraphQLAPI response.dig(*path.split(".")) end - def hidden_field?(response, field_name) - data_is_empty = response["data"].nil? - error_message = /Field '#{field_name}' doesn't exist on type '[[:alnum:]]*'/ - - error_is_present = ((response["errors"].first["message"] =~ error_message) == 0) - data_is_empty && error_is_present - end - def extract_fields(response, collection_name, field_chain) fields = field_chain.split(".") dig(response, "data.#{collection_name}.edges").map do |node| From 3227b7e18464b476281d5990983e88724ca92881 Mon Sep 17 00:00:00 2001 From: taitus Date: Wed, 6 Nov 2024 13:29:28 +0100 Subject: [PATCH 4/4] Ensure that we are testing a belongs_to association --- spec/graphql/types/query_type_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb index 9c50e04bc..37888ef32 100644 --- a/spec/graphql/types/query_type_spec.rb +++ b/spec/graphql/types/query_type_spec.rb @@ -52,11 +52,11 @@ describe Types::QueryType do end it "hides confidential belongs_to associations" do - create(:failed_census_call, user: user) + user.geozone = create(:geozone) expect do - run_graphql_field("User.failed_census_calls.id", user) - end.to raise_error GraphQL::Testing::Helpers::FieldNotDefinedError, /no field named `failed_census_calls`/ + run_graphql_field("User.geozone.id", user) + end.to raise_error GraphQL::Testing::Helpers::FieldNotDefinedError, /no field named `geozone`/ end it "hides confidential has_many associations" do