summaryrefslogtreecommitdiff
path: root/spec/graphql/resolvers/concerns/looks_ahead_spec.rb
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-06-18 11:18:50 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-06-18 11:18:50 +0000
commit8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 (patch)
treea77e7fe7a93de11213032ed4ab1f33a3db51b738 /spec/graphql/resolvers/concerns/looks_ahead_spec.rb
parent00b35af3db1abfe813a778f643dad221aad51fca (diff)
downloadgitlab-ce-8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781.tar.gz
Add latest changes from gitlab-org/gitlab@13-1-stable-ee
Diffstat (limited to 'spec/graphql/resolvers/concerns/looks_ahead_spec.rb')
-rw-r--r--spec/graphql/resolvers/concerns/looks_ahead_spec.rb177
1 files changed, 177 insertions, 0 deletions
diff --git a/spec/graphql/resolvers/concerns/looks_ahead_spec.rb b/spec/graphql/resolvers/concerns/looks_ahead_spec.rb
new file mode 100644
index 00000000000..8b83f887846
--- /dev/null
+++ b/spec/graphql/resolvers/concerns/looks_ahead_spec.rb
@@ -0,0 +1,177 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe LooksAhead do
+ include GraphqlHelpers
+
+ let_it_be(:the_user) { create(:user) }
+ let_it_be(:label_a) { create(:label) }
+ let_it_be(:label_b) { create(:label) }
+ let_it_be(:issue_a) { create(:issue, author: the_user, labels: [label_a, label_b]) }
+ let_it_be(:issue_b) { create(:issue, author: the_user, labels: [label_a]) }
+ let_it_be(:issue_c) { create(:issue, author: the_user, labels: [label_b]) }
+
+ # Simplified schema to test lookahead
+ let_it_be(:schema) do
+ issues_resolver = Class.new(Resolvers::BaseResolver) do
+ include LooksAhead
+
+ def resolve_with_lookahead(**args)
+ apply_lookahead(object.issues)
+ end
+
+ def preloads
+ { labels: [:labels] }
+ end
+ end
+
+ label = Class.new(GraphQL::Schema::Object) do
+ graphql_name 'Label'
+ field :id, Integer, null: false
+ end
+ issue = Class.new(GraphQL::Schema::Object) do
+ graphql_name 'Issue'
+ field :title, String, null: true
+ field :labels, label.connection_type, null: true
+ end
+ user = Class.new(GraphQL::Schema::Object) do
+ graphql_name 'User'
+ field :name, String, null: true
+ field :issues, issue.connection_type,
+ null: true
+ field :issues_with_lookahead, issue.connection_type,
+ extras: [:lookahead],
+ resolver: issues_resolver,
+ null: true
+ end
+
+ Class.new(GraphQL::Schema) do
+ query(Class.new(GraphQL::Schema::Object) do
+ graphql_name 'Query'
+ field :find_user, user, null: true do
+ argument :username, String, required: true
+ end
+
+ def find_user(username:)
+ context[:user_db].find { |u| u.username == username }
+ end
+ end)
+ end
+ end
+
+ def query(doc = document)
+ GraphQL::Query.new(schema,
+ document: doc,
+ context: { user_db: [the_user] },
+ variables: { username: the_user.username })
+ end
+
+ let(:document) do
+ GraphQL.parse <<-GRAPHQL
+ query($username: String!){
+ findUser(username: $username) {
+ name
+ issues {
+ nodes {
+ title
+ labels { nodes { id } }
+ }
+ }
+ issuesWithLookahead {
+ nodes {
+ title
+ labels { nodes { id } }
+ }
+ }
+ }
+ }
+ GRAPHQL
+ end
+
+ def run_query(gql_query)
+ query(GraphQL.parse(gql_query)).result
+ end
+
+ shared_examples 'a working query on the test schema' do
+ it 'has a good test setup', :aggregate_failures do
+ expected_label_ids = [label_a, label_b].cycle.take(4).map(&:id)
+ issue_titles = [issue_a, issue_b, issue_c].map(&:title)
+
+ res = query.result
+
+ expect(res['errors']).to be_blank
+ expect(res.dig('data', 'findUser', 'name')).to eq(the_user.name)
+ %w(issues issuesWithLookahead).each do |field|
+ expect(all_issue_titles(res, field)).to match_array(issue_titles)
+ expect(all_label_ids(res, field)).to match_array(expected_label_ids)
+ end
+ end
+ end
+
+ it_behaves_like 'a working query on the test schema'
+
+ it 'preloads labels on issues' do
+ expect(the_user.issues).to receive(:preload).with(:labels)
+
+ query.result
+ end
+
+ context 'the feature flag is off' do
+ before do
+ stub_feature_flags(described_class::FEATURE_FLAG => false)
+ end
+
+ it_behaves_like 'a working query on the test schema'
+
+ it 'does not preload labels on issues' do
+ expect(the_user.issues).not_to receive(:preload).with(:labels)
+
+ query.result
+ end
+ end
+
+ it 'issues fewer queries than the naive approach' do
+ the_user.reload # ensure no attributes are loaded before we begin
+ naive = <<-GQL
+ query($username: String!){
+ findUser(username: $username) {
+ name
+ issues {
+ nodes {
+ labels { nodes { id } }
+ }
+ }
+ }
+ }
+ GQL
+ with_lookahead = <<-GQL
+ query($username: String!){
+ findUser(username: $username) {
+ name
+ issuesWithLookahead {
+ nodes {
+ labels { nodes { id } }
+ }
+ }
+ }
+ }
+ GQL
+
+ expect { run_query(with_lookahead) }.to issue_fewer_queries_than { run_query(naive) }
+ end
+
+ private
+
+ def all_label_ids(result, field_name)
+ result.dig('data', 'findUser', field_name, 'nodes').flat_map do |node|
+ node.dig('labels', 'nodes').map { |n| n['id'] }
+ end
+ end
+
+ def all_issue_titles(result, field_name)
+ result.dig('data', 'findUser', field_name, 'nodes').map do |node|
+ node['title']
+ end
+ end
+end