diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-17 19:05:49 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-17 19:05:49 +0300 |
commit | 43a25d93ebdabea52f99b05e15b06250cd8f07d7 (patch) | |
tree | dceebdc68925362117480a5d672bcff122fb625b /spec/graphql | |
parent | 20c84b99005abd1c82101dfeff264ac50d2df211 (diff) |
Add latest changes from gitlab-org/gitlab@16-0-stable-eev16.0.0-rc42
Diffstat (limited to 'spec/graphql')
99 files changed, 1325 insertions, 375 deletions
diff --git a/spec/graphql/graphql_triggers_spec.rb b/spec/graphql/graphql_triggers_spec.rb index 00b5aec366e..a8a37289ddd 100644 --- a/spec/graphql/graphql_triggers_spec.rb +++ b/spec/graphql/graphql_triggers_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe GraphqlTriggers do +RSpec.describe GraphqlTriggers, feature_category: :shared do let_it_be(:issuable, refind: true) { create(:work_item) } describe '.issuable_assignees_updated' do @@ -12,9 +12,9 @@ RSpec.describe GraphqlTriggers do issuable.update!(assignees: assignees) end - it 'triggers the issuableAssigneesUpdated subscription' do + it 'triggers the issuable_assignees_updated subscription' do expect(GitlabSchema.subscriptions).to receive(:trigger).with( - 'issuableAssigneesUpdated', + :issuable_assignees_updated, { issuable_id: issuable.to_gid }, issuable ) @@ -24,9 +24,9 @@ RSpec.describe GraphqlTriggers do end describe '.issuable_title_updated' do - it 'triggers the issuableTitleUpdated subscription' do + it 'triggers the issuable_title_updated subscription' do expect(GitlabSchema.subscriptions).to receive(:trigger).with( - 'issuableTitleUpdated', + :issuable_title_updated, { issuable_id: issuable.to_gid }, issuable ).and_call_original @@ -36,9 +36,9 @@ RSpec.describe GraphqlTriggers do end describe '.issuable_description_updated' do - it 'triggers the issuableDescriptionUpdated subscription' do + it 'triggers the issuable_description_updated subscription' do expect(GitlabSchema.subscriptions).to receive(:trigger).with( - 'issuableDescriptionUpdated', + :issuable_description_updated, { issuable_id: issuable.to_gid }, issuable ).and_call_original @@ -54,9 +54,9 @@ RSpec.describe GraphqlTriggers do issuable.update!(labels: labels) end - it 'triggers the issuableLabelsUpdated subscription' do + it 'triggers the issuable_labels_updated subscription' do expect(GitlabSchema.subscriptions).to receive(:trigger).with( - 'issuableLabelsUpdated', + :issuable_labels_updated, { issuable_id: issuable.to_gid }, issuable ) @@ -66,9 +66,9 @@ RSpec.describe GraphqlTriggers do end describe '.issuable_dates_updated' do - it 'triggers the issuableDatesUpdated subscription' do + it 'triggers the issuable_dates_updated subscription' do expect(GitlabSchema.subscriptions).to receive(:trigger).with( - 'issuableDatesUpdated', + :issuable_dates_updated, { issuable_id: issuable.to_gid }, issuable ).and_call_original @@ -78,9 +78,9 @@ RSpec.describe GraphqlTriggers do end describe '.issuable_milestone_updated' do - it 'triggers the issuableMilestoneUpdated subscription' do + it 'triggers the issuable_milestone_updated subscription' do expect(GitlabSchema.subscriptions).to receive(:trigger).with( - 'issuableMilestoneUpdated', + :issuable_milestone_updated, { issuable_id: issuable.to_gid }, issuable ).and_call_original @@ -90,11 +90,11 @@ RSpec.describe GraphqlTriggers do end describe '.merge_request_reviewers_updated' do - it 'triggers the mergeRequestReviewersUpdated subscription' do + it 'triggers the merge_request_reviewers_updated subscription' do merge_request = build_stubbed(:merge_request) expect(GitlabSchema.subscriptions).to receive(:trigger).with( - 'mergeRequestReviewersUpdated', + :merge_request_reviewers_updated, { issuable_id: merge_request.to_gid }, merge_request ).and_call_original @@ -104,25 +104,39 @@ RSpec.describe GraphqlTriggers do end describe '.merge_request_merge_status_updated' do - it 'triggers the mergeRequestMergeStatusUpdated subscription' do + it 'triggers the merge_request_merge_status_updated subscription' do merge_request = build_stubbed(:merge_request) expect(GitlabSchema.subscriptions).to receive(:trigger).with( - 'mergeRequestMergeStatusUpdated', + :merge_request_merge_status_updated, { issuable_id: merge_request.to_gid }, merge_request ).and_call_original GraphqlTriggers.merge_request_merge_status_updated(merge_request) end + + context 'when realtime_mr_status_change feature flag is disabled' do + before do + stub_feature_flags(realtime_mr_status_change: false) + end + + it 'does not trigger realtime_mr_status_change subscription' do + merge_request = build_stubbed(:merge_request) + + expect(GitlabSchema.subscriptions).not_to receive(:trigger) + + GraphqlTriggers.merge_request_merge_status_updated(merge_request) + end + end end describe '.merge_request_approval_state_updated' do - it 'triggers the mergeRequestApprovalStateUpdated subscription' do + it 'triggers the merge_request_approval_state_updated subscription' do merge_request = build_stubbed(:merge_request) expect(GitlabSchema.subscriptions).to receive(:trigger).with( - 'mergeRequestApprovalStateUpdated', + :merge_request_approval_state_updated, { issuable_id: merge_request.to_gid }, merge_request ).and_call_original diff --git a/spec/graphql/mutations/achievements/award_spec.rb b/spec/graphql/mutations/achievements/award_spec.rb new file mode 100644 index 00000000000..1bfad46a616 --- /dev/null +++ b/spec/graphql/mutations/achievements/award_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Mutations::Achievements::Award, feature_category: :user_profile do + include GraphqlHelpers + + let_it_be(:developer) { create(:user) } + let_it_be(:maintainer) { create(:user) } + let_it_be(:recipient) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:achievement) { create(:achievement, namespace: group) } + + describe '#resolve' do + subject(:resolve_mutation) do + described_class.new(object: nil, context: { current_user: current_user }, field: nil).resolve( + achievement_id: achievement&.to_global_id, user_id: recipient&.to_global_id + ) + end + + before_all do + group.add_developer(developer) + group.add_maintainer(maintainer) + end + + context 'when the user does not have permission' do + let(:current_user) { developer } + + it 'raises an error' do + expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + .with_message(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR) + end + end + + context 'when the user has permission' do + let(:current_user) { maintainer } + + context 'when the params are invalid' do + let(:achievement) { nil } + + it 'returns the validation error' do + expect { resolve_mutation }.to raise_error { Gitlab::Graphql::Errors::ArgumentError } + end + end + + it 'creates user_achievement with correct values' do + expect(resolve_mutation[:user_achievement]).to have_attributes({ achievement: achievement, user: recipient }) + end + end + end + + specify { expect(described_class).to require_graphql_authorizations(:award_achievement) } +end diff --git a/spec/graphql/mutations/achievements/delete_spec.rb b/spec/graphql/mutations/achievements/delete_spec.rb new file mode 100644 index 00000000000..0eb6f5a2e6f --- /dev/null +++ b/spec/graphql/mutations/achievements/delete_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Mutations::Achievements::Delete, feature_category: :user_profile do + include GraphqlHelpers + + let_it_be(:developer) { create(:user) } + let_it_be(:maintainer) { create(:user) } + let_it_be(:recipient) { create(:user) } + let_it_be(:group) { create(:group) } + + let(:achievement) { create(:achievement, namespace: group) } + + describe '#resolve' do + subject(:resolve_mutation) do + described_class.new(object: nil, context: { current_user: current_user }, field: nil).resolve( + achievement_id: achievement&.to_global_id + ) + end + + before_all do + group.add_developer(developer) + group.add_maintainer(maintainer) + end + + context 'when the user does not have permission' do + let(:current_user) { developer } + + it 'raises an error' do + expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + .with_message(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR) + end + end + + context 'when the user has permission' do + let(:current_user) { maintainer } + + context 'when the params are invalid' do + let(:achievement) { nil } + + it 'returns the validation error' do + expect { resolve_mutation }.to raise_error { Gitlab::Graphql::Errors::ArgumentError } + end + end + + it 'deletes the achievement' do + resolve_mutation + + expect(Achievements::Achievement.find_by(id: achievement.id)).to be_nil + end + end + end + + specify { expect(described_class).to require_graphql_authorizations(:admin_achievement) } +end diff --git a/spec/graphql/mutations/achievements/revoke_spec.rb b/spec/graphql/mutations/achievements/revoke_spec.rb new file mode 100644 index 00000000000..0c221b492af --- /dev/null +++ b/spec/graphql/mutations/achievements/revoke_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Mutations::Achievements::Revoke, feature_category: :user_profile do + include GraphqlHelpers + + let_it_be(:developer) { create(:user) } + let_it_be(:maintainer) { create(:user) } + let_it_be(:recipient) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:achievement) { create(:achievement, namespace: group) } + let_it_be(:user_achievement) { create(:user_achievement, achievement: achievement) } + + describe '#resolve' do + subject(:resolve_mutation) do + described_class.new(object: nil, context: { current_user: current_user }, field: nil).resolve( + user_achievement_id: user_achievement&.to_global_id + ) + end + + before_all do + group.add_developer(developer) + group.add_maintainer(maintainer) + end + + context 'when the user does not have permission' do + let(:current_user) { developer } + + it 'raises an error' do + expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + .with_message(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR) + end + end + + context 'when the user has permission' do + let(:current_user) { maintainer } + + context 'when the params are invalid' do + let(:user_achievement) { nil } + + it 'returns the validation error' do + expect { resolve_mutation }.to raise_error { Gitlab::Graphql::Errors::ArgumentError } + end + end + + it 'revokes user_achievement' do + response = resolve_mutation[:user_achievement] + + expect(response.revoked_at).not_to be_nil + expect(response.revoked_by_user_id).to be(current_user.id) + end + end + end + + specify { expect(described_class).to require_graphql_authorizations(:award_achievement) } +end diff --git a/spec/graphql/mutations/achievements/update_spec.rb b/spec/graphql/mutations/achievements/update_spec.rb new file mode 100644 index 00000000000..b69c8bef478 --- /dev/null +++ b/spec/graphql/mutations/achievements/update_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Mutations::Achievements::Update, feature_category: :user_profile do + include GraphqlHelpers + + let_it_be(:developer) { create(:user) } + let_it_be(:maintainer) { create(:user) } + let_it_be(:recipient) { create(:user) } + let_it_be(:group) { create(:group) } + + let(:achievement) { create(:achievement, namespace: group) } + let(:name) { 'Hero' } + + describe '#resolve' do + subject(:resolve_mutation) do + described_class.new(object: nil, context: { current_user: current_user }, field: nil).resolve( + achievement_id: achievement&.to_global_id, name: name + ) + end + + before_all do + group.add_developer(developer) + group.add_maintainer(maintainer) + end + + context 'when the user does not have permission' do + let(:current_user) { developer } + + it 'raises an error' do + expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + .with_message(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR) + end + end + + context 'when the user has permission' do + let(:current_user) { maintainer } + + context 'when the params are invalid' do + let(:achievement) { nil } + + it 'returns the validation error' do + expect { resolve_mutation }.to raise_error { Gitlab::Graphql::Errors::ArgumentError } + end + end + + it 'updates the achievement' do + resolve_mutation + + expect(Achievements::Achievement.find_by(id: achievement.id).name).to eq(name) + end + end + end + + specify { expect(described_class).to require_graphql_authorizations(:admin_achievement) } +end diff --git a/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb b/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb index 125e15b70cf..da5531d2b93 100644 --- a/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb +++ b/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb @@ -58,7 +58,6 @@ RSpec.describe Mutations::AlertManagement::Alerts::SetAssignees do it_behaves_like 'an incident management tracked event', :incident_management_alert_assigned it_behaves_like 'Snowplow event tracking with RedisHLL context' do - let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } let(:namespace) { project.namespace.reload } let(:category) { described_class.to_s } let(:user) { current_user } diff --git a/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb b/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb index bcb7c74fa09..8ba1e785b63 100644 --- a/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb +++ b/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb @@ -20,7 +20,6 @@ RSpec.describe Mutations::AlertManagement::Alerts::Todo::Create do it_behaves_like 'an incident management tracked event', :incident_management_alert_todo it_behaves_like 'Snowplow event tracking with RedisHLL context' do - let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } let(:namespace) { project.namespace.reload } let(:category) { described_class.to_s } let(:user) { current_user } diff --git a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb index e49596b37c9..f86046bb0d6 100644 --- a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb +++ b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb @@ -32,7 +32,6 @@ RSpec.describe Mutations::AlertManagement::CreateAlertIssue do it_behaves_like 'an incident management tracked event', :incident_management_alert_create_incident it_behaves_like 'Snowplow event tracking with RedisHLL context' do - let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } let(:namespace) { project.namespace.reload } let(:category) { described_class.to_s } let(:user) { current_user } @@ -57,7 +56,6 @@ RSpec.describe Mutations::AlertManagement::CreateAlertIssue do end it_behaves_like 'Snowplow event tracking with RedisHLL context' do - let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } let(:namespace) { project.namespace.reload } let(:category) { described_class.to_s } let(:user) { current_user } diff --git a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb index 22ad93df79b..fb11ec7065b 100644 --- a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb +++ b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb @@ -36,7 +36,6 @@ RSpec.describe Mutations::AlertManagement::UpdateAlertStatus do end it_behaves_like 'Snowplow event tracking with RedisHLL context' do - let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } let(:namespace) { project.namespace } let(:category) { described_class.to_s } let(:user) { current_user } diff --git a/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb b/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb index 44147987ebb..0485796fe56 100644 --- a/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb +++ b/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb @@ -20,6 +20,10 @@ RSpec.describe Mutations::Ci::JobTokenScope::AddProject, feature_category: :cont mutation.resolve(**mutation_args) end + before do + stub_feature_flags(frozen_outbound_job_token_scopes_override: false) + end + context 'when user is not logged in' do let(:current_user) { nil } @@ -43,10 +47,10 @@ RSpec.describe Mutations::Ci::JobTokenScope::AddProject, feature_category: :cont target_project.add_guest(current_user) end - it 'adds target project to the outbound job token scope by default' do + it 'adds target project to the inbound job token scope by default' do expect do expect(subject).to include(ci_job_token_scope: be_present, errors: be_empty) - end.to change { Ci::JobToken::ProjectScopeLink.outbound.count }.by(1) + end.to change { Ci::JobToken::ProjectScopeLink.inbound.count }.by(1) end context 'when mutation uses the direction argument' do @@ -55,10 +59,8 @@ RSpec.describe Mutations::Ci::JobTokenScope::AddProject, feature_category: :cont context 'when targeting the outbound allowlist' do let(:direction) { :outbound } - it 'adds the target project' do - expect do - expect(subject).to include(ci_job_token_scope: be_present, errors: be_empty) - end.to change { Ci::JobToken::ProjectScopeLink.outbound.count }.by(1) + it 'raises an error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ArgumentError) end end @@ -73,6 +75,42 @@ RSpec.describe Mutations::Ci::JobTokenScope::AddProject, feature_category: :cont end end + context 'when FF frozen_outbound_job_token_scopes is disabled' do + before do + stub_feature_flags(frozen_outbound_job_token_scopes: false) + end + + it 'adds target project to the outbound job token scope by default' do + expect do + expect(subject).to include(ci_job_token_scope: be_present, errors: be_empty) + end.to change { Ci::JobToken::ProjectScopeLink.outbound.count }.by(1) + end + + context 'when mutation uses the direction argument' do + let(:mutation_args) { super().merge!(direction: direction) } + + context 'when targeting the outbound allowlist' do + let(:direction) { :outbound } + + it 'adds the target project' do + expect do + expect(subject).to include(ci_job_token_scope: be_present, errors: be_empty) + end.to change { Ci::JobToken::ProjectScopeLink.outbound.count }.by(1) + end + end + + context 'when targeting the inbound allowlist' do + let(:direction) { :inbound } + + it 'adds the target project' do + expect do + expect(subject).to include(ci_job_token_scope: be_present, errors: be_empty) + end.to change { Ci::JobToken::ProjectScopeLink.inbound.count }.by(1) + end + end + end + end + context 'when the service returns an error' do let(:service) { double(:service) } @@ -81,7 +119,7 @@ RSpec.describe Mutations::Ci::JobTokenScope::AddProject, feature_category: :cont project, current_user ).and_return(service) - expect(service).to receive(:execute).with(target_project, direction: :outbound).and_return(ServiceResponse.error(message: 'The error message')) + expect(service).to receive(:execute).with(target_project, direction: :inbound).and_return(ServiceResponse.error(message: 'The error message')) expect(subject.fetch(:ci_job_token_scope)).to be_nil expect(subject.fetch(:errors)).to include("The error message") diff --git a/spec/graphql/mutations/concerns/mutations/finds_by_gid_spec.rb b/spec/graphql/mutations/concerns/mutations/finds_by_gid_spec.rb deleted file mode 100644 index 451f6d1fe06..00000000000 --- a/spec/graphql/mutations/concerns/mutations/finds_by_gid_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Mutations::FindsByGid do - include GraphqlHelpers - - let(:mutation_class) do - Class.new(Mutations::BaseMutation) do - authorize :read_user - - include Mutations::FindsByGid - end - end - - let(:query) { query_double(schema: GitlabSchema) } - let(:context) { GraphQL::Query::Context.new(query: query, object: nil, values: { current_user: user }) } - let(:user) { create(:user) } - let(:gid) { user.to_global_id } - - subject(:mutation) { mutation_class.new(object: nil, context: context, field: nil) } - - it 'calls GitlabSchema.find_by_gid to find objects during authorized_find!' do - expect(mutation.authorized_find!(id: gid)).to eq(user) - end -end diff --git a/spec/graphql/mutations/container_repositories/destroy_spec.rb b/spec/graphql/mutations/container_repositories/destroy_spec.rb index 50e83ccdd30..85e0ac96e55 100644 --- a/spec/graphql/mutations/container_repositories/destroy_spec.rb +++ b/spec/graphql/mutations/container_repositories/destroy_spec.rb @@ -25,7 +25,7 @@ RSpec.describe Mutations::ContainerRepositories::Destroy do .to receive(:new).with(nil, user, event_name: :delete_repository, scope: :container).and_call_original expect(DeleteContainerRepositoryWorker).not_to receive(:perform_async) - expect { subject }.to change { ::Packages::Event.count }.by(1) + subject expect(container_repository.reload.delete_scheduled?).to be true end end diff --git a/spec/graphql/mutations/container_repositories/destroy_tags_spec.rb b/spec/graphql/mutations/container_repositories/destroy_tags_spec.rb index 3e5f28ee244..96dd1754155 100644 --- a/spec/graphql/mutations/container_repositories/destroy_tags_spec.rb +++ b/spec/graphql/mutations/container_repositories/destroy_tags_spec.rb @@ -39,7 +39,7 @@ RSpec.describe Mutations::ContainerRepositories::DestroyTags do it 'creates a package event' do expect(::Packages::CreateEventService) .to receive(:new).with(nil, user, event_name: :delete_tag_bulk, scope: :tag).and_call_original - expect { subject }.to change { ::Packages::Event.count }.by(1) + subject end end @@ -87,7 +87,7 @@ RSpec.describe Mutations::ContainerRepositories::DestroyTags do it 'does not create a package event' do expect(::Packages::CreateEventService).not_to receive(:new) - expect { subject }.not_to change { ::Packages::Event.count } + subject end end end diff --git a/spec/graphql/mutations/customer_relations/contacts/create_spec.rb b/spec/graphql/mutations/customer_relations/contacts/create_spec.rb index f2bbf0949fb..3ee898c2079 100644 --- a/spec/graphql/mutations/customer_relations/contacts/create_spec.rb +++ b/spec/graphql/mutations/customer_relations/contacts/create_spec.rb @@ -57,10 +57,10 @@ RSpec.describe Mutations::CustomerRelations::Contacts::Create do end end - context 'when attaching to an organization' do + context 'when attaching to an crm_organization' do context 'when all ok' do before do - organization = create(:organization, group: group) + organization = create(:crm_organization, group: group) valid_params[:organization_id] = organization.to_global_id end @@ -69,7 +69,7 @@ RSpec.describe Mutations::CustomerRelations::Contacts::Create do end end - context 'when organization does not exist' do + context 'when crm_organization does not exist' do before do valid_params[:organization_id] = global_id_of(model_name: 'CustomerRelations::Organization', id: non_existing_record_id) end @@ -79,10 +79,10 @@ RSpec.describe Mutations::CustomerRelations::Contacts::Create do end end - context 'when organzation belongs to a different group' do + context 'when crm_organzation belongs to a different group' do before do - organization = create(:organization) - valid_params[:organization_id] = organization.to_global_id + crm_organization = create(:crm_organization) + valid_params[:organization_id] = crm_organization.to_global_id end it 'returns the relevant error' do diff --git a/spec/graphql/mutations/customer_relations/organizations/create_spec.rb b/spec/graphql/mutations/customer_relations/organizations/create_spec.rb index ffc9632350a..cf1ff2d5653 100644 --- a/spec/graphql/mutations/customer_relations/organizations/create_spec.rb +++ b/spec/graphql/mutations/customer_relations/organizations/create_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Create do let_it_be(:group) { create(:group, :crm_enabled) } let(:valid_params) do - attributes_for(:organization, + attributes_for(:crm_organization, group: group, description: 'This company is super important!', default_rate: 1_000 diff --git a/spec/graphql/mutations/customer_relations/organizations/update_spec.rb b/spec/graphql/mutations/customer_relations/organizations/update_spec.rb index f0f37ee9c47..2fad320b497 100644 --- a/spec/graphql/mutations/customer_relations/organizations/update_spec.rb +++ b/spec/graphql/mutations/customer_relations/organizations/update_spec.rb @@ -10,10 +10,10 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do let(:default_rate) { 1000.to_f } let(:description) { 'VIP' } let(:does_not_exist_or_no_permission) { Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR } - let(:organization) { create(:organization, group: group) } + let(:crm_organization) { create(:crm_organization, group: group) } let(:attributes) do { - id: organization.to_global_id, + id: crm_organization.to_global_id, name: name, default_rate: default_rate, description: description @@ -27,7 +27,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do ) end - context 'when the user does not have permission to update an organization' do + context 'when the user does not have permission to update an crm_organization' do before do group.add_reporter(user) end @@ -38,7 +38,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do end end - context 'when the organization does not exist' do + context 'when the crm_organization does not exist' do it 'raises an error' do attributes[:id] = "gid://gitlab/CustomerRelations::Organization/#{non_existing_record_id}" @@ -47,12 +47,12 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do end end - context 'when the user has permission to update an organization' do + context 'when the user has permission to update an crm_organization' do before_all do group.add_developer(user) end - it 'updates the organization with correct values' do + it 'updates the crm_organization with correct values' do expect(resolve_mutation[:organization]).to have_attributes(attributes) end diff --git a/spec/graphql/mutations/design_management/delete_spec.rb b/spec/graphql/mutations/design_management/delete_spec.rb index 79196d4965d..a76943b9ff8 100644 --- a/spec/graphql/mutations/design_management/delete_spec.rb +++ b/spec/graphql/mutations/design_management/delete_spec.rb @@ -86,7 +86,7 @@ RSpec.describe Mutations::DesignManagement::Delete do end end - it 'runs no more than 30 queries' do + it 'runs no more than 31 queries' do allow(Gitlab::Tracking).to receive(:event) # rubocop:disable RSpec/ExpectGitlabTracking filenames.each(&:present?) # ignore setup @@ -107,22 +107,23 @@ RSpec.describe Mutations::DesignManagement::Delete do # 14. project.authorizations for user (same query as 5) # 15. current designs by filename and issue # 16, 17 project.authorizations for user (same query as 5) - # 18. find route by id and source_type - # 19. find plan for standard context + # 18. find design_management_repository for project + # 19. find route by id and source_type + # 20. find plan for standard context # ------------- our queries are below: - # 20. start transaction 1 - # 21. start transaction 2 - # 22. find version by sha and issue - # 23. exists version with sha and issue? - # 24. leave transaction 2 - # 25. create version with sha and issue - # 26. create design-version links - # 27. validate version.actions.present? - # 28. validate version.issue.present? - # 29. validate version.sha is unique - # 30. leave transaction 1 + # 21. start transaction 1 + # 22. start transaction 2 + # 23. find version by sha and issue + # 24. exists version with sha and issue? + # 25. leave transaction 2 + # 26. create version with sha and issue + # 27. create design-version links + # 28. validate version.actions.present? + # 29. validate version.issue.present? + # 30. validate version.sha is unique + # 31. leave transaction 1 # - expect { run_mutation }.not_to exceed_query_limit(30) + expect { run_mutation }.not_to exceed_query_limit(31) end end diff --git a/spec/graphql/mutations/environments/stop_spec.rb b/spec/graphql/mutations/environments/stop_spec.rb new file mode 100644 index 00000000000..085d168bc53 --- /dev/null +++ b/spec/graphql/mutations/environments/stop_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Mutations::Environments::Stop, feature_category: :environment_management do + let_it_be(:project) { create(:project) } + let_it_be(:environment) { create(:environment, project: project, state: 'available') } + let_it_be(:maintainer) { create(:user) } + let_it_be(:reporter) { create(:user) } + + let(:user) { maintainer } + + subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + + before_all do + project.add_maintainer(maintainer) + project.add_reporter(reporter) + end + + describe '#resolve' do + subject { mutation.resolve(id: environment_id, force: force) } + + let(:environment_id) { environment.to_global_id } + let(:force) { false } + + context 'when service execution succeeded' do + it 'returns no errors' do + expect(subject[:errors]).to be_empty + end + + it 'stops the environment' do + expect(subject[:environment]).to be_stopped + end + end + + context 'when service cannot change the status without force' do + before do + environment.update!(state: 'stopping') + end + + it 'returns an error' do + expect(subject) + .to eq({ + environment: environment, + errors: ['Attemped to stop the environment but failed to change the status'] + }) + end + end + + context 'when force is set to true' do + let(:force) { true } + + context 'and state transition would fail without force' do + before do + environment.update!(state: 'stopping') + end + + it 'stops the environment' do + expect(subject[:environment]).to be_stopped + end + end + end + + context 'when user is reporter who does not have permission to access the environment' do + let(:user) { reporter } + + it 'raises an error' do + expect { subject }.to raise_error(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR) + end + end + end +end diff --git a/spec/graphql/mutations/members/bulk_update_base_spec.rb b/spec/graphql/mutations/members/bulk_update_base_spec.rb new file mode 100644 index 00000000000..61a27984824 --- /dev/null +++ b/spec/graphql/mutations/members/bulk_update_base_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Mutations::Members::BulkUpdateBase, feature_category: :subgroups do + include GraphqlHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group).tap { |group| group.add_owner(user) } } + + it 'raises a NotImplementedError error if the source_type method is called on the base class' do + mutation = described_class.new(context: { current_user: user }, object: nil, field: nil) + + expect { mutation.resolve(group_id: group.to_gid.to_s) }.to raise_error(NotImplementedError) + end +end diff --git a/spec/graphql/mutations/release_asset_links/create_spec.rb b/spec/graphql/mutations/release_asset_links/create_spec.rb index a5291a00799..cc6c1554866 100644 --- a/spec/graphql/mutations/release_asset_links/create_spec.rb +++ b/spec/graphql/mutations/release_asset_links/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::ReleaseAssetLinks::Create do +RSpec.describe Mutations::ReleaseAssetLinks::Create, feature_category: :release_orchestration do include GraphqlHelpers let_it_be(:project) { create(:project, :private, :repository) } diff --git a/spec/graphql/mutations/release_asset_links/delete_spec.rb b/spec/graphql/mutations/release_asset_links/delete_spec.rb index cca7bd2ba38..3aecc44afd1 100644 --- a/spec/graphql/mutations/release_asset_links/delete_spec.rb +++ b/spec/graphql/mutations/release_asset_links/delete_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::ReleaseAssetLinks::Delete do +RSpec.describe Mutations::ReleaseAssetLinks::Delete, feature_category: :release_orchestration do include GraphqlHelpers let_it_be(:project) { create(:project, :private, :repository) } @@ -60,6 +60,18 @@ RSpec.describe Mutations::ReleaseAssetLinks::Delete do expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) end end + + context 'when destroy process fails' do + before do + allow_next_instance_of(::Releases::Links::DestroyService) do |service| + allow(service).to receive(:execute).and_return(ServiceResponse.error(message: 'error')) + end + end + + it 'returns errors' do + expect(resolve).to include(errors: 'error') + end + end end context 'when the current user does not have access to delete the link' do diff --git a/spec/graphql/mutations/release_asset_links/update_spec.rb b/spec/graphql/mutations/release_asset_links/update_spec.rb index e119cf9cc77..abb091fc68d 100644 --- a/spec/graphql/mutations/release_asset_links/update_spec.rb +++ b/spec/graphql/mutations/release_asset_links/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::ReleaseAssetLinks::Update do +RSpec.describe Mutations::ReleaseAssetLinks::Update, feature_category: :release_orchestration do include GraphqlHelpers let_it_be(:project) { create(:project, :private, :repository) } diff --git a/spec/graphql/mutations/work_items/update_spec.rb b/spec/graphql/mutations/work_items/update_spec.rb new file mode 100644 index 00000000000..3acb06346a4 --- /dev/null +++ b/spec/graphql/mutations/work_items/update_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Mutations::WorkItems::Update, feature_category: :portfolio_management do + let_it_be(:project) { create(:project) } + let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } } + let_it_be(:current_work_item) { create(:work_item, :task, project: project) } + let_it_be(:parent_work_item) { create(:work_item, project: project) } + + subject(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) } + + describe '#ready?' do + let(:current_user) { developer } + let(:current_gid) { current_work_item.to_gid.to_s } + let(:parent_gid) { parent_work_item.to_gid.to_s } + let(:valid_arguments) { { id: current_gid, parent_id: parent_gid } } + + it { is_expected.to be_ready(**valid_arguments) } + end +end diff --git a/spec/graphql/resolvers/achievements/achievements_resolver_spec.rb b/spec/graphql/resolvers/achievements/achievements_resolver_spec.rb new file mode 100644 index 00000000000..a70c89aa7c7 --- /dev/null +++ b/spec/graphql/resolvers/achievements/achievements_resolver_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Achievements::AchievementsResolver, feature_category: :user_profile do + include GraphqlHelpers + + let_it_be(:group) { create(:group, :public) } + let_it_be(:achievements) { create_list(:achievement, 3, namespace: group) } + + let(:args) { {} } + + specify do + expect(described_class).to have_nullable_graphql_type(Types::Achievements::AchievementType.connection_type) + end + + describe '#resolve' do + it 'returns all achievements' do + expect(resolve_achievements.items).to match_array(achievements) + end + + context 'with ids argument' do + let(:args) { { ids: [achievements[0].to_global_id, achievements[1].to_global_id] } } + + it 'returns the specified achievement' do + expect(resolve_achievements.items).to contain_exactly(achievements[0], achievements[1]) + end + end + + context 'when `achievements` feature flag is diabled' do + before do + stub_feature_flags(achievements: false) + end + + it 'is empty' do + expect(resolve_achievements).to be_empty + end + end + end + + def resolve_achievements + resolve(described_class, args: args, obj: group) + end +end diff --git a/spec/graphql/resolvers/base_resolver_spec.rb b/spec/graphql/resolvers/base_resolver_spec.rb index 39b00c14161..d80a61fd318 100644 --- a/spec/graphql/resolvers/base_resolver_spec.rb +++ b/spec/graphql/resolvers/base_resolver_spec.rb @@ -112,7 +112,7 @@ RSpec.describe Resolvers::BaseResolver do end def resolve(foo: 1) - [foo * foo] # rubocop: disable Lint/BinaryOperatorWithIdenticalOperands + [foo * foo] end end end diff --git a/spec/graphql/resolvers/blobs_resolver_spec.rb b/spec/graphql/resolvers/blobs_resolver_spec.rb index a666ed2a9fc..26eb6dc0abe 100644 --- a/spec/graphql/resolvers/blobs_resolver_spec.rb +++ b/spec/graphql/resolvers/blobs_resolver_spec.rb @@ -71,6 +71,14 @@ RSpec.describe Resolvers::BlobsResolver do end end + context 'when specifying HEAD ref' do + let(:ref) { 'HEAD' } + + it 'returns the specified blobs for HEAD' do + is_expected.to contain_exactly(have_attributes(path: 'README.md')) + end + end + context 'when specifying an invalid ref' do let(:ref) { 'ma:in' } diff --git a/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb index 5c632ed3443..fddc73fadfe 100644 --- a/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Resolvers::Ci::AllJobsResolver do +RSpec.describe Resolvers::Ci::AllJobsResolver, feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:successful_job) { create(:ci_build, :success, name: 'Job One') } diff --git a/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb b/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb index 5d06db904d5..ff343f3f43d 100644 --- a/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb @@ -78,7 +78,7 @@ RSpec.describe Resolvers::Ci::GroupRunnersResolver, feature_category: :runner_fl status_status: 'active', type_type: :group_type, tag_name: ['active_runner'], - preload: { tag_name: false }, + preload: false, search: 'abc', sort: 'contacted_asc', membership: :descendants, diff --git a/spec/graphql/resolvers/ci/inherited_variables_resolver_spec.rb b/spec/graphql/resolvers/ci/inherited_variables_resolver_spec.rb new file mode 100644 index 00000000000..6837d4b0459 --- /dev/null +++ b/spec/graphql/resolvers/ci/inherited_variables_resolver_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Ci::InheritedVariablesResolver, feature_category: :secrets_management do + include GraphqlHelpers + + describe '#resolve' do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:subgroup) { create(:group, parent: group) } + let_it_be(:project) { create(:project, group: subgroup) } + let_it_be(:project_without_group) { create(:project) } + + let_it_be(:inherited_ci_variables) do + [ + create(:ci_group_variable, group: group, key: 'GROUP_VAR_A'), + create(:ci_group_variable, group: subgroup, key: 'SUBGROUP_VAR_B') + ] + end + + subject(:resolve_variables) { resolve(described_class, obj: obj, ctx: { current_user: user }, args: {}) } + + context 'when project does not have a group' do + let_it_be(:obj) { project_without_group } + + it 'returns an empty array' do + expect(resolve_variables.items.to_a).to match_array([]) + end + end + + context 'when project belongs to a group' do + let_it_be(:obj) { project } + + it 'returns variables from parent group and ancestors' do + expect(resolve_variables.items.to_a).to match_array(inherited_ci_variables) + end + end + end +end diff --git a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb index 581652a8cea..b99eb56d6ab 100644 --- a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Resolvers::Ci::JobsResolver do +RSpec.describe Resolvers::Ci::JobsResolver, feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project, :repository, :public) } @@ -14,10 +14,11 @@ RSpec.describe Resolvers::Ci::JobsResolver do create(:ci_build, :dast, name: 'SAST job', pipeline: pipeline) create(:ci_build, :container_scanning, name: 'Container scanning job', pipeline: pipeline) create(:ci_build, name: 'Job with tags', pipeline: pipeline, tag_list: ['review']) + create(:ci_bridge, name: 'Bridge job', pipeline: pipeline) end describe '#resolve' do - context 'when security_report_types is empty' do + context 'when none of the optional params are given' do it "returns all of the pipeline's jobs" do jobs = resolve(described_class, obj: pipeline, arg_style: :internal) @@ -26,7 +27,8 @@ RSpec.describe Resolvers::Ci::JobsResolver do have_attributes(name: 'DAST job'), have_attributes(name: 'SAST job'), have_attributes(name: 'Container scanning job'), - have_attributes(name: 'Job with tags') + have_attributes(name: 'Job with tags'), + have_attributes(name: 'Bridge job') ) end end @@ -50,12 +52,14 @@ RSpec.describe Resolvers::Ci::JobsResolver do context 'when a job has tags' do it "returns jobs with tags when applicable" do jobs = resolve(described_class, obj: pipeline, arg_style: :internal) + expect(jobs).to contain_exactly( have_attributes(tag_list: []), have_attributes(tag_list: []), have_attributes(tag_list: []), have_attributes(tag_list: []), - have_attributes(tag_list: ['review']) + have_attributes(tag_list: ['review']), + have_attributes(name: 'Bridge job') # A bridge job has no tag list ) end end @@ -72,5 +76,14 @@ RSpec.describe Resolvers::Ci::JobsResolver do ) end end + + context 'when filtering by job kind' do + it "returns jobs with that type" do + jobs = resolve(described_class, obj: pipeline, arg_style: :internal, args: { job_kind: ::Ci::Bridge }) + expect(jobs).to contain_exactly( + have_attributes(name: 'Bridge job') + ) + end + end end end diff --git a/spec/graphql/resolvers/ci/project_runners_resolver_spec.rb b/spec/graphql/resolvers/ci/project_runners_resolver_spec.rb index 4cc00ced104..83435db2ea7 100644 --- a/spec/graphql/resolvers/ci/project_runners_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/project_runners_resolver_spec.rb @@ -67,7 +67,7 @@ RSpec.describe Resolvers::Ci::ProjectRunnersResolver, feature_category: :runner_ status_status: 'active', type_type: :group_type, tag_name: ['active_runner'], - preload: { tag_name: false }, + preload: false, search: 'abc', sort: 'contacted_asc', project: project diff --git a/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb index 6c69cdc19cc..44203fb2912 100644 --- a/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb @@ -27,6 +27,28 @@ RSpec.describe Resolvers::Ci::RunnerProjectsResolver, feature_category: :runner_ end end + context 'with sort argument' do + let(:args) { { sort: sort } } + + context 'when :id_asc' do + let(:sort) { :id_asc } + + it 'returns a lazy value with projects sorted by :id_asc' do + expect(subject).to be_a(GraphQL::Execution::Lazy) + expect(subject.value.items).to eq([project1, project2, project3]) + end + end + + context 'when :id_desc' do + let(:sort) { :id_desc } + + it 'returns a lazy value with projects sorted by :id_desc' do + expect(subject).to be_a(GraphQL::Execution::Lazy) + expect(subject.value.items).to eq([project3, project2, project1]) + end + end + end + context 'with supported arguments' do let(:args) { { membership: true, search_namespaces: true, topics: %w[xyz] } } @@ -47,9 +69,9 @@ RSpec.describe Resolvers::Ci::RunnerProjectsResolver, feature_category: :runner_ end context 'without arguments' do - it 'returns a lazy value with all projects' do + it 'returns a lazy value with all projects sorted by :id_asc' do expect(subject).to be_a(GraphQL::Execution::Lazy) - expect(subject.value).to contain_exactly(project1, project2, project3) + expect(subject.value.items).to eq([project1, project2, project3]) end end end diff --git a/spec/graphql/resolvers/ci/runner_status_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_status_resolver_spec.rb index 2bea256856d..97a10a7da33 100644 --- a/spec/graphql/resolvers/ci/runner_status_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/runner_status_resolver_spec.rb @@ -9,32 +9,12 @@ RSpec.describe Resolvers::Ci::RunnerStatusResolver, feature_category: :runner_fl let(:user) { build(:user) } let(:runner) { build(:ci_runner) } - subject(:resolve_subject) { resolve(described_class, ctx: { current_user: user }, obj: runner, args: args) } + subject(:resolve_subject) { resolve(described_class, ctx: { current_user: user }, obj: runner) } - context 'with legacy_mode' do - context 'set to 14.5' do - let(:args) do - { legacy_mode: '14.5' } - end + it 'calls runner.status and returns it' do + expect(runner).to receive(:status).once.and_return(:stale) - it 'calls runner.status with specified legacy_mode' do - expect(runner).to receive(:status).with('14.5').once.and_return(:online) - - expect(resolve_subject).to eq(:online) - end - end - - context 'set to nil' do - let(:args) do - { legacy_mode: nil } - end - - it 'calls runner.status with specified legacy_mode' do - expect(runner).to receive(:status).with(nil).once.and_return(:stale) - - expect(resolve_subject).to eq(:stale) - end - end + expect(resolve_subject).to eq(:stale) end end end diff --git a/spec/graphql/resolvers/ci/runners_resolver_spec.rb b/spec/graphql/resolvers/ci/runners_resolver_spec.rb index d6da8222234..e4620b96cae 100644 --- a/spec/graphql/resolvers/ci/runners_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/runners_resolver_spec.rb @@ -83,7 +83,7 @@ RSpec.describe Resolvers::Ci::RunnersResolver, feature_category: :runner_fleet d upgrade_status: 'recommended', type_type: :instance_type, tag_name: ['active_runner'], - preload: { tag_name: false }, + preload: false, search: 'abc', sort: 'contacted_asc' } @@ -108,7 +108,7 @@ RSpec.describe Resolvers::Ci::RunnersResolver, feature_category: :runner_fleet d let(:expected_params) do { active: false, - preload: { tag_name: false } + preload: false } end @@ -128,7 +128,7 @@ RSpec.describe Resolvers::Ci::RunnersResolver, feature_category: :runner_fleet d let(:expected_params) do { active: false, - preload: { tag_name: false } + preload: false } end @@ -146,9 +146,7 @@ RSpec.describe Resolvers::Ci::RunnersResolver, feature_category: :runner_fleet d end let(:expected_params) do - { - preload: { tag_name: false } - } + { preload: false } end it 'calls RunnersFinder with expected arguments' do diff --git a/spec/graphql/resolvers/ci/variables_resolver_spec.rb b/spec/graphql/resolvers/ci/variables_resolver_spec.rb index 16b72e8cb7f..42227df1fe5 100644 --- a/spec/graphql/resolvers/ci/variables_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/variables_resolver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Resolvers::Ci::VariablesResolver, feature_category: :pipeline_authoring do +RSpec.describe Resolvers::Ci::VariablesResolver, feature_category: :secrets_management do include GraphqlHelpers describe '#resolve' do diff --git a/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb b/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb index 866f4ce7b5a..dfd1addff71 100644 --- a/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb +++ b/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Resolvers::Clusters::AgentTokensResolver do it { expect(described_class.type).to eq(Types::Clusters::AgentTokenType) } it { expect(described_class.null).to be_truthy } - it { expect(described_class.arguments.keys).to contain_exactly('status') } + it { expect(described_class.arguments.keys).to be_empty } describe '#resolve' do let(:agent) { create(:cluster_agent) } @@ -16,22 +16,15 @@ RSpec.describe Resolvers::Clusters::AgentTokensResolver do let!(:matching_token1) { create(:cluster_agent_token, agent: agent, last_used_at: 5.days.ago) } let!(:matching_token2) { create(:cluster_agent_token, agent: agent, last_used_at: 2.days.ago) } + let!(:revoked_token) { create(:cluster_agent_token, :revoked, agent: agent) } let!(:other_token) { create(:cluster_agent_token) } subject { resolve(described_class, obj: agent, ctx: ctx) } - it 'returns tokens associated with the agent, ordered by last_used_at' do + it 'returns active tokens associated with the agent, ordered by last_used_at' do expect(subject).to eq([matching_token2, matching_token1]) end - context 'token status is specified' do - let!(:revoked_token) { create(:cluster_agent_token, :revoked, agent: agent) } - - subject { resolve(described_class, obj: agent, ctx: ctx, args: { status: 'revoked' }) } - - it { is_expected.to contain_exactly(revoked_token) } - end - context 'user does not have permission' do let(:user) { create(:user) } diff --git a/spec/graphql/resolvers/clusters/agents/authorizations/ci_access_resolver_spec.rb b/spec/graphql/resolvers/clusters/agents/authorizations/ci_access_resolver_spec.rb new file mode 100644 index 00000000000..b5280365794 --- /dev/null +++ b/spec/graphql/resolvers/clusters/agents/authorizations/ci_access_resolver_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Clusters::Agents::Authorizations::CiAccessResolver, + feature_category: :deployment_management do + include GraphqlHelpers + + it { expect(described_class.type).to eq(Types::Clusters::Agents::Authorizations::CiAccessType) } + it { expect(described_class.null).to be_truthy } + + describe '#resolve' do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user, maintainer_projects: [project]) } + + let(:ctx) { { current_user: user } } + + subject { resolve(described_class, obj: project, ctx: ctx) } + + it 'calls the finder' do + expect_next_instance_of(::Clusters::Agents::Authorizations::CiAccess::Finder, project) do |finder| + expect(finder).to receive(:execute) + end + + subject + end + end +end diff --git a/spec/graphql/resolvers/clusters/agents/authorizations/user_access_resolver_spec.rb b/spec/graphql/resolvers/clusters/agents/authorizations/user_access_resolver_spec.rb new file mode 100644 index 00000000000..b7e2fef78eb --- /dev/null +++ b/spec/graphql/resolvers/clusters/agents/authorizations/user_access_resolver_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Clusters::Agents::Authorizations::UserAccessResolver, + feature_category: :deployment_management do + include GraphqlHelpers + + it { expect(described_class.type).to eq(Types::Clusters::Agents::Authorizations::UserAccessType) } + it { expect(described_class.null).to be_truthy } + + describe '#resolve' do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user, maintainer_projects: [project]) } + + let(:ctx) { { current_user: user } } + + subject { resolve(described_class, obj: project, ctx: ctx) } + + it 'calls the finder' do + expect_next_instance_of(::Clusters::Agents::Authorizations::UserAccess::Finder, + user, project: project) do |finder| + expect(finder).to receive(:execute) + end + + subject + end + end +end diff --git a/spec/graphql/resolvers/crm/contacts_resolver_spec.rb b/spec/graphql/resolvers/crm/contacts_resolver_spec.rb index c7c2d11e114..1a53f42633f 100644 --- a/spec/graphql/resolvers/crm/contacts_resolver_spec.rb +++ b/spec/graphql/resolvers/crm/contacts_resolver_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Resolvers::Crm::ContactsResolver do last_name: "DEF", email: "ghi@test.com", description: "LMNO", - organization: create(:organization, group: group), + organization: create(:crm_organization, group: group), state: "inactive" ) end diff --git a/spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb b/spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb index c6ad4beeee0..f6c2040b7d0 100644 --- a/spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb +++ b/spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb @@ -9,10 +9,10 @@ RSpec.describe Resolvers::Crm::OrganizationStateCountsResolver do let_it_be(:group) { create(:group, :crm_enabled) } before_all do - create(:organization, group: group, name: "ABC Corp") - create(:organization, group: group, name: "123 Corp", state: 'inactive') - create_list(:organization, 3, group: group) - create_list(:organization, 2, group: group, state: 'inactive') + create(:crm_organization, group: group, name: "ABC Corp") + create(:crm_organization, group: group, name: "123 Corp", state: 'inactive') + create_list(:crm_organization, 3, group: group) + create_list(:crm_organization, 2, group: group, state: 'inactive') end describe '#resolve' do @@ -36,7 +36,7 @@ RSpec.describe Resolvers::Crm::OrganizationStateCountsResolver do context 'with a group' do context 'when no filter is provided' do - it 'returns the count of all organizations' do + it 'returns the count of all crm_organizations' do counts = resolve_counts(group) expect(counts['active']).to eq(4) expect(counts['inactive']).to eq(3) diff --git a/spec/graphql/resolvers/crm/organizations_resolver_spec.rb b/spec/graphql/resolvers/crm/organizations_resolver_spec.rb index d5980bf3c41..edc1986799a 100644 --- a/spec/graphql/resolvers/crm/organizations_resolver_spec.rb +++ b/spec/graphql/resolvers/crm/organizations_resolver_spec.rb @@ -8,18 +8,18 @@ RSpec.describe Resolvers::Crm::OrganizationsResolver do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group, :crm_enabled) } - let_it_be(:organization_a) do + let_it_be(:crm_organization_a) do create( - :organization, + :crm_organization, group: group, name: "ABC", state: "inactive" ) end - let_it_be(:organization_b) do + let_it_be(:crm_organization_b) do create( - :organization, + :crm_organization, group: group, name: "DEF", state: "active" @@ -28,23 +28,23 @@ RSpec.describe Resolvers::Crm::OrganizationsResolver do describe '#resolve' do context 'with unauthorized user' do - it 'does not rise an error and returns no organizations' do + it 'does not rise an error and returns no crm_organizations' do expect { resolve_organizations(group) }.not_to raise_error expect(resolve_organizations(group)).to be_empty end end context 'with authorized user' do - it 'does not rise an error and returns all organizations in the correct order' do + it 'does not rise an error and returns all crm_organizations in the correct order' do group.add_reporter(user) expect { resolve_organizations(group) }.not_to raise_error - expect(resolve_organizations(group)).to eq([organization_a, organization_b]) + expect(resolve_organizations(group)).to eq([crm_organization_a, crm_organization_b]) end end context 'without parent' do - it 'returns no organizations' do + it 'returns no crm_organizations' do expect(resolve_organizations(nil)).to be_empty end end @@ -55,40 +55,42 @@ RSpec.describe Resolvers::Crm::OrganizationsResolver do end context 'when no filter is provided' do - it 'returns all the organizations in the default order' do - expect(resolve_organizations(group)).to eq([organization_a, organization_b]) + it 'returns all the crm_organizations in the default order' do + expect(resolve_organizations(group)).to eq([crm_organization_a, crm_organization_b]) end end context 'when a sort is provided' do - it 'returns all the organizations in the correct order' do - expect(resolve_organizations(group, { sort: 'NAME_DESC' })).to eq([organization_b, organization_a]) + it 'returns all the crm_organizations in the correct order' do + expect(resolve_organizations(group, { sort: 'NAME_DESC' })).to eq([crm_organization_b, crm_organization_a]) end end context 'when filtering for all states' do - it 'returns all the organizations' do - expect(resolve_organizations(group, { state: 'all' })).to contain_exactly(organization_a, organization_b) + it 'returns all the crm_organizations' do + expect(resolve_organizations(group, { state: 'all' })).to contain_exactly( + crm_organization_a, crm_organization_b + ) end end context 'when search term is provided' do - it 'returns the correct organizations' do - expect(resolve_organizations(group, { search: "def" })).to contain_exactly(organization_b) + it 'returns the correct crm_organizations' do + expect(resolve_organizations(group, { search: "def" })).to contain_exactly(crm_organization_b) end end context 'when state is provided' do - it 'returns the correct organizations' do - expect(resolve_organizations(group, { state: :inactive })).to contain_exactly(organization_a) + it 'returns the correct crm_organizations' do + expect(resolve_organizations(group, { state: :inactive })).to contain_exactly(crm_organization_a) end end context 'when ids are provided' do - it 'returns the correct organizations' do + it 'returns the correct crm_organizations' do expect(resolve_organizations(group, { - ids: [organization_b.to_global_id] - })).to contain_exactly(organization_b) + ids: [crm_organization_b.to_global_id] + })).to contain_exactly(crm_organization_b) end end end diff --git a/spec/graphql/resolvers/data_transfer/group_data_transfer_resolver_spec.rb b/spec/graphql/resolvers/data_transfer/group_data_transfer_resolver_spec.rb new file mode 100644 index 00000000000..4ea3d287454 --- /dev/null +++ b/spec/graphql/resolvers/data_transfer/group_data_transfer_resolver_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::DataTransfer::GroupDataTransferResolver, feature_category: :source_code_management do + include GraphqlHelpers + + let_it_be(:group) { create(:group) } + let_it_be(:current_user) { create(:user) } + + let(:from) { Date.new(2022, 1, 1) } + let(:to) { Date.new(2023, 1, 1) } + let(:finder_results) do + [ + build(:project_data_transfer, date: to, repository_egress: 250000) + ] + end + + context 'with anonymous access' do + let_it_be(:current_user) { nil } + + it 'does not raise an error and returns no data' do + expect { resolve_egress }.not_to raise_error + expect(resolve_egress).to be_nil + end + end + + context 'with authorized user but without enough permissions' do + it 'does not raise an error and returns no data' do + group.add_developer(current_user) + + expect { resolve_egress }.not_to raise_error + expect(resolve_egress).to be_nil + end + end + + context 'when user has permissions to see data transfer' do + before do + group.add_owner(current_user) + end + + include_examples 'Data transfer resolver' + + context 'when data_transfer_monitoring_mock_data is disabled' do + let(:finder) { instance_double(::DataTransfer::GroupDataTransferFinder) } + + before do + stub_feature_flags(data_transfer_monitoring_mock_data: false) + end + + it 'calls GroupDataTransferFinder with expected arguments' do + expect(::DataTransfer::GroupDataTransferFinder).to receive(:new).with( + group: group, from: from, to: to, user: current_user + ).once.and_return(finder) + allow(finder).to receive(:execute).once.and_return(finder_results) + + expect(resolve_egress).to eq({ egress_nodes: finder_results.map(&:attributes) }) + end + end + end + + def resolve_egress + resolve(described_class, obj: group, args: { from: from, to: to }, ctx: { current_user: current_user }) + end +end diff --git a/spec/graphql/resolvers/data_transfer/project_data_transfer_resolver_spec.rb b/spec/graphql/resolvers/data_transfer/project_data_transfer_resolver_spec.rb new file mode 100644 index 00000000000..7307c1a54a9 --- /dev/null +++ b/spec/graphql/resolvers/data_transfer/project_data_transfer_resolver_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::DataTransfer::ProjectDataTransferResolver, feature_category: :source_code_management do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:current_user) { create(:user) } + + let(:from) { Date.new(2022, 1, 1) } + let(:to) { Date.new(2023, 1, 1) } + let(:finder_results) do + [ + { + date: to, + repository_egress: 250000 + } + ] + end + + context 'with anonymous access' do + let_it_be(:current_user) { nil } + + it 'does not raise an error and returns no data' do + expect { resolve_egress }.not_to raise_error + expect(resolve_egress).to be_nil + end + end + + context 'with authorized user but without enough permissions' do + it 'does not raise an error and returns no data' do + project.add_developer(current_user) + + expect { resolve_egress }.not_to raise_error + expect(resolve_egress).to be_nil + end + end + + context 'when user has permissions to see data transfer' do + before do + project.add_owner(current_user) + end + + include_examples 'Data transfer resolver' + + context 'when data_transfer_monitoring_mock_data is disabled' do + let(:finder) { instance_double(::DataTransfer::ProjectDataTransferFinder) } + + before do + stub_feature_flags(data_transfer_monitoring_mock_data: false) + end + + it 'calls ProjectDataTransferFinder with expected arguments' do + expect(::DataTransfer::ProjectDataTransferFinder).to receive(:new).with( + project: project, from: from, to: to, user: current_user + ).once.and_return(finder) + allow(finder).to receive(:execute).once.and_return(finder_results) + + expect(resolve_egress).to eq({ egress_nodes: finder_results }) + end + end + end + + def resolve_egress + resolve(described_class, obj: project, args: { from: from, to: to }, ctx: { current_user: current_user }) + end +end diff --git a/spec/graphql/resolvers/data_transfer_resolver_spec.rb b/spec/graphql/resolvers/data_transfer_resolver_spec.rb deleted file mode 100644 index f5a088dc1c3..00000000000 --- a/spec/graphql/resolvers/data_transfer_resolver_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Resolvers::DataTransferResolver, feature_category: :source_code_management do - include GraphqlHelpers - - describe '.source' do - context 'with base DataTransferResolver' do - it 'raises NotImplementedError' do - expect { described_class.source }.to raise_error ::NotImplementedError - end - end - - context 'with projects DataTransferResolver' do - let(:source) { described_class.project.source } - - it 'outputs "Project"' do - expect(source).to eq 'Project' - end - end - - context 'with groups DataTransferResolver' do - let(:source) { described_class.group.source } - - it 'outputs "Group"' do - expect(source).to eq 'Group' - end - end - end -end diff --git a/spec/graphql/resolvers/group_labels_resolver_spec.rb b/spec/graphql/resolvers/group_labels_resolver_spec.rb index 71290885e6b..341448d7add 100644 --- a/spec/graphql/resolvers/group_labels_resolver_spec.rb +++ b/spec/graphql/resolvers/group_labels_resolver_spec.rb @@ -48,6 +48,40 @@ RSpec.describe Resolvers::GroupLabelsResolver do end end + describe 'association preloading', :saas do + let(:params) do + { + include_ancestor_groups: true, + include_descendant_groups: true, + only_group_labels: false + } + end + + before do + group.add_developer(current_user) + + # warmup + resolve_labels(group, params).to_a + end + + it 'prevents N+1 queries' do + control = Gitlab::WithRequestStore.with_request_store do + ActiveRecord::QueryRecorder.new { resolve_labels(group, params).to_a } + end + + another_project = create(:project, :private, group: sub_subgroup) + another_subgroup = create(:group, :private, parent: group) + create(:label, project: another_project, name: 'another project feature') + create(:group_label, group: another_subgroup, name: 'another group feature') + + expect do + Gitlab::WithRequestStore.with_request_store do + resolve_labels(group, params).to_a + end + end.not_to exceed_query_limit(control.count) + end + end + context 'at group level' do before_all do group.add_developer(current_user) diff --git a/spec/graphql/resolvers/group_milestones_resolver_spec.rb b/spec/graphql/resolvers/group_milestones_resolver_spec.rb index a32a031a88f..b9b8ef1870b 100644 --- a/spec/graphql/resolvers/group_milestones_resolver_spec.rb +++ b/spec/graphql/resolvers/group_milestones_resolver_spec.rb @@ -2,15 +2,15 @@ require 'spec_helper' -RSpec.describe Resolvers::GroupMilestonesResolver do +RSpec.describe Resolvers::GroupMilestonesResolver, feature_category: :team_planning do using RSpec::Parameterized::TableSyntax include GraphqlHelpers describe '#resolve' do let_it_be(:current_user) { create(:user) } - def resolve_group_milestones(args = {}, context = { current_user: current_user }) - resolve(described_class, obj: group, args: args, ctx: context, arg_style: :internal) + def resolve_group_milestones(args: {}, context: { current_user: current_user }, arg_style: :internal) + resolve(described_class, obj: group, args: args, ctx: context, arg_style: arg_style) end let_it_be(:now) { Time.now } @@ -45,18 +45,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do end context 'with parameters' do - it 'calls MilestonesFinder with correct parameters' do - start_date = now - end_date = start_date + 1.hour - - expect(MilestonesFinder).to receive(:new) - .with(args(group_ids: group.id, state: 'closed', start_date: start_date, end_date: end_date)) - .and_call_original - - resolve_group_milestones(start_date: start_date, end_date: end_date, state: 'closed') - end - - it 'understands the timeframe argument' do + it 'timeframe argument' do start_date = now end_date = start_date + 1.hour @@ -64,7 +53,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do .with(args(group_ids: group.id, state: 'closed', start_date: start_date, end_date: end_date)) .and_call_original - resolve_group_milestones(timeframe: { start: start_date, end: end_date }, state: 'closed') + resolve_group_milestones(args: { timeframe: { start: start_date, end: end_date }, state: 'closed' }) end end @@ -76,7 +65,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do .with(args(ids: [milestone.id.to_s], group_ids: group.id, state: 'all')) .and_call_original - resolve_group_milestones(ids: [milestone.to_global_id]) + resolve_group_milestones(args: { ids: [milestone.to_global_id] }) end end @@ -86,12 +75,12 @@ RSpec.describe Resolvers::GroupMilestonesResolver do .with(args(group_ids: group.id, state: 'all', sort: :due_date_desc)) .and_call_original - resolve_group_milestones(sort: :due_date_desc) + resolve_group_milestones(args: { sort: :due_date_desc }) end %i[expired_last_due_date_asc expired_last_due_date_desc].each do |sort_by| it "uses offset-pagination when sorting by #{sort_by}" do - resolved = resolve_group_milestones(sort: sort_by) + resolved = resolve_group_milestones(args: { sort: sort_by }) expect(resolved).to be_a(::Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection) end @@ -99,31 +88,18 @@ RSpec.describe Resolvers::GroupMilestonesResolver do end context 'by timeframe' do - context 'when start_date and end_date are present' do - context 'when start date is after end_date' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, "startDate is after endDate") do - resolve_group_milestones(start_date: now, end_date: now - 2.days) + context 'when timeframe start and end are present' do + context 'when start is after end' do + it 'raises error' do + expect_graphql_error_to_be_created(::Gitlab::Graphql::Errors::ArgumentError, 'start must be before end') do + resolve_group_milestones( + args: { timeframe: { start: now.to_date, end: now.to_date - 2.days } }, + arg_style: :internal_prepared + ) end end end end - - context 'when only start_date is present' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, /Both startDate and endDate/) do - resolve_group_milestones(start_date: now) - end - end - end - - context 'when only end_date is present' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, /Both startDate and endDate/) do - resolve_group_milestones(end_date: now) - end - end - end end context 'when including descendant milestones in a public group' do @@ -143,7 +119,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do create(:milestone, group: inaccessible_group) create(:milestone, project: inaccessible_project) - expect(resolve_group_milestones(args)).to match_array([milestone1, milestone2, milestone3]) + expect(resolve_group_milestones(args: args)).to match_array([milestone1, milestone2, milestone3]) end end @@ -169,7 +145,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do let(:args) { {} } it 'finds milestones only in accessible projects and groups' do - expect(resolve_group_milestones(args)).to match_array([milestone1]) + expect(resolve_group_milestones(args: args)).to match_array([milestone1]) end end @@ -177,7 +153,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do let(:args) { { include_descendants: true } } it 'finds milestones only in accessible projects and groups' do - expect(resolve_group_milestones(args)).to match_array([milestone1, milestone2, milestone3]) + expect(resolve_group_milestones(args: args)).to match_array([milestone1, milestone2, milestone3]) end end @@ -185,7 +161,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do let(:args) { { include_ancestors: true } } it 'finds milestones only in accessible projects and groups' do - expect(resolve_group_milestones(args)).to match_array([milestone1, milestone6]) + expect(resolve_group_milestones(args: args)).to match_array([milestone1, milestone6]) end end @@ -193,7 +169,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do let(:args) { { include_descendants: true, include_ancestors: true } } it 'finds milestones only in accessible projects and groups' do - expect(resolve_group_milestones(args)).to match_array([milestone1, milestone2, milestone3, milestone6]) + expect(resolve_group_milestones(args: args)).to match_array([milestone1, milestone2, milestone3, milestone6]) end end end diff --git a/spec/graphql/resolvers/labels_resolver_spec.rb b/spec/graphql/resolvers/labels_resolver_spec.rb index efd2596b9eb..8196315dd7c 100644 --- a/spec/graphql/resolvers/labels_resolver_spec.rb +++ b/spec/graphql/resolvers/labels_resolver_spec.rb @@ -48,6 +48,40 @@ RSpec.describe Resolvers::LabelsResolver do end end + describe 'association preloading' do + let_it_be(:project) { create(:project, :private, group: sub_subgroup) } + + let(:params) do + { + include_ancestor_groups: true + } + end + + before do + group.add_developer(current_user) + + # warmup + resolve_labels(project, params).to_a + end + + it 'prevents N+1 queries' do + control = Gitlab::WithRequestStore.with_request_store do + ActiveRecord::QueryRecorder.new { resolve_labels(project, params).to_a } + end + + another_project = create(:project, :private, group: subgroup) + another_subgroup = create(:group, :private, parent: group) + create(:label, project: another_project, name: 'another project feature') + create(:group_label, group: another_subgroup, name: 'another group feature') + + expect do + Gitlab::WithRequestStore.with_request_store do + resolve_labels(project, params).to_a + end + end.not_to exceed_query_limit(control.count) + end + end + context 'with a parent project' do before_all do group.add_developer(current_user) diff --git a/spec/graphql/resolvers/metrics/dashboard_resolver_spec.rb b/spec/graphql/resolvers/metrics/dashboard_resolver_spec.rb index 4112e3d4fe6..354fd350aa7 100644 --- a/spec/graphql/resolvers/metrics/dashboard_resolver_spec.rb +++ b/spec/graphql/resolvers/metrics/dashboard_resolver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Resolvers::Metrics::DashboardResolver do +RSpec.describe Resolvers::Metrics::DashboardResolver, feature_category: :metrics do include GraphqlHelpers let_it_be(:current_user) { create(:user) } @@ -21,6 +21,7 @@ RSpec.describe Resolvers::Metrics::DashboardResolver do let(:parent_object) { create(:environment, project: project) } before do + stub_feature_flags(remove_monitor_metrics: false) project.add_developer(current_user) end @@ -39,6 +40,17 @@ RSpec.describe Resolvers::Metrics::DashboardResolver do expect(resolve_dashboard).to be_nil end end + + context 'when metrics dashboard feature is unavailable' do + before do + stub_feature_flags(remove_monitor_metrics: true) + end + + it 'returns nil', :aggregate_failures do + expect(PerformanceMonitoring::PrometheusDashboard).not_to receive(:find_for) + expect(resolve_dashboard).to be_nil + end + end end end end diff --git a/spec/graphql/resolvers/metrics/dashboards/annotation_resolver_spec.rb b/spec/graphql/resolvers/metrics/dashboards/annotation_resolver_spec.rb index a83cef40bdf..2ca194d519c 100644 --- a/spec/graphql/resolvers/metrics/dashboards/annotation_resolver_spec.rb +++ b/spec/graphql/resolvers/metrics/dashboards/annotation_resolver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Resolvers::Metrics::Dashboards::AnnotationResolver do +RSpec.describe Resolvers::Metrics::Dashboards::AnnotationResolver, feature_category: :metrics do include GraphqlHelpers describe '#resolve' do @@ -25,6 +25,10 @@ RSpec.describe Resolvers::Metrics::Dashboards::AnnotationResolver do environment.project.add_developer(current_user) end + before do + stub_feature_flags(remove_monitor_metrics: false) + end + context 'with annotation records' do let_it_be(:annotation_1) { create(:metrics_dashboard_annotation, environment: environment, starting_at: 9.minutes.ago, dashboard_path: path) } @@ -55,6 +59,16 @@ RSpec.describe Resolvers::Metrics::Dashboards::AnnotationResolver do expect(resolve_annotations).to be_empty end end + + context 'when metrics dashboard feature is unavailable' do + before do + stub_feature_flags(remove_monitor_metrics: true) + end + + it 'returns nothing' do + expect(resolve_annotations).to be_nil + end + end end end end diff --git a/spec/graphql/resolvers/paginated_tree_resolver_spec.rb b/spec/graphql/resolvers/paginated_tree_resolver_spec.rb index 9a04b716001..931d4ba132c 100644 --- a/spec/graphql/resolvers/paginated_tree_resolver_spec.rb +++ b/spec/graphql/resolvers/paginated_tree_resolver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Resolvers::PaginatedTreeResolver do +RSpec.describe Resolvers::PaginatedTreeResolver, feature_category: :source_code_management do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } @@ -61,6 +61,16 @@ RSpec.describe Resolvers::PaginatedTreeResolver do end end + context 'when repository is empty' do + before do + allow(repository).to receive(:empty?).and_return(true) + end + + it 'returns nil' do + is_expected.to be(nil) + end + end + describe 'Cursor pagination' do context 'when cursor is invalid' do let(:args) { super().merge(after: 'invalid') } diff --git a/spec/graphql/resolvers/project_issues_resolver_spec.rb b/spec/graphql/resolvers/project_issues_resolver_spec.rb index b2796ad9b18..a510baab5a9 100644 --- a/spec/graphql/resolvers/project_issues_resolver_spec.rb +++ b/spec/graphql/resolvers/project_issues_resolver_spec.rb @@ -359,9 +359,9 @@ RSpec.describe Resolvers::ProjectIssuesResolver do end describe 'filtering by crm' do - let_it_be(:organization) { create(:organization, group: group) } - let_it_be(:contact1) { create(:contact, group: group, organization: organization) } - let_it_be(:contact2) { create(:contact, group: group, organization: organization) } + let_it_be(:crm_organization) { create(:crm_organization, group: group) } + let_it_be(:contact1) { create(:contact, group: group, organization: crm_organization) } + let_it_be(:contact2) { create(:contact, group: group, organization: crm_organization) } let_it_be(:contact3) { create(:contact, group: group) } let_it_be(:crm_issue1) { create(:issue, project: project) } let_it_be(:crm_issue2) { create(:issue, project: project) } @@ -381,9 +381,9 @@ RSpec.describe Resolvers::ProjectIssuesResolver do end end - context 'when filtering by organization' do + context 'when filtering by crm_organization' do it 'returns only the issues for the contact' do - expect(resolve_issues({ crm_organization_id: organization.id })).to contain_exactly(crm_issue1, crm_issue2) + expect(resolve_issues({ crm_organization_id: crm_organization.id })).to contain_exactly(crm_issue1, crm_issue2) end end end diff --git a/spec/graphql/resolvers/project_milestones_resolver_spec.rb b/spec/graphql/resolvers/project_milestones_resolver_spec.rb index ad1190e3df7..af6b16804b0 100644 --- a/spec/graphql/resolvers/project_milestones_resolver_spec.rb +++ b/spec/graphql/resolvers/project_milestones_resolver_spec.rb @@ -94,44 +94,6 @@ RSpec.describe 'Resolvers::ProjectMilestonesResolver' do end context 'by timeframe' do - context 'when start_date and end_date are present' do - it 'calls MilestonesFinder with correct parameters' do - start_date = now - end_date = now + 5.days - - expect(MilestonesFinder).to receive(:new) - .with(args(project_ids: project.id, state: 'all', - start_date: start_date, end_date: end_date, sort: :due_date_asc)) - .and_call_original - - resolve_project_milestones(start_date: start_date, end_date: end_date) - end - - context 'when start date is after end_date' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'startDate is after endDate') do - resolve_project_milestones(start_date: now, end_date: now - 2.days) - end - end - end - end - - context 'when only start_date is present' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, /Both startDate and endDate/) do - resolve_project_milestones(start_date: now) - end - end - end - - context 'when only end_date is present' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, /Both startDate and endDate/) do - resolve_project_milestones(end_date: now) - end - end - end - context 'when passing a timeframe' do it 'calls MilestonesFinder with correct parameters' do start_date = now_date diff --git a/spec/graphql/resolvers/timelog_resolver_spec.rb b/spec/graphql/resolvers/timelog_resolver_spec.rb index cd52308d895..5177873321c 100644 --- a/spec/graphql/resolvers/timelog_resolver_spec.rb +++ b/spec/graphql/resolvers/timelog_resolver_spec.rb @@ -214,7 +214,11 @@ RSpec.describe Resolvers::TimelogResolver, feature_category: :team_planning do let_it_be(:timelog3) { create(:merge_request_timelog, merge_request: merge_request, user: current_user) } it 'blah' do - expect(timelogs).to contain_exactly(timelog1, timelog3) + if user_found + expect(timelogs).to contain_exactly(timelog1, timelog3) + else + expect(timelogs).to be_empty + end end end @@ -250,16 +254,28 @@ RSpec.describe Resolvers::TimelogResolver, feature_category: :team_planning do let(:object) { current_user } let(:extra_args) { {} } let(:args) { {} } + let(:user_found) { true } it_behaves_like 'with a user' end context 'with a user filter' do let(:object) { nil } - let(:extra_args) { { username: current_user.username } } let(:args) { {} } - it_behaves_like 'with a user' + context 'when the user has timelogs' do + let(:extra_args) { { username: current_user.username } } + let(:user_found) { true } + + it_behaves_like 'with a user' + end + + context 'when the user doest not have timelogs' do + let(:extra_args) { { username: 'not_existing_user' } } + let(:user_found) { false } + + it_behaves_like 'with a user' + end end context 'when no object or arguments provided' do diff --git a/spec/graphql/types/achievements/achievement_type_spec.rb b/spec/graphql/types/achievements/achievement_type_spec.rb index f967dc8e25e..08fadcdff22 100644 --- a/spec/graphql/types/achievements/achievement_type_spec.rb +++ b/spec/graphql/types/achievements/achievement_type_spec.rb @@ -14,6 +14,7 @@ RSpec.describe GitlabSchema.types['Achievement'], feature_category: :user_profil description created_at updated_at + user_achievements ] end diff --git a/spec/graphql/types/achievements/user_achievement_type_spec.rb b/spec/graphql/types/achievements/user_achievement_type_spec.rb new file mode 100644 index 00000000000..b7fe4d815f7 --- /dev/null +++ b/spec/graphql/types/achievements/user_achievement_type_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['UserAchievement'], feature_category: :user_profile do + include GraphqlHelpers + + let(:fields) do + %w[ + id + achievement + user + awarded_by_user + revoked_by_user + created_at + updated_at + revoked_at + ] + end + + it { expect(described_class.graphql_name).to eq('UserAchievement') } + it { expect(described_class).to have_graphql_fields(fields) } + it { expect(described_class).to require_graphql_authorizations(:read_user_achievement) } +end diff --git a/spec/graphql/types/ci/catalog/resource_type_spec.rb b/spec/graphql/types/ci/catalog/resource_type_spec.rb new file mode 100644 index 00000000000..d0bb45a4f1d --- /dev/null +++ b/spec/graphql/types/ci/catalog/resource_type_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::Ci::Catalog::ResourceType, feature_category: :pipeline_composition do + specify { expect(described_class.graphql_name).to eq('CiCatalogResource') } + + it 'exposes the expected fields' do + expected_fields = %i[ + id + name + description + icon + ] + + expect(described_class).to have_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/ci/config/include_type_enum_spec.rb b/spec/graphql/types/ci/config/include_type_enum_spec.rb index a88316ae6f2..a75b9018a2e 100644 --- a/spec/graphql/types/ci/config/include_type_enum_spec.rb +++ b/spec/graphql/types/ci/config/include_type_enum_spec.rb @@ -6,6 +6,6 @@ RSpec.describe GitlabSchema.types['CiConfigIncludeType'] do it { expect(described_class.graphql_name).to eq('CiConfigIncludeType') } it 'exposes all the existing include types' do - expect(described_class.values.keys).to match_array(%w[remote local file template]) + expect(described_class.values.keys).to match_array(%w[remote local file template component]) end end diff --git a/spec/graphql/types/ci/inherited_ci_variable_type_spec.rb b/spec/graphql/types/ci/inherited_ci_variable_type_spec.rb new file mode 100644 index 00000000000..daf80ff9978 --- /dev/null +++ b/spec/graphql/types/ci/inherited_ci_variable_type_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['InheritedCiVariable'], feature_category: :secrets_management do + specify do + expect(described_class).to have_graphql_fields( + :id, + :key, + :raw, + :variable_type, + :environment_scope, + :masked, + :protected, + :group_name, + :group_ci_cd_settings_path + ).at_least + end +end diff --git a/spec/graphql/types/ci/job_trace_type_spec.rb b/spec/graphql/types/ci/job_trace_type_spec.rb new file mode 100644 index 00000000000..71803aa9ece --- /dev/null +++ b/spec/graphql/types/ci/job_trace_type_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['CiJobTrace'], feature_category: :continuous_integration do + include GraphqlHelpers + + let_it_be(:job) { create(:ci_build) } + + it 'has the correct fields' do + expected_fields = [:html_summary] + + expect(described_class).to have_graphql_fields(*expected_fields) + end + + it 'shows the correct trace contents' do + job.trace.set('BUILD TRACE') + + expect_next_instance_of(Gitlab::Ci::Trace) do |trace| + expect(trace).to receive(:html).with(last_lines: 10).and_call_original + end + + resolved_field = resolve_field(:html_summary, job.trace) + + expect(resolved_field).to eq("<span>BUILD TRACE</span>") + end +end diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb index 714eaebfe73..e927bac431c 100644 --- a/spec/graphql/types/ci/job_type_spec.rb +++ b/spec/graphql/types/ci/job_type_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Types::Ci::JobType do +RSpec.describe Types::Ci::JobType, feature_category: :continuous_integration do include GraphqlHelpers specify { expect(described_class.graphql_name).to eq('CiJob') } @@ -40,6 +40,8 @@ RSpec.describe Types::Ci::JobType do refPath retryable retried + runner + runnerManager scheduledAt schedulingType shortSha @@ -51,6 +53,11 @@ RSpec.describe Types::Ci::JobType do triggered userPermissions webPath + playPath + canPlayJob + scheduled + trace + failure_message ] expect(described_class).to have_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/ci/runner_manager_type_spec.rb b/spec/graphql/types/ci/runner_manager_type_spec.rb new file mode 100644 index 00000000000..240e1edbf78 --- /dev/null +++ b/spec/graphql/types/ci/runner_manager_type_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['CiRunnerManager'], feature_category: :runner_fleet do + specify { expect(described_class.graphql_name).to eq('CiRunnerManager') } + + specify { expect(described_class).to require_graphql_authorizations(:read_runner_manager) } + + it 'contains attributes related to a runner manager' do + expected_fields = %w[ + architecture_name contacted_at created_at executor_name id ip_address platform_name revision + runner status system_id version + ] + + expect(described_class).to have_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/ci/runner_type_spec.rb b/spec/graphql/types/ci/runner_type_spec.rb index a2d107ae295..dc664f281b7 100644 --- a/spec/graphql/types/ci/runner_type_spec.rb +++ b/spec/graphql/types/ci/runner_type_spec.rb @@ -9,11 +9,11 @@ RSpec.describe GitlabSchema.types['CiRunner'], feature_category: :runner do it 'contains attributes related to a runner' do expected_fields = %w[ - id description created_at contacted_at maximum_timeout access_level active paused status + id description created_by created_at contacted_at managers maximum_timeout access_level active paused status version short_sha revision locked run_untagged ip_address runner_type tag_list - project_count job_count admin_url edit_admin_url user_permissions executor_name architecture_name platform_name - maintenance_note maintenance_note_html groups projects jobs token_expires_at owner_project job_execution_status - ephemeral_authentication_token + project_count job_count admin_url edit_admin_url register_admin_url user_permissions executor_name + architecture_name platform_name maintenance_note maintenance_note_html groups projects jobs token_expires_at + owner_project job_execution_status ephemeral_authentication_token ephemeral_register_url ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/ci/variable_sort_enum_spec.rb b/spec/graphql/types/ci/variable_sort_enum_spec.rb index 1702360a21f..8bfe6dde915 100644 --- a/spec/graphql/types/ci/variable_sort_enum_spec.rb +++ b/spec/graphql/types/ci/variable_sort_enum_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Types::Ci::VariableSortEnum, feature_category: :pipeline_authoring do +RSpec.describe Types::Ci::VariableSortEnum, feature_category: :secrets_management do it 'exposes the available order methods' do expect(described_class.values).to match( 'KEY_ASC' => have_attributes(value: :key_asc), diff --git a/spec/graphql/types/clusters/agent_activity_event_type_spec.rb b/spec/graphql/types/clusters/agent_activity_event_type_spec.rb index cae75485846..f89bd877920 100644 --- a/spec/graphql/types/clusters/agent_activity_event_type_spec.rb +++ b/spec/graphql/types/clusters/agent_activity_event_type_spec.rb @@ -6,6 +6,6 @@ RSpec.describe GitlabSchema.types['ClusterAgentActivityEvent'] do let(:fields) { %i[recorded_at kind level user agent_token] } it { expect(described_class.graphql_name).to eq('ClusterAgentActivityEvent') } - it { expect(described_class).to require_graphql_authorizations(:read_cluster) } + it { expect(described_class).to require_graphql_authorizations(:read_cluster_agent) } it { expect(described_class).to have_graphql_fields(fields) } end diff --git a/spec/graphql/types/clusters/agent_token_type_spec.rb b/spec/graphql/types/clusters/agent_token_type_spec.rb index 1ca6d690c80..e04b33f92f8 100644 --- a/spec/graphql/types/clusters/agent_token_type_spec.rb +++ b/spec/graphql/types/clusters/agent_token_type_spec.rb @@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['ClusterAgentToken'] do it { expect(described_class.graphql_name).to eq('ClusterAgentToken') } - it { expect(described_class).to require_graphql_authorizations(:read_cluster) } + it { expect(described_class).to require_graphql_authorizations(:read_cluster_agent) } it { expect(described_class).to have_graphql_fields(fields) } end diff --git a/spec/graphql/types/clusters/agent_type_spec.rb b/spec/graphql/types/clusters/agent_type_spec.rb index bb1006c55c0..4bae0ea5602 100644 --- a/spec/graphql/types/clusters/agent_type_spec.rb +++ b/spec/graphql/types/clusters/agent_type_spec.rb @@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['ClusterAgent'] do it { expect(described_class.graphql_name).to eq('ClusterAgent') } - it { expect(described_class).to require_graphql_authorizations(:read_cluster) } + it { expect(described_class).to require_graphql_authorizations(:read_cluster_agent) } it { expect(described_class).to include_graphql_fields(*fields) } end diff --git a/spec/graphql/types/clusters/agents/authorizations/ci_access_type_spec.rb b/spec/graphql/types/clusters/agents/authorizations/ci_access_type_spec.rb new file mode 100644 index 00000000000..17725ec11e0 --- /dev/null +++ b/spec/graphql/types/clusters/agents/authorizations/ci_access_type_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['ClusterAgentAuthorizationCiAccess'], + feature_category: :deployment_management do + let(:fields) { %i[agent config] } + + it { expect(described_class.graphql_name).to eq('ClusterAgentAuthorizationCiAccess') } + it { expect(described_class).to have_graphql_fields(fields) } +end diff --git a/spec/graphql/types/clusters/agents/authorizations/user_access_type_spec.rb b/spec/graphql/types/clusters/agents/authorizations/user_access_type_spec.rb new file mode 100644 index 00000000000..0e798cd1b18 --- /dev/null +++ b/spec/graphql/types/clusters/agents/authorizations/user_access_type_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['ClusterAgentAuthorizationUserAccess'], + feature_category: :deployment_management do + let(:fields) { %i[agent config] } + + it { expect(described_class.graphql_name).to eq('ClusterAgentAuthorizationUserAccess') } + it { expect(described_class).to have_graphql_fields(fields) } +end diff --git a/spec/graphql/types/commit_signature_interface_spec.rb b/spec/graphql/types/commit_signature_interface_spec.rb index 4962131d9b5..d37c0d1b4fa 100644 --- a/spec/graphql/types/commit_signature_interface_spec.rb +++ b/spec/graphql/types/commit_signature_interface_spec.rb @@ -18,6 +18,11 @@ RSpec.describe GitlabSchema.types['CommitSignature'] do Types::CommitSignatures::X509SignatureType) end + it 'resolves SSH signatures' do + expect(described_class.resolve_type(build(:ssh_signature), {})).to eq( + Types::CommitSignatures::SshSignatureType) + end + it 'raises an error when type is not known' do expect { described_class.resolve_type(Class, {}) }.to raise_error('Unsupported commit signature type') end diff --git a/spec/graphql/types/commit_signatures/ssh_signature_type_spec.rb b/spec/graphql/types/commit_signatures/ssh_signature_type_spec.rb index 4ffb70a0b22..c16e29312a3 100644 --- a/spec/graphql/types/commit_signatures/ssh_signature_type_spec.rb +++ b/spec/graphql/types/commit_signatures/ssh_signature_type_spec.rb @@ -11,7 +11,7 @@ RSpec.describe GitlabSchema.types['SshSignature'], feature_category: :source_cod it 'contains attributes related to SSH signatures' do expect(described_class).to have_graphql_fields( - :user, :verification_status, :commit_sha, :project, :key + :user, :verification_status, :commit_sha, :project, :key, :key_fingerprint_sha256 ) end end diff --git a/spec/graphql/types/data_transfer/project_data_transfer_type_spec.rb b/spec/graphql/types/data_transfer/project_data_transfer_type_spec.rb new file mode 100644 index 00000000000..a93da279b7f --- /dev/null +++ b/spec/graphql/types/data_transfer/project_data_transfer_type_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['ProjectDataTransfer'], feature_category: :source_code_management do + include GraphqlHelpers + + it 'includes the specific fields' do + expect(described_class).to have_graphql_fields( + :total_egress, :egress_nodes) + end + + describe '#total_egress' do + let_it_be(:project) { create(:project) } + let(:from) { Date.new(2022, 1, 1) } + let(:to) { Date.new(2023, 1, 1) } + let(:finder_result) { 40_000_000 } + + it 'returns mock data' do + expect(resolve_field(:total_egress, { from: from, to: to }, extras: { parent: project }, + arg_style: :internal)).to eq(finder_result) + end + + context 'when data_transfer_monitoring_mock_data is disabled' do + let(:relation) { instance_double(ActiveRecord::Relation) } + + before do + allow(relation).to receive(:sum).and_return(10) + stub_feature_flags(data_transfer_monitoring_mock_data: false) + end + + it 'calls sum on active record relation' do + expect(resolve_field(:total_egress, { egress_nodes: relation }, extras: { parent: project }, + arg_style: :internal)).to eq(10) + end + end + end +end diff --git a/spec/graphql/types/design_management/design_at_version_type_spec.rb b/spec/graphql/types/design_management/design_at_version_type_spec.rb index 4d61ecf62cc..06aefb6fea3 100644 --- a/spec/graphql/types/design_management/design_at_version_type_spec.rb +++ b/spec/graphql/types/design_management/design_at_version_type_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe GitlabSchema.types['DesignAtVersion'] do +RSpec.describe GitlabSchema.types['DesignAtVersion'], feature_category: :portfolio_management do it_behaves_like 'a GraphQL type with design fields' do let(:extra_design_fields) { %i[version design] } let_it_be(:design) { create(:design, :with_versions) } diff --git a/spec/graphql/types/design_management/design_type_spec.rb b/spec/graphql/types/design_management/design_type_spec.rb index 24b007a6b33..a093f785eb7 100644 --- a/spec/graphql/types/design_management/design_type_spec.rb +++ b/spec/graphql/types/design_management/design_type_spec.rb @@ -2,13 +2,16 @@ require 'spec_helper' -RSpec.describe GitlabSchema.types['Design'] do +RSpec.describe GitlabSchema.types['Design'], feature_category: :portfolio_management do specify { expect(described_class.interfaces).to include(Types::CurrentUserTodos) } specify { expect(described_class.interfaces).to include(Types::TodoableInterface) } it_behaves_like 'a GraphQL type with design fields' do - let(:extra_design_fields) { %i[notes current_user_todos discussions versions web_url commenters] } + let(:extra_design_fields) do + %i[notes current_user_todos discussions versions web_url commenters description descriptionHtml] + end + let_it_be(:design) { create(:design, :with_versions) } let(:object_id) { GitlabSchema.id_from_object(design) } let_it_be(:object_id_b) { GitlabSchema.id_from_object(create(:design, :with_versions)) } diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb index 6820cf2738e..0fbf50fe258 100644 --- a/spec/graphql/types/group_type_spec.rb +++ b/spec/graphql/types/group_type_spec.rb @@ -22,7 +22,7 @@ RSpec.describe GitlabSchema.types['Group'] do merge_requests container_repositories container_repositories_count packages dependency_proxy_setting dependency_proxy_manifests dependency_proxy_blobs dependency_proxy_image_count - dependency_proxy_blob_count dependency_proxy_total_size + dependency_proxy_blob_count dependency_proxy_total_size dependency_proxy_total_size_in_bytes dependency_proxy_image_prefix dependency_proxy_image_ttl_policy shared_runners_setting timelogs organization_state_counts organizations contact_state_counts contacts work_item_types diff --git a/spec/graphql/types/issue_type_spec.rb b/spec/graphql/types/issue_type_spec.rb index 7c6cf137a1e..a9fe85ac62f 100644 --- a/spec/graphql/types/issue_type_spec.rb +++ b/spec/graphql/types/issue_type_spec.rb @@ -265,7 +265,10 @@ RSpec.describe GitlabSchema.types['Issue'] do context 'for an incident' do before do - issue.update!(issue_type: Issue.issue_types[:incident]) + issue.update!( + issue_type: WorkItems::Type.base_types[:incident], + work_item_type: WorkItems::Type.default_by_type(:incident) + ) end it { is_expected.to be_nil } @@ -277,44 +280,4 @@ RSpec.describe GitlabSchema.types['Issue'] do end end end - - describe 'type' do - let_it_be(:issue) { create(:issue, project: project) } - - let(:query) do - %( - query { - issue(id: "#{issue.to_gid}") { - type - } - } - ) - end - - subject(:execute) { GitlabSchema.execute(query, context: { current_user: user }).as_json } - - context 'when the issue_type_uses_work_item_types_table feature flag is enabled' do - it 'gets the type field from the work_item_types table' do - expect_next_instance_of(::IssuePresenter) do |presented_issue| - expect(presented_issue).to receive_message_chain(:work_item_type, :base_type) - end - - execute - end - end - - context 'when the issue_type_uses_work_item_types_table feature flag is disabled' do - before do - stub_feature_flags(issue_type_uses_work_item_types_table: false) - end - - it 'does not get the type field from the work_item_types table' do - expect_next_instance_of(::IssuePresenter) do |presented_issue| - expect(presented_issue).not_to receive(:work_item_type) - end - - execute - end - end - end end diff --git a/spec/graphql/types/key_type_spec.rb b/spec/graphql/types/key_type_spec.rb index 78144076467..13c00d94b37 100644 --- a/spec/graphql/types/key_type_spec.rb +++ b/spec/graphql/types/key_type_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe GitlabSchema.types['Key'], feature_category: :authentication_and_authorization do +RSpec.describe GitlabSchema.types['Key'], feature_category: :system_access do specify { expect(described_class.graphql_name).to eq('Key') } it 'contains attributes for SSH keys' do diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb index 8a4c89fc340..bd271da55a9 100644 --- a/spec/graphql/types/merge_request_type_spec.rb +++ b/spec/graphql/types/merge_request_type_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe GitlabSchema.types['MergeRequest'] do +RSpec.describe GitlabSchema.types['MergeRequest'], feature_category: :code_review_workflow do include GraphqlHelpers specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::MergeRequest) } @@ -36,7 +36,7 @@ RSpec.describe GitlabSchema.types['MergeRequest'] do commit_count current_user_todos conflicts auto_merge_enabled approved_by source_branch_protected squash_on_merge available_auto_merge_strategies has_ci mergeable commits committers commits_without_merge_commits squash security_auto_fix default_squash_commit_message - auto_merge_strategy merge_user + auto_merge_strategy merge_user award_emoji prepared_at ] expect(described_class).to have_graphql_fields(*expected_fields).at_least diff --git a/spec/graphql/types/permission_types/issue_spec.rb b/spec/graphql/types/permission_types/issue_spec.rb index 58c5808cbcc..8f43a4a44a0 100644 --- a/spec/graphql/types/permission_types/issue_spec.rb +++ b/spec/graphql/types/permission_types/issue_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Types::PermissionTypes::Issue do expected_permissions = [ :read_issue, :admin_issue, :update_issue, :reopen_issue, :read_design, :create_design, :destroy_design, - :create_note + :create_note, :update_design ] expected_permissions.each do |permission| diff --git a/spec/graphql/types/permission_types/work_item_spec.rb b/spec/graphql/types/permission_types/work_item_spec.rb index db6d78b1538..7e16b43a12f 100644 --- a/spec/graphql/types/permission_types/work_item_spec.rb +++ b/spec/graphql/types/permission_types/work_item_spec.rb @@ -5,7 +5,8 @@ require 'spec_helper' RSpec.describe Types::PermissionTypes::WorkItem do it do expected_permissions = [ - :read_work_item, :update_work_item, :delete_work_item, :admin_work_item + :read_work_item, :update_work_item, :delete_work_item, :admin_work_item, + :admin_parent_link, :set_work_item_metadata ] expected_permissions.each do |permission| diff --git a/spec/graphql/types/project_member_relation_enum_spec.rb b/spec/graphql/types/project_member_relation_enum_spec.rb index 3c947bf8406..a486844a687 100644 --- a/spec/graphql/types/project_member_relation_enum_spec.rb +++ b/spec/graphql/types/project_member_relation_enum_spec.rb @@ -6,6 +6,7 @@ RSpec.describe Types::ProjectMemberRelationEnum do specify { expect(described_class.graphql_name).to eq('ProjectMemberRelation') } it 'exposes all the existing project member relation type values' do - expect(described_class.values.keys).to contain_exactly('DIRECT', 'INHERITED', 'DESCENDANTS', 'INVITED_GROUPS') + relation_types = %w[DIRECT INHERITED DESCENDANTS INVITED_GROUPS SHARED_INTO_ANCESTORS] + expect(described_class.values.keys).to contain_exactly(*relation_types) end end diff --git a/spec/graphql/types/project_statistics_redirect_type_spec.rb b/spec/graphql/types/project_statistics_redirect_type_spec.rb new file mode 100644 index 00000000000..3600a5b932f --- /dev/null +++ b/spec/graphql/types/project_statistics_redirect_type_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['ProjectStatisticsRedirect'], feature_category: :consumables_cost_management do + it 'has all the required fields' do + expect(described_class).to have_graphql_fields(:repository, :build_artifacts, :packages, + :wiki, :snippets, :container_registry) + end +end diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 7f26190830e..bcfdb05ca90 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -24,7 +24,7 @@ RSpec.describe GitlabSchema.types['Project'] do snippets_enabled jobs_enabled public_jobs open_issues_count import_status only_allow_merge_if_pipeline_succeeds request_access_enabled only_allow_merge_if_all_discussions_are_resolved printing_merge_request_link_enabled - namespace group statistics repository merge_requests merge_request issues + namespace group statistics statistics_details_paths repository merge_requests merge_request issues issue milestones pipelines removeSourceBranchAfterMerge pipeline_counts sentryDetailedError snippets grafanaIntegration autocloseReferencedIssues suggestion_commit_message environments environment boards jira_import_status jira_imports services releases release @@ -34,11 +34,11 @@ RSpec.describe GitlabSchema.types['Project'] do issue_status_counts terraform_states alert_management_integrations container_repositories container_repositories_count pipeline_analytics squash_read_only sast_ci_configuration - cluster_agent cluster_agents agent_configurations + cluster_agent cluster_agents agent_configurations ci_access_authorized_agents user_access_authorized_agents ci_template timelogs merge_commit_template squash_commit_template work_item_types recent_issue_boards ci_config_path_or_default packages_cleanup_policy ci_variables timelog_categories fork_targets branch_rules ci_config_variables pipeline_schedules languages - incident_management_timeline_event_tags visible_forks + incident_management_timeline_event_tags visible_forks inherited_ci_variables ] expect(described_class).to include_graphql_fields(*expected_fields) @@ -291,7 +291,7 @@ RSpec.describe GitlabSchema.types['Project'] do let_it_be(:project) { create(:project_empty_repo) } it 'raises an error' do - expect(subject['errors'][0]['message']).to eq('You must <a target="_blank" rel="noopener noreferrer" ' \ + expect(subject['errors'][0]['message']).to eq('UF You must <a target="_blank" rel="noopener noreferrer" ' \ 'href="http://localhost/help/user/project/repository/index.md#' \ 'add-files-to-a-repository">add at least one file to the ' \ 'repository</a> before using Security features.') @@ -333,6 +333,7 @@ RSpec.describe GitlabSchema.types['Project'] do :target_branches, :state, :draft, + :approved, :labels, :before, :after, @@ -676,8 +677,8 @@ RSpec.describe GitlabSchema.types['Project'] do subject { GitlabSchema.execute(query, context: { current_user: user }).as_json } before do - allow(::Gitlab::ServiceDeskEmail).to receive(:enabled?) { true } - allow(::Gitlab::ServiceDeskEmail).to receive(:address_for_key) { 'address-suffix@example.com' } + allow(::Gitlab::Email::ServiceDeskEmail).to receive(:enabled?) { true } + allow(::Gitlab::Email::ServiceDeskEmail).to receive(:address_for_key) { 'address-suffix@example.com' } end context 'when a user can admin issues' do diff --git a/spec/graphql/types/projects/fork_details_type_spec.rb b/spec/graphql/types/projects/fork_details_type_spec.rb index 8e20e2c8299..f79371ce4ca 100644 --- a/spec/graphql/types/projects/fork_details_type_spec.rb +++ b/spec/graphql/types/projects/fork_details_type_spec.rb @@ -9,6 +9,8 @@ RSpec.describe GitlabSchema.types['ForkDetails'], feature_category: :source_code fields = %i[ ahead behind + isSyncing + hasConflicts ] expect(described_class).to have_graphql_fields(*fields) diff --git a/spec/graphql/types/release_asset_link_type_spec.rb b/spec/graphql/types/release_asset_link_type_spec.rb index 0c903b8d27a..57a42a3966f 100644 --- a/spec/graphql/types/release_asset_link_type_spec.rb +++ b/spec/graphql/types/release_asset_link_type_spec.rb @@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['ReleaseAssetLink'] do it 'has the expected fields' do expected_fields = %w[ - id name url external link_type direct_asset_url direct_asset_path + id name url link_type direct_asset_url direct_asset_path ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/root_storage_statistics_type_spec.rb b/spec/graphql/types/root_storage_statistics_type_spec.rb index 07c8378e7a6..5dde6aa8b14 100644 --- a/spec/graphql/types/root_storage_statistics_type_spec.rb +++ b/spec/graphql/types/root_storage_statistics_type_spec.rb @@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['RootStorageStatistics'] do expect(described_class).to have_graphql_fields(:storage_size, :repository_size, :lfs_objects_size, :build_artifacts_size, :packages_size, :wiki_size, :snippets_size, :pipeline_artifacts_size, :uploads_size, :dependency_proxy_size, - :container_registry_size) + :container_registry_size, :registry_size_estimated) end specify { expect(described_class).to require_graphql_authorizations(:read_statistics) } diff --git a/spec/graphql/types/time_tracking/timelog_connection_type_spec.rb b/spec/graphql/types/time_tracking/timelog_connection_type_spec.rb index 5cfe561b42c..db1ffd4f59d 100644 --- a/spec/graphql/types/time_tracking/timelog_connection_type_spec.rb +++ b/spec/graphql/types/time_tracking/timelog_connection_type_spec.rb @@ -37,7 +37,7 @@ RSpec.describe GitlabSchema.types['TimelogConnection'], feature_category: :team_ context 'when requested' do it 'returns the total spent time' do - expect(total_spent_time).to eq(5064) + expect(total_spent_time).to eq('5064') end end end diff --git a/spec/graphql/types/timelog_type_spec.rb b/spec/graphql/types/timelog_type_spec.rb index 59a0e373c5d..aa05c5ffd94 100644 --- a/spec/graphql/types/timelog_type_spec.rb +++ b/spec/graphql/types/timelog_type_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['Timelog'], feature_category: :team_planning do - let_it_be(:fields) { %i[id spent_at time_spent user issue merge_request note summary userPermissions] } + let_it_be(:fields) { %i[id spent_at time_spent user issue merge_request note summary userPermissions project] } it { expect(described_class.graphql_name).to eq('Timelog') } it { expect(described_class).to have_graphql_fields(fields) } diff --git a/spec/graphql/types/user_preferences_type_spec.rb b/spec/graphql/types/user_preferences_type_spec.rb index fac45443290..06749dda239 100644 --- a/spec/graphql/types/user_preferences_type_spec.rb +++ b/spec/graphql/types/user_preferences_type_spec.rb @@ -2,12 +2,13 @@ require 'spec_helper' -RSpec.describe Types::UserPreferencesType do +RSpec.describe Types::UserPreferencesType, feature_category: :user_profile do specify { expect(described_class.graphql_name).to eq('UserPreferences') } it 'exposes the expected fields' do expected_fields = %i[ issues_sort + visibility_pipeline_id_type ] expect(described_class).to have_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/user_type_spec.rb b/spec/graphql/types/user_type_spec.rb index a6b5d454b60..0b0dcf2fb6a 100644 --- a/spec/graphql/types/user_type_spec.rb +++ b/spec/graphql/types/user_type_spec.rb @@ -47,9 +47,10 @@ RSpec.describe GitlabSchema.types['User'], feature_category: :user_profile do profileEnableGitpodPath savedReplies savedReply + user_achievements ] - expect(described_class).to have_graphql_fields(*expected_fields) + expect(described_class).to include_graphql_fields(*expected_fields) end describe 'name field' do diff --git a/spec/graphql/types/visibility_pipeline_id_type_enum_spec.rb b/spec/graphql/types/visibility_pipeline_id_type_enum_spec.rb new file mode 100644 index 00000000000..f1dc6a79b29 --- /dev/null +++ b/spec/graphql/types/visibility_pipeline_id_type_enum_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::VisibilityPipelineIdTypeEnum, feature_category: :user_profile do + specify { expect(described_class.graphql_name).to eq('VisibilityPipelineIdType') } + + it 'exposes all visibility pipeline id types' do + expect(described_class.values.keys).to contain_exactly( + *UserPreference.visibility_pipeline_id_types.keys.map(&:upcase) + ) + end +end diff --git a/spec/graphql/types/work_item_type_spec.rb b/spec/graphql/types/work_item_type_spec.rb index 42d56598944..328450084c2 100644 --- a/spec/graphql/types/work_item_type_spec.rb +++ b/spec/graphql/types/work_item_type_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe GitlabSchema.types['WorkItem'] do +RSpec.describe GitlabSchema.types['WorkItem'], feature_category: :team_planning do specify { expect(described_class.graphql_name).to eq('WorkItem') } specify { expect(described_class).to require_graphql_authorizations(:read_work_item) } @@ -18,6 +18,7 @@ RSpec.describe GitlabSchema.types['WorkItem'] do id iid lock_version + namespace project state title title_html @@ -28,6 +29,8 @@ RSpec.describe GitlabSchema.types['WorkItem'] do updated_at closed_at web_url + create_note_email + reference ] expect(described_class).to have_graphql_fields(*fields) diff --git a/spec/graphql/types/work_items/available_export_fields_enum_spec.rb b/spec/graphql/types/work_items/available_export_fields_enum_spec.rb new file mode 100644 index 00000000000..9010aabe3cc --- /dev/null +++ b/spec/graphql/types/work_items/available_export_fields_enum_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['AvailableExportFields'], feature_category: :team_planning do + specify { expect(described_class.graphql_name).to eq('AvailableExportFields') } + + describe 'enum values' do + using RSpec::Parameterized::TableSyntax + + where(:field_name, :field_value) do + 'ID' | 'id' + 'TYPE' | 'type' + 'TITLE' | 'title' + 'DESCRIPTION' | 'description' + 'AUTHOR' | 'author' + 'AUTHOR_USERNAME' | 'author username' + 'CREATED_AT' | 'created_at' + end + + with_them do + it 'exposes correct available fields' do + expect(described_class.values[field_name].value).to eq(field_value) + end + end + end +end diff --git a/spec/graphql/types/work_items/widget_interface_spec.rb b/spec/graphql/types/work_items/widget_interface_spec.rb index a2b12ed52dc..d955ec5023e 100644 --- a/spec/graphql/types/work_items/widget_interface_spec.rb +++ b/spec/graphql/types/work_items/widget_interface_spec.rb @@ -15,11 +15,14 @@ RSpec.describe Types::WorkItems::WidgetInterface do using RSpec::Parameterized::TableSyntax where(:widget_class, :widget_type_name) do - WorkItems::Widgets::Description | Types::WorkItems::Widgets::DescriptionType - WorkItems::Widgets::Hierarchy | Types::WorkItems::Widgets::HierarchyType - WorkItems::Widgets::Assignees | Types::WorkItems::Widgets::AssigneesType - WorkItems::Widgets::Labels | Types::WorkItems::Widgets::LabelsType - WorkItems::Widgets::Notes | Types::WorkItems::Widgets::NotesType + WorkItems::Widgets::Description | Types::WorkItems::Widgets::DescriptionType + WorkItems::Widgets::Hierarchy | Types::WorkItems::Widgets::HierarchyType + WorkItems::Widgets::Assignees | Types::WorkItems::Widgets::AssigneesType + WorkItems::Widgets::Labels | Types::WorkItems::Widgets::LabelsType + WorkItems::Widgets::Notes | Types::WorkItems::Widgets::NotesType + WorkItems::Widgets::Notifications | Types::WorkItems::Widgets::NotificationsType + WorkItems::Widgets::CurrentUserTodos | Types::WorkItems::Widgets::CurrentUserTodosType + WorkItems::Widgets::AwardEmoji | Types::WorkItems::Widgets::AwardEmojiType end with_them do diff --git a/spec/graphql/types/work_items/widgets/award_emoji_type_spec.rb b/spec/graphql/types/work_items/widgets/award_emoji_type_spec.rb new file mode 100644 index 00000000000..493e628ac83 --- /dev/null +++ b/spec/graphql/types/work_items/widgets/award_emoji_type_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::WorkItems::Widgets::AwardEmojiType, feature_category: :team_planning do + it 'exposes the expected fields' do + expected_fields = %i[award_emoji downvotes upvotes type] + + expect(described_class.graphql_name).to eq('WorkItemWidgetAwardEmoji') + expect(described_class).to have_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/work_items/widgets/current_user_todos_input_type_spec.rb b/spec/graphql/types/work_items/widgets/current_user_todos_input_type_spec.rb new file mode 100644 index 00000000000..0ae660ffac0 --- /dev/null +++ b/spec/graphql/types/work_items/widgets/current_user_todos_input_type_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Types::WorkItems::Widgets::CurrentUserTodosInputType, feature_category: :team_planning do + it { expect(described_class.graphql_name).to eq('WorkItemWidgetCurrentUserTodosInput') } + + it { expect(described_class.arguments.keys).to match_array(%w[action todoId]) } +end diff --git a/spec/graphql/types/work_items/widgets/current_user_todos_type_spec.rb b/spec/graphql/types/work_items/widgets/current_user_todos_type_spec.rb new file mode 100644 index 00000000000..b39adefbd87 --- /dev/null +++ b/spec/graphql/types/work_items/widgets/current_user_todos_type_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::WorkItems::Widgets::CurrentUserTodosType, feature_category: :team_planning do + it 'exposes the expected fields' do + expected_fields = %i[current_user_todos type] + + expect(described_class).to have_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/work_items/widgets/hierarchy_update_input_type_spec.rb b/spec/graphql/types/work_items/widgets/hierarchy_update_input_type_spec.rb index 6221580605e..0d4d31faee1 100644 --- a/spec/graphql/types/work_items/widgets/hierarchy_update_input_type_spec.rb +++ b/spec/graphql/types/work_items/widgets/hierarchy_update_input_type_spec.rb @@ -5,5 +5,11 @@ require 'spec_helper' RSpec.describe ::Types::WorkItems::Widgets::HierarchyUpdateInputType do it { expect(described_class.graphql_name).to eq('WorkItemWidgetHierarchyUpdateInput') } - it { expect(described_class.arguments.keys).to match_array(%w[parentId childrenIds]) } + it 'accepts documented arguments' do + expect(described_class.arguments.keys).to match_array(%w[parentId childrenIds adjacentWorkItemId relativePosition]) + end + + it 'sets the type of relative_position argument to RelativePositionTypeEnum' do + expect(described_class.arguments['relativePosition'].type).to eq(Types::RelativePositionTypeEnum) + end end diff --git a/spec/graphql/types/work_items/widgets/notifications_type_spec.rb b/spec/graphql/types/work_items/widgets/notifications_type_spec.rb new file mode 100644 index 00000000000..4f457a24710 --- /dev/null +++ b/spec/graphql/types/work_items/widgets/notifications_type_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::WorkItems::Widgets::NotificationsType, feature_category: :team_planning do + it 'exposes the expected fields' do + expected_fields = %i[subscribed type] + + expect(described_class.graphql_name).to eq('WorkItemWidgetNotifications') + expect(described_class).to have_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/work_items/widgets/notifications_update_input_type_spec.rb b/spec/graphql/types/work_items/widgets/notifications_update_input_type_spec.rb new file mode 100644 index 00000000000..db0d02c597c --- /dev/null +++ b/spec/graphql/types/work_items/widgets/notifications_update_input_type_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Types::WorkItems::Widgets::NotificationsUpdateInputType, feature_category: :team_planning do + it { expect(described_class.graphql_name).to eq('WorkItemWidgetNotificationsUpdateInput') } + + it { expect(described_class.arguments.keys).to contain_exactly('subscribed') } +end |