Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-05-19 10:33:21 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-19 10:33:21 +0300
commit36a59d088eca61b834191dacea009677a96c052f (patch)
treee4f33972dab5d8ef79e3944a9f403035fceea43f /spec/graphql/types/current_user_todos_type_spec.rb
parenta1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff)
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'spec/graphql/types/current_user_todos_type_spec.rb')
-rw-r--r--spec/graphql/types/current_user_todos_type_spec.rb207
1 files changed, 207 insertions, 0 deletions
diff --git a/spec/graphql/types/current_user_todos_type_spec.rb b/spec/graphql/types/current_user_todos_type_spec.rb
index a0015e96788..4ce97e1c006 100644
--- a/spec/graphql/types/current_user_todos_type_spec.rb
+++ b/spec/graphql/types/current_user_todos_type_spec.rb
@@ -3,7 +3,214 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['CurrentUserTodos'] do
+ include GraphqlHelpers
+
specify { expect(described_class.graphql_name).to eq('CurrentUserTodos') }
specify { expect(described_class).to have_graphql_fields(:current_user_todos).only }
+
+ # Request store is necessary to prevent duplicate max-member-access lookups
+ describe '.current_user_todos', :request_store, :aggregate_failures do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:issue_a) { create(:issue, project: project) }
+ let_it_be(:issue_b) { create(:issue, project: project) }
+
+ let_it_be(:todo_a) { create(:todo, :pending, user: user, project: project, target: issue_a) }
+ let_it_be(:todo_b) { create(:todo, :done, user: user, project: project, target: issue_a) }
+ let_it_be(:todo_c) { create(:todo, :pending, user: user, project: project, target: issue_b) }
+ let_it_be(:todo_d) { create(:todo, :done, user: user, project: project, target: issue_b) }
+
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+ let_it_be(:todo_e) { create(:todo, :pending, user: user, project: project, target: merge_request) }
+
+ let(:object_type) do
+ fresh_object_type('HasTodos').tap { _1.implements(Types::CurrentUserTodos) }
+ end
+
+ let(:id_enum) do
+ Class.new(Types::BaseEnum) do
+ graphql_name 'AorB'
+
+ value 'A'
+ value 'B'
+ end
+ end
+
+ let(:query_type) do
+ i_a = issue_a
+ i_b = issue_b
+ issue_id = id_enum
+ mr = merge_request
+
+ q = fresh_object_type('Query')
+
+ q.field :issue, null: false, type: object_type do
+ argument :id, type: issue_id, required: true
+ end
+
+ q.field :mr, null: false, type: object_type
+
+ q.define_method(:issue) do |id:|
+ case id
+ when 'A'
+ i_a
+ when 'B'
+ i_b
+ end
+ end
+
+ q.define_method(:mr) { mr }
+
+ q
+ end
+
+ let(:todo_fragment) do
+ <<-GQL
+ fragment todos on HasTodos {
+ todos: currentUserTodos {
+ nodes { id }
+ }
+ }
+ GQL
+ end
+
+ let(:base_query) do
+ <<-GQL
+ query {
+ issue(id: A) { ... todos }
+ }
+ #{todo_fragment}
+ GQL
+ end
+
+ let(:query_without_state_arguments) do
+ <<-GQL
+ query {
+ a: issue(id: A) {
+ ... todos
+ }
+ b: issue(id: B) {
+ ... todos
+ }
+ c: mr {
+ ... todos
+ }
+ d: mr {
+ ... todos
+ }
+ e: issue(id: A) {
+ ... todos
+ }
+ }
+
+ #{todo_fragment}
+ GQL
+ end
+
+ let(:with_state_arguments) do
+ <<-GQL
+ query {
+ a: issue(id: A) {
+ todos: currentUserTodos(state: pending) { nodes { id } }
+ }
+ b: issue(id: B) {
+ todos: currentUserTodos(state: done) { nodes { id } }
+ }
+ c: mr {
+ ... todos
+ }
+ }
+
+ #{todo_fragment}
+ GQL
+ end
+
+ before_all do
+ project.add_developer(user)
+ end
+
+ it 'batches todo lookups, linear in the number of target types/state arguments' do
+ # The baseline is 4 queries:
+ #
+ # When we batch queries, we see the following three groups of queries:
+ # # user authorization
+ # 1. SELECT "users".* FROM "users"
+ # INNER JOIN "project_authorizations"
+ # ON "users"."id" = "project_authorizations"."user_id"
+ # WHERE "project_authorizations"."project_id" = project_id
+ # AND "project_authorizations"."access_level" = 50
+ # 2. SELECT MAX("project_authorizations"."access_level") AS maximum_access_level,
+ # "project_authorizations"."user_id" AS project_authorizations_user_id
+ # FROM "project_authorizations"
+ # WHERE "project_authorizations"."project_id" = project_id
+ # AND "project_authorizations"."user_id" = user_id
+ # GROUP BY "project_authorizations"."user_id"
+ #
+ # # find todos for issues
+ # 1. SELECT "todos".* FROM "todos"
+ # WHERE "todos"."user_id" = user_id
+ # AND ("todos"."state" IN ('done','pending'))
+ # AND "todos"."target_id" IN (issue_a, issue_b)
+ # AND "todos"."target_type" = 'Issue' ORDER BY "todos"."id" DESC
+ #
+ # # find todos for merge_requests
+ # 1. SELECT "todos".* FROM "todos" WHERE "todos"."user_id" = user_id
+ # AND ("todos"."state" IN ('done','pending'))
+ # AND "todos"."target_id" = merge_request
+ # AND "todos"."target_type" = 'MergeRequest' ORDER BY "todos"."id" DESC
+ baseline = ActiveRecord::QueryRecorder.new do
+ execute_query(query_type, graphql: base_query)
+ end
+
+ expect do
+ execute_query(query_type, graphql: query_without_state_arguments)
+ end.not_to exceed_query_limit(baseline) # at present this is 3
+
+ expect do
+ execute_query(query_type, graphql: with_state_arguments)
+ end.not_to exceed_query_limit(baseline.count + 1)
+ end
+
+ it 'returns correct data' do
+ result = execute_query(query_type,
+ graphql: query_without_state_arguments,
+ raise_on_error: true).to_h
+
+ expect(result.dig('data', 'a', 'todos', 'nodes')).to contain_exactly(
+ a_graphql_entity_for(todo_a),
+ a_graphql_entity_for(todo_b)
+ )
+ expect(result.dig('data', 'b', 'todos', 'nodes')).to contain_exactly(
+ a_graphql_entity_for(todo_c),
+ a_graphql_entity_for(todo_d)
+ )
+ expect(result.dig('data', 'c', 'todos', 'nodes')).to contain_exactly(
+ a_graphql_entity_for(todo_e)
+ )
+ expect(result.dig('data', 'd', 'todos', 'nodes')).to contain_exactly(
+ a_graphql_entity_for(todo_e)
+ )
+ expect(result.dig('data', 'e', 'todos', 'nodes')).to contain_exactly(
+ a_graphql_entity_for(todo_a),
+ a_graphql_entity_for(todo_b)
+ )
+ end
+
+ it 'returns correct data, when state arguments are supplied' do
+ result = execute_query(query_type,
+ raise_on_error: true,
+ graphql: with_state_arguments).to_h
+
+ expect(result.dig('data', 'a', 'todos', 'nodes')).to contain_exactly(
+ a_graphql_entity_for(todo_a)
+ )
+ expect(result.dig('data', 'b', 'todos', 'nodes')).to contain_exactly(
+ a_graphql_entity_for(todo_d)
+ )
+ expect(result.dig('data', 'c', 'todos', 'nodes')).to contain_exactly(
+ a_graphql_entity_for(todo_e)
+ )
+ end
+ end
end