gitlab-org--gitlab-foss/spec/graphql/resolvers/concerns/looks_ahead_spec.rb

160 lines
4.1 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
RSpec.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(GraphQL::Schema::Resolver) 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, 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
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