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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-09-05 15:11:04 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-09-05 15:11:04 +0300
commit164ac94bbd2eadc02ab54322a6fe12ed48ae8041 (patch)
tree6eec29a4fd554eeb6f4a1b296e37bdf6fb5f6f80 /spec
parent8934df30a36d16ac9de9aebb079e16f16fda6912 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/activity_pub/projects/releases_controller_spec.rb134
-rw-r--r--spec/factories/self_managed_prometheus_alert_event.rb12
-rw-r--r--spec/factories/usage_data.rb1
-rw-r--r--spec/finders/organizations/groups_finder_spec.rb12
-rw-r--r--spec/finders/organizations/organization_users_finder_spec.rb35
-rw-r--r--spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event.yml14
-rw-r--r--spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event_ee.yml10
-rw-r--r--spec/frontend/admin/abuse_report/components/report_actions_spec.js27
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js10
-rw-r--r--spec/graphql/types/organizations/organization_type_spec.rb2
-rw-r--r--spec/graphql/types/organizations/organization_user_type_spec.rb11
-rw-r--r--spec/helpers/issuables_helper_spec.rb180
-rw-r--r--spec/helpers/issues_helper_spec.rb5
-rw-r--r--spec/lib/api/helpers_spec.rb43
-rw-r--r--spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb5
-rw-r--r--spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb8
-rw-r--r--spec/lib/gitlab/job_waiter_spec.rb54
-rw-r--r--spec/lib/gitlab/manifest_import/metadata_spec.rb18
-rw-r--r--spec/lib/gitlab/tracking/standard_context_spec.rb3
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb2
-rw-r--r--spec/models/issue_spec.rb1
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/policies/organizations/organization_policy_spec.rb14
-rw-r--r--spec/requests/api/commit_statuses_spec.rb732
-rw-r--r--spec/requests/api/graphql/organizations/organization_query_spec.rb83
-rw-r--r--spec/requests/api/internal/kubernetes_spec.rb154
-rw-r--r--spec/requests/groups/email_campaigns_controller_spec.rb127
-rw-r--r--spec/serializers/activity_pub/activity_streams_serializer_spec.rb157
-rw-r--r--spec/serializers/activity_pub/project_entity_spec.rb32
-rw-r--r--spec/serializers/activity_pub/release_entity_spec.rb48
-rw-r--r--spec/serializers/activity_pub/releases_actor_entity_spec.rb39
-rw-r--r--spec/serializers/activity_pub/releases_actor_serializer_spec.rb16
-rw-r--r--spec/serializers/activity_pub/releases_outbox_serializer_spec.rb34
-rw-r--r--spec/serializers/activity_pub/user_entity_spec.rb28
-rw-r--r--spec/services/ci/create_commit_status_service_spec.rb461
-rw-r--r--spec/services/concerns/services/return_service_responses_spec.rb32
-rw-r--r--spec/support/rspec_order_todo.yml1
-rw-r--r--spec/support/shared_examples/config/metrics/every_metric_definition_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/lib/gitlab/bitbucket_server_import/object_import_shared_examples.rb4
-rw-r--r--spec/workers/concerns/gitlab/github_import/object_importer_spec.rb3
-rw-r--r--spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb2
-rw-r--r--spec/workers/concerns/gitlab/import/notify_upon_death_spec.rb (renamed from spec/workers/concerns/gitlab/notify_upon_death_spec.rb)10
-rw-r--r--spec/workers/gitlab/bitbucket_server_import/import_pull_request_worker_spec.rb6
-rw-r--r--spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb8
-rw-r--r--spec/workers/gitlab/github_import/advance_stage_worker_spec.rb11
-rw-r--r--spec/workers/gitlab/jira_import/import_issue_worker_spec.rb2
46 files changed, 1829 insertions, 766 deletions
diff --git a/spec/controllers/activity_pub/projects/releases_controller_spec.rb b/spec/controllers/activity_pub/projects/releases_controller_spec.rb
new file mode 100644
index 00000000000..8719756b260
--- /dev/null
+++ b/spec/controllers/activity_pub/projects/releases_controller_spec.rb
@@ -0,0 +1,134 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ActivityPub::Projects::ReleasesController, feature_category: :groups_and_projects do
+ include AccessMatchersForController
+
+ let_it_be(:project) { create(:project, :repository, :public) }
+ let_it_be(:private_project) { create(:project, :repository, :private) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:release_1) { create(:release, project: project, released_at: Time.zone.parse('2018-10-18')) }
+ let_it_be(:release_2) { create(:release, project: project, released_at: Time.zone.parse('2019-10-19')) }
+
+ before_all do
+ project.add_developer(developer)
+ end
+
+ shared_examples 'common access controls' do
+ it 'renders a 200' do
+ get(action, params: params)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ context 'when the project is private' do
+ let(:project) { private_project }
+
+ context 'when user is not logged in' do
+ it 'renders a 404' do
+ get(action, params: params)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when user is a developer' do
+ before do
+ sign_in(developer)
+ end
+
+ it 'still renders a 404' do
+ get(action, params: params)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ context 'when activity_pub feature flag is disabled' do
+ before do
+ stub_feature_flags(activity_pub: false)
+ end
+
+ it 'renders a 404' do
+ get(action, params: params)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when activity_pub_project feature flag is disabled' do
+ before do
+ stub_feature_flags(activity_pub_project: false)
+ end
+
+ it 'renders a 404' do
+ get(action, params: params)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ shared_examples_for 'ActivityPub response' do
+ it 'returns an application/activity+json content_type' do
+ expect(response.media_type).to eq 'application/activity+json'
+ end
+
+ it 'is formated as an ActivityStream document' do
+ expect(json_response['@context']).to eq 'https://www.w3.org/ns/activitystreams'
+ end
+ end
+
+ describe 'GET #index' do
+ before do
+ get(action, params: params)
+ end
+
+ let(:action) { :index }
+ let(:params) { { namespace_id: project.namespace, project_id: project } }
+
+ it_behaves_like 'common access controls'
+ it_behaves_like 'ActivityPub response'
+
+ it "returns the project's releases actor profile data" do
+ expect(json_response['id']).to include project_releases_path(project)
+ end
+ end
+
+ describe 'GET #outbox' do
+ before do
+ get(action, params: params)
+ end
+
+ let(:action) { :outbox }
+ let(:params) { { namespace_id: project.namespace, project_id: project, page: page } }
+
+ context 'with no page parameter' do
+ let(:page) { nil }
+
+ it_behaves_like 'common access controls'
+ it_behaves_like 'ActivityPub response'
+
+ it "returns the project's releases collection index" do
+ expect(json_response['id']).to include outbox_project_releases_path(project)
+ expect(json_response['totalItems']).to eq 2
+ end
+ end
+
+ context 'with a page parameter' do
+ let(:page) { 1 }
+
+ it_behaves_like 'common access controls'
+ it_behaves_like 'ActivityPub response'
+
+ it "returns the project's releases list" do
+ expect(json_response['id']).to include outbox_project_releases_path(project, page: 1)
+
+ names = json_response['orderedItems'].map { |release| release['object']['name'] }
+ expect(names).to match_array([release_2.name, release_1.name])
+ end
+ end
+ end
+end
diff --git a/spec/factories/self_managed_prometheus_alert_event.rb b/spec/factories/self_managed_prometheus_alert_event.rb
deleted file mode 100644
index 3a48aba5f54..00000000000
--- a/spec/factories/self_managed_prometheus_alert_event.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-FactoryBot.define do
- factory :self_managed_prometheus_alert_event do
- project
- sequence(:payload_key) { |n| "hash payload key #{n}" }
- status { SelfManagedPrometheusAlertEvent.status_value_for(:firing) }
- title { 'alert' }
- query_expression { 'vector(2)' }
- started_at { Time.now }
- end
-end
diff --git a/spec/factories/usage_data.rb b/spec/factories/usage_data.rb
index b5d4ce9546e..1084891b07f 100644
--- a/spec/factories/usage_data.rb
+++ b/spec/factories/usage_data.rb
@@ -62,7 +62,6 @@ FactoryBot.define do
# Alert Issues
create(:alert_management_alert, issue: issues[0], project: projects[0])
create(:alert_management_alert, issue: alert_bot_issues[0], project: projects[0])
- create(:self_managed_prometheus_alert_event, related_issues: [issues[1]], project: projects[0])
# Kubernetes agents
create(:cluster_agent, project: projects[0])
diff --git a/spec/finders/organizations/groups_finder_spec.rb b/spec/finders/organizations/groups_finder_spec.rb
index 972d80ab036..08c5604149b 100644
--- a/spec/finders/organizations/groups_finder_spec.rb
+++ b/spec/finders/organizations/groups_finder_spec.rb
@@ -3,12 +3,6 @@
require 'spec_helper'
RSpec.describe Organizations::GroupsFinder, feature_category: :cell do
- include AdminModeHelper
-
- let(:current_user) { user }
- let(:params) { {} }
- let(:finder) { described_class.new(organization: organization, current_user: current_user, params: params) }
-
let_it_be(:organization_user) { create(:organization_user) }
let_it_be(:organization) { organization_user.organization }
let_it_be(:user) { organization_user.user }
@@ -23,6 +17,10 @@ RSpec.describe Organizations::GroupsFinder, feature_category: :cell do
create(:group, :private, name: 'no-access', organization: organization)
end
+ let(:current_user) { user }
+ let(:params) { {} }
+ let(:finder) { described_class.new(organization: organization, current_user: current_user, params: params) }
+
before_all do
private_group.add_developer(user)
public_group.add_developer(user)
@@ -40,7 +38,7 @@ RSpec.describe Organizations::GroupsFinder, feature_category: :cell do
end
context 'when organization is nil' do
- let(:finder) { described_class.new(organization: nil, current_user: current_user, params: params) }
+ let(:organization) { nil }
it { is_expected.to be_empty }
end
diff --git a/spec/finders/organizations/organization_users_finder_spec.rb b/spec/finders/organizations/organization_users_finder_spec.rb
new file mode 100644
index 00000000000..d7fba372e40
--- /dev/null
+++ b/spec/finders/organizations/organization_users_finder_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Organizations::OrganizationUsersFinder, feature_category: :cell do
+ let_it_be(:organization) { create(:organization) }
+ let_it_be(:organization_user_1) { create(:organization_user, organization: organization) }
+ let_it_be(:organization_user_2) { create(:organization_user, organization: organization) }
+ let_it_be(:other_organization_user) { create(:organization_user) }
+
+ let(:current_user) { organization_user_1.user }
+ let(:finder) { described_class.new(organization: organization, current_user: current_user) }
+
+ subject(:result) { finder.execute.to_a }
+
+ describe '#execute' do
+ context 'when user is not authorized to read the organization' do
+ let(:current_user) { create(:user) }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'when organization is nil' do
+ let(:organization) { nil }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'when user is authorized to read the organization' do
+ it 'returns all organization users' do
+ expect(result).to contain_exactly(organization_user_1, organization_user_2)
+ end
+ end
+ end
+end
diff --git a/spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event.yml b/spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event.yml
index 1c1ad65796c..6c3a0ef5ed6 100644
--- a/spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event.yml
+++ b/spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event.yml
@@ -1,6 +1,6 @@
---
description:
-category: Groups::EmailCampaignsController
+category: Projects::Pipelines::EmailCampaignsController
action: click
label_description:
property_description:
@@ -13,12 +13,12 @@ identifiers:
product_section:
product_stage:
product_group:
-milestone: "13.11"
+milestone: '13.11'
introduced_by_url:
distributions:
-- ce
-- ee
+ - ce
+ - ee
tiers:
-- free
-- premium
-- ultimate
+ - free
+ - premium
+ - ultimate
diff --git a/spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event_ee.yml b/spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event_ee.yml
index 174468028b8..3381c73f23e 100644
--- a/spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event_ee.yml
+++ b/spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event_ee.yml
@@ -1,6 +1,6 @@
---
description:
-category: Groups::EmailCampaignsController
+category: Projects::Pipelines::EmailCampaignsController
action: click
label_description:
property_description:
@@ -13,10 +13,10 @@ identifiers:
product_section:
product_stage:
product_group:
-milestone: "13.11"
+milestone: '13.11'
introduced_by_url:
distributions:
-- ee
+ - ee
tiers:
-#- premium
-- ultimate
+ #- premium
+ - ultimate
diff --git a/spec/frontend/admin/abuse_report/components/report_actions_spec.js b/spec/frontend/admin/abuse_report/components/report_actions_spec.js
index 6dd6d0e55c5..0e20630db14 100644
--- a/spec/frontend/admin/abuse_report/components/report_actions_spec.js
+++ b/spec/frontend/admin/abuse_report/components/report_actions_spec.js
@@ -191,31 +191,4 @@ describe('ReportActions', () => {
);
});
});
-
- describe('when moderateUserPath is not present', () => {
- it('sends the request to updatePath', async () => {
- jest.spyOn(axios, 'put');
- axiosMock.onPut(report.updatePath).replyOnce(HTTP_STATUS_OK, {});
-
- const reportWithoutModerateUserPath = { ...report };
- delete reportWithoutModerateUserPath.moderateUserPath;
-
- createComponent({ report: reportWithoutModerateUserPath });
-
- clickActionsButton();
-
- await nextTick();
-
- selectAction(params.user_action);
- selectReason(params.reason);
-
- await nextTick();
-
- submitForm();
-
- await waitForPromises();
-
- expect(axios.put).toHaveBeenCalledWith(report.updatePath, expect.any(Object));
- });
- });
});
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index 450eeefd898..ecd2d7f888d 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -1,11 +1,8 @@
-import * as Sentry from '@sentry/browser';
import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
import * as urlUtils from '~/lib/utils/url_utility';
import { safeUrls, unsafeUrls } from './mock_data';
-jest.mock('@sentry/browser');
-
const shas = {
valid: [
'ad9be38573f9ee4c4daec22673478c2dd1d81cd8',
@@ -434,11 +431,10 @@ describe('URL utility', () => {
it('does not navigate to unsafe urls', () => {
// eslint-disable-next-line no-script-url
const url = 'javascript:alert(document.domain)';
- urlUtils.visitUrl(url);
- expect(Sentry.captureException).toHaveBeenCalledWith(
- new RangeError(`Only http and https protocols are allowed: ${url}`),
- );
+ expect(() => {
+ urlUtils.visitUrl(url);
+ }).toThrow(new RangeError(`Only http and https protocols are allowed: ${url}`));
});
it('navigates to a page', () => {
diff --git a/spec/graphql/types/organizations/organization_type_spec.rb b/spec/graphql/types/organizations/organization_type_spec.rb
index 78c5dea7308..26d7c10a715 100644
--- a/spec/graphql/types/organizations/organization_type_spec.rb
+++ b/spec/graphql/types/organizations/organization_type_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['Organization'], feature_category: :cell do
- let(:expected_fields) { %w[groups id name path] }
+ let(:expected_fields) { %w[groups id name organization_users path] }
specify { expect(described_class.graphql_name).to eq('Organization') }
specify { expect(described_class).to require_graphql_authorizations(:read_organization) }
diff --git a/spec/graphql/types/organizations/organization_user_type_spec.rb b/spec/graphql/types/organizations/organization_user_type_spec.rb
new file mode 100644
index 00000000000..876080b0f15
--- /dev/null
+++ b/spec/graphql/types/organizations/organization_user_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['OrganizationUser'], feature_category: :cell do
+ let(:expected_fields) { %w[badges id user] }
+
+ specify { expect(described_class.graphql_name).to eq('OrganizationUser') }
+ specify { expect(described_class).to require_graphql_authorizations(:read_organization_user) }
+ specify { expect(described_class).to have_graphql_fields(*expected_fields) }
+end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 7108d2c1c9b..9fe820ccae9 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -245,80 +245,74 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
end
end
- describe '#updated_at_by' do
+ describe '#issuable_initial_data' do
let(:user) { create(:user) }
- let(:unedited_issuable) { create(:issue) }
- let(:edited_issuable) { create(:issue, last_edited_by: user, created_at: 3.days.ago, updated_at: 1.day.ago, last_edited_at: 2.days.ago) }
- let(:edited_updated_at_by) do
- {
- updatedAt: edited_issuable.last_edited_at.to_time.iso8601,
- updatedBy: {
- name: user.name,
- path: user_path(user)
- }
- }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?).and_return(true)
+ stub_commonmark_sourcepos_disabled
end
- it { expect(helper.updated_at_by(unedited_issuable)).to eq({}) }
- it { expect(helper.updated_at_by(edited_issuable)).to eq(edited_updated_at_by) }
+ context 'when issue' do
+ it 'returns the correct data for an issue' do
+ issue = create(:issue, author: user, description: 'issue text')
+ @project = issue.project
+
+ base_data = {
+ endpoint: "/#{@project.full_path}/-/issues/#{issue.iid}",
+ updateEndpoint: "/#{@project.full_path}/-/issues/#{issue.iid}.json",
+ canUpdate: true,
+ canDestroy: true,
+ issuableRef: "##{issue.iid}",
+ markdownPreviewPath: "/#{@project.full_path}/preview_markdown?target_id=#{issue.iid}&target_type=Issue",
+ markdownDocsPath: '/help/user/markdown',
+ lockVersion: issue.lock_version,
+ state: issue.state,
+ issuableTemplateNamesPath: template_names_path(@project, issue),
+ initialTitleHtml: issue.title,
+ initialTitleText: issue.title,
+ initialDescriptionHtml: '<p dir="auto">issue text</p>',
+ initialDescriptionText: 'issue text',
+ initialTaskCompletionStatus: { completed_count: 0, count: 0 }
+ }
- context 'when updated by a deleted user' do
- let(:edited_updated_at_by) do
- {
- updatedAt: edited_issuable.last_edited_at.to_time.iso8601,
- updatedBy: {
- name: Users::Internal.ghost.name,
- path: user_path(Users::Internal.ghost)
- }
+ issue_only_data = {
+ canCreateIncident: true,
+ fullPath: issue.project.full_path,
+ iid: issue.iid,
+ issuableId: issue.id,
+ issueType: 'issue',
+ isHidden: false,
+ sentryIssueIdentifier: nil,
+ zoomMeetingUrl: nil
}
- end
- before do
- user.destroy!
- end
+ issue_header_data = {
+ authorId: issue.author.id,
+ authorName: issue.author.name,
+ authorUsername: issue.author.username,
+ authorWebUrl: url_for(user_path(issue.author)),
+ createdAt: issue.created_at.to_time.iso8601,
+ isFirstContribution: issue.first_contribution?,
+ serviceDeskReplyTo: nil
+ }
- it 'returns "Ghost user" as edited_by' do
- expect(helper.updated_at_by(edited_issuable.reload)).to eq(edited_updated_at_by)
- end
- end
- end
+ work_items_data = {
+ registerPath: '/users/sign_up?redirect_to_referer=yes',
+ signInPath: '/users/sign_in?redirect_to_referer=yes'
+ }
- describe '#issuable_initial_data' do
- let(:user) { create(:user) }
+ path_data = {
+ projectPath: @project.path,
+ projectId: @project.id,
+ projectNamespace: @project.namespace.path
+ }
- before do
- allow(helper).to receive(:current_user).and_return(user)
- allow(helper).to receive(:can?).and_return(true)
- stub_commonmark_sourcepos_disabled
- end
+ expected = base_data.merge(issue_only_data, issue_header_data, work_items_data, path_data)
- it 'returns the correct data for an issue' do
- issue = create(:issue, author: user, description: 'issue text')
- @project = issue.project
-
- expected_data = {
- endpoint: "/#{@project.full_path}/-/issues/#{issue.iid}",
- updateEndpoint: "/#{@project.full_path}/-/issues/#{issue.iid}.json",
- canUpdate: true,
- canDestroy: true,
- issuableRef: "##{issue.iid}",
- markdownPreviewPath: "/#{@project.full_path}/preview_markdown?target_id=#{issue.iid}&target_type=Issue",
- markdownDocsPath: '/help/user/markdown',
- lockVersion: issue.lock_version,
- projectPath: @project.path,
- projectId: @project.id,
- projectNamespace: @project.namespace.path,
- state: issue.state,
- initialTitleHtml: issue.title,
- initialTitleText: issue.title,
- initialDescriptionHtml: '<p dir="auto">issue text</p>',
- initialDescriptionText: 'issue text',
- initialTaskCompletionStatus: { completed_count: 0, count: 0 },
- issueType: 'issue',
- iid: issue.iid.to_s,
- isHidden: false
- }
- expect(helper.issuable_initial_data(issue)).to match(hash_including(expected_data))
+ expect(helper.issuable_initial_data(issue)).to include(expected)
+ end
end
context 'for incident tab' do
@@ -350,6 +344,46 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
end
end
+ context 'when edited' do
+ it 'contains edited metadata' do
+ edited_issuable = create(:issue, author: user, description: 'issue text', last_edited_by: user, created_at: 3.days.ago, updated_at: 1.day.ago, last_edited_at: 2.days.ago)
+ @project = edited_issuable.project
+
+ expected = {
+ updatedAt: edited_issuable.last_edited_at.to_time.iso8601,
+ updatedBy: {
+ name: user.name,
+ path: user_path(user)
+ }
+ }
+
+ expect(helper.issuable_initial_data(edited_issuable)).to include(expected)
+ end
+
+ context 'when updated by a deleted user' do
+ let(:destroyed_user) { create(:user) }
+
+ before do
+ destroyed_user.destroy!
+ end
+
+ it 'returns "Ghost user" for updated by data' do
+ edited_issuable = create(:issue, author: user, description: 'issue text', last_edited_by: destroyed_user, created_at: 3.days.ago, updated_at: 1.day.ago, last_edited_at: 2.days.ago)
+ @project = edited_issuable.project
+
+ expected = {
+ updatedAt: edited_issuable.last_edited_at.to_time.iso8601,
+ updatedBy: {
+ name: Users::Internal.ghost.name,
+ path: user_path(Users::Internal.ghost)
+ }
+ }
+
+ expect(helper.issuable_initial_data(edited_issuable.reload)).to include(expected)
+ end
+ end
+ end
+
describe '#sentryIssueIdentifier' do
let(:issue) { create(:issue, author: user) }
@@ -569,24 +603,6 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
end
end
- describe '#issuable_display_type' do
- using RSpec::Parameterized::TableSyntax
-
- where(:issuable_type, :issuable_display_type) do
- :issue | 'issue'
- :incident | 'incident'
- :merge_request | 'merge request'
- end
-
- with_them do
- let(:issuable) { build_stubbed(issuable_type) }
-
- subject { helper.issuable_display_type(issuable) }
-
- it { is_expected.to eq(issuable_display_type) }
- end
- end
-
describe '#issuable_type_selector_data' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index dfd65ebdee8..72fa264698d 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IssuesHelper do
+RSpec.describe IssuesHelper, feature_category: :team_planning do
include Features::MergeRequestHelpers
let_it_be(:project) { create(:project) }
@@ -174,14 +174,13 @@ RSpec.describe IssuesHelper do
it 'returns expected result' do
expected = {
can_create_issue: 'true',
+ can_create_incident: 'true',
can_destroy_issue: 'true',
can_reopen_issue: 'true',
can_report_spam: 'false',
can_update_issue: 'true',
- iid: issue.iid,
is_issue_author: 'false',
issue_path: issue_path(issue),
- issue_type: 'issue',
new_issue_path: new_project_issue_path(project, { add_related_issue: issue.iid }),
project_path: project.full_path,
report_abuse_path: add_category_abuse_reports_path,
diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb
index ec63c4af13c..5e28490b2c8 100644
--- a/spec/lib/api/helpers_spec.rb
+++ b/spec/lib/api/helpers_spec.rb
@@ -1076,4 +1076,47 @@ RSpec.describe API::Helpers, feature_category: :shared do
it_behaves_like 'authorized'
end
end
+
+ describe "attributes_for_keys" do
+ let(:hash) do
+ {
+ existing_key_with_present_value: 'actual value',
+ existing_key_with_nil_value: nil,
+ existing_key_with_false_value: false
+ }
+ end
+
+ let(:parameters) { ::ActionController::Parameters.new(hash) }
+ let(:symbol_keys) do
+ %i[
+ existing_key_with_present_value
+ existing_key_with_nil_value
+ existing_key_with_false_value
+ non_existing_key
+ ]
+ end
+
+ let(:string_keys) { symbol_keys.map(&:to_s) }
+ let(:filtered_attrs) do
+ {
+ 'existing_key_with_present_value' => 'actual value',
+ 'existing_key_with_false_value' => false
+ }
+ end
+
+ let(:empty_attrs) { {} }
+
+ where(:params, :keys, :attrs_result) do
+ ref(:hash) | ref(:symbol_keys) | ref(:filtered_attrs)
+ ref(:hash) | ref(:string_keys) | ref(:empty_attrs)
+ ref(:parameters) | ref(:symbol_keys) | ref(:filtered_attrs)
+ ref(:parameters) | ref(:string_keys) | ref(:filtered_attrs)
+ end
+
+ with_them do
+ it 'returns the values for given keys' do
+ expect(helper.attributes_for_keys(keys, params)).to eq(attrs_result)
+ end
+ end
+ end
end
diff --git a/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb b/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb
index 62a52ee5fb9..740cfa767e4 100644
--- a/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb
+++ b/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb
@@ -6,7 +6,10 @@ RSpec.describe Gitlab::SnowplowEventDefinitionGenerator, :silence_stdout, featur
let(:ce_temp_dir) { Dir.mktmpdir }
let(:ee_temp_dir) { Dir.mktmpdir }
let(:timestamp) { Time.now.utc.strftime('%Y%m%d%H%M%S') }
- let(:generator_options) { { 'category' => 'Groups::EmailCampaignsController', 'action' => 'click' } }
+
+ let(:generator_options) do
+ { 'category' => 'Projects::Pipelines::EmailCampaignsController', 'action' => 'click' }
+ end
before do
stub_const("#{described_class}::CE_DIR", ce_temp_dir)
diff --git a/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb b/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb
index ab6b1cd6171..cd96af2c056 100644
--- a/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb
+++ b/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb
@@ -55,14 +55,6 @@ RSpec.describe Gitlab::Email::Message::InProductMarketing::Base do
end
end
- describe '#cta_link' do
- subject(:cta_link) { test_class.new(group: group, user: user, series: series).cta_link }
-
- it 'renders link' do
- expect(CGI.unescapeHTML(cta_link)).to include(Gitlab::Routing.url_helpers.group_email_campaigns_url(group, track: :create, series: series))
- end
- end
-
describe '#progress' do
subject { test_class.new(group: group, user: user, series: series).progress }
diff --git a/spec/lib/gitlab/job_waiter_spec.rb b/spec/lib/gitlab/job_waiter_spec.rb
index af2da8f20c0..829d788b237 100644
--- a/spec/lib/gitlab/job_waiter_spec.rb
+++ b/spec/lib/gitlab/job_waiter_spec.rb
@@ -4,13 +4,35 @@ require 'spec_helper'
RSpec.describe Gitlab::JobWaiter, :redis, feature_category: :shared do
describe '.notify' do
- it 'pushes the jid to the named queue' do
- key = described_class.new.key
+ let(:key) { described_class.new.key }
+ it 'pushes the jid to the named queue', :freeze_time do
described_class.notify(key, 123)
Gitlab::Redis::SharedState.with do |redis|
- expect(redis.ttl(key)).to be > 0
+ expect(redis.ttl(key)).to eq(described_class::DEFAULT_TTL)
+ end
+ end
+
+ it 'can be passed a custom TTL', :freeze_time do
+ described_class.notify(key, 123, ttl: 5.minutes)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.ttl(key)).to eq(5.minutes.to_i)
+ end
+ end
+
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(custom_job_waiter_ttl: false)
+ end
+
+ it 'ignores the custom TTL', :freeze_time do
+ described_class.notify(key, 123, ttl: 5.minutes)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.ttl(key)).to eq(described_class::DEFAULT_TTL)
+ end
end
end
end
@@ -23,6 +45,32 @@ RSpec.describe Gitlab::JobWaiter, :redis, feature_category: :shared do
end
end
+ describe '.delete_key' do
+ let(:key) { described_class.generate_key }
+
+ it 'deletes the key' do
+ described_class.notify(key, '1')
+ described_class.delete_key(key)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.llen(key)).to eq(0)
+ end
+ end
+
+ context 'when key is not a JobWaiter key' do
+ let(:key) { 'foo' }
+
+ it 'does not delete the key' do
+ described_class.notify(key, '1')
+ described_class.delete_key(key)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.llen(key)).to eq(1)
+ end
+ end
+ end
+ end
+
describe '#wait' do
let(:waiter) { described_class.new(2) }
diff --git a/spec/lib/gitlab/manifest_import/metadata_spec.rb b/spec/lib/gitlab/manifest_import/metadata_spec.rb
index c55b407088d..011371782fe 100644
--- a/spec/lib/gitlab/manifest_import/metadata_spec.rb
+++ b/spec/lib/gitlab/manifest_import/metadata_spec.rb
@@ -46,16 +46,6 @@ RSpec.describe Gitlab::ManifestImport::Metadata, :clean_gitlab_redis_shared_stat
expect(status.repositories).to eq(repositories)
end
-
- it 'reads non-hash-tagged keys if hash-tag keys are missing' do
- status = described_class.new(user)
-
- Gitlab::Redis::SharedState.with do |redis|
- redis.set(repositories_key, Gitlab::Json.dump(repositories))
- end
-
- expect(status.repositories).to eq(repositories)
- end
end
describe '#group_id' do
@@ -73,13 +63,5 @@ RSpec.describe Gitlab::ManifestImport::Metadata, :clean_gitlab_redis_shared_stat
expect(status.group_id).to eq(3)
end
-
- it 'reads non-hash-tagged keys if hash-tag keys are missing' do
- status = described_class.new(user)
-
- Gitlab::Redis::SharedState.with { |redis| redis.set(group_id_key, 2) }
-
- expect(status.group_id).to eq(2)
- end
end
end
diff --git a/spec/lib/gitlab/tracking/standard_context_spec.rb b/spec/lib/gitlab/tracking/standard_context_spec.rb
index c44cfdea1cd..4485a30ae66 100644
--- a/spec/lib/gitlab/tracking/standard_context_spec.rb
+++ b/spec/lib/gitlab/tracking/standard_context_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Tracking::StandardContext do
+RSpec.describe Gitlab::Tracking::StandardContext, feature_category: :service_ping do
let(:snowplow_context) { subject.to_context }
describe '#to_context' do
@@ -76,6 +76,7 @@ RSpec.describe Gitlab::Tracking::StandardContext do
it 'holds the correct values', :aggregate_failures do
json_data = snowplow_context.to_json.fetch(:data)
expect(json_data[:user_id]).to eq(user_id)
+ expect(json_data[:is_gitlab_team_member]).to eq(nil)
expect(json_data[:project_id]).to eq(project_id)
expect(json_data[:namespace_id]).to eq(namespace_id)
expect(json_data[:plan]).to eq(plan_name)
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 7be10c2e357..143d0484392 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -556,7 +556,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures, feature_category: :servic
expect(count_data[:issues_using_zoom_quick_actions]).to eq(3)
expect(count_data[:issues_with_embedded_grafana_charts_approx]).to eq(2)
expect(count_data[:incident_issues]).to eq(4)
- expect(count_data[:issues_created_from_alerts]).to eq(3)
+ expect(count_data[:issues_created_from_alerts]).to eq(2)
expect(count_data[:alert_bot_incident_issues]).to eq(4)
expect(count_data[:clusters_enabled]).to eq(6)
expect(count_data[:project_clusters_enabled]).to eq(4)
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 7c793399f19..4e217e3a9f7 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -28,7 +28,6 @@ RSpec.describe Issue, feature_category: :team_planning do
it { is_expected.to have_many(:resource_milestone_events) }
it { is_expected.to have_many(:resource_state_events) }
it { is_expected.to have_and_belong_to_many(:prometheus_alert_events) }
- it { is_expected.to have_and_belong_to_many(:self_managed_prometheus_alert_events) }
it { is_expected.to have_many(:prometheus_alerts) }
it { is_expected.to have_many(:issue_email_participants) }
it { is_expected.to have_one(:email) }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 5312ca0ef70..db5b5c914c9 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -141,7 +141,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
it { is_expected.to have_many(:sourced_pipelines) }
it { is_expected.to have_many(:source_pipelines) }
it { is_expected.to have_many(:prometheus_alert_events) }
- it { is_expected.to have_many(:self_managed_prometheus_alert_events) }
it { is_expected.to have_many(:alert_management_alerts) }
it { is_expected.to have_many(:alert_management_http_integrations) }
it { is_expected.to have_many(:jira_imports) }
diff --git a/spec/policies/organizations/organization_policy_spec.rb b/spec/policies/organizations/organization_policy_spec.rb
index e51362227c9..3fcfa63b1b2 100644
--- a/spec/policies/organizations/organization_policy_spec.rb
+++ b/spec/policies/organizations/organization_policy_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
let_it_be(:organization) { create(:organization) }
+ let_it_be(:current_user) { create :user }
subject(:policy) { described_class.new(current_user, organization) }
@@ -19,6 +20,7 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:admin_organization) }
it { is_expected.to be_allowed(:read_organization) }
+ it { is_expected.to be_allowed(:read_organization_user) }
end
context 'when admin mode is disabled' do
@@ -27,13 +29,19 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
end
end
- context 'when the user is an organization user' do
- let_it_be(:current_user) { create :user }
-
+ context 'when the user is part of the organization' do
before do
create :organization_user, organization: organization, user: current_user
end
+ it { is_expected.to be_allowed(:read_organization_user) }
+ it { is_expected.to be_allowed(:read_organization) }
+ end
+
+ context 'when the user is not part of the organization' do
+ it { is_expected.to be_disallowed(:read_organization_user) }
+ # All organizations are currently public, and hence they are allowed to be read
+ # even if the user is not a part of the organization.
it { is_expected.to be_allowed(:read_organization) }
end
end
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 2f0e64cd4da..b7377a45dc9 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::CommitStatuses, feature_category: :continuous_integration do
+RSpec.describe API::CommitStatuses, :clean_gitlab_redis_cache, feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:commit) { project.repository.commit }
let_it_be(:guest) { create_user(:guest) }
@@ -120,6 +120,7 @@ RSpec.describe API::CommitStatuses, feature_category: :continuous_integration do
it "does not return project commits" do
expect(response).to have_gitlab_http_status(:forbidden)
+ expect(json_response['message']).to eq('403 Forbidden')
end
end
@@ -134,454 +135,471 @@ RSpec.describe API::CommitStatuses, feature_category: :continuous_integration do
end
end
- describe 'POST /projects/:id/statuses/:sha' do
- let(:post_url) { "/projects/#{project.id}/statuses/#{sha}" }
+ [true, false].each do |flag|
+ context "when feature flag ci_commit_statuses_api_exclusive_lock is set to #{flag}" do
+ before do
+ stub_feature_flags(ci_commit_statuses_api_exclusive_lock: flag)
+ end
- context 'developer user' do
- context 'uses only required parameters' do
- %w[pending running success failed canceled].each do |status|
- context "for #{status}" do
- context 'when pipeline for sha does not exists' do
- it 'creates commit status and sets pipeline iid' do
- post api(post_url, developer), params: { state: status }
+ describe 'POST /projects/:id/statuses/:sha' do
+ let(:post_url) { "/projects/#{project.id}/statuses/#{sha}" }
+
+ context 'developer user' do
+ context 'uses only required parameters' do
+ valid_statues = %w[pending running success failed canceled]
+ valid_statues.each do |status|
+ context "for #{status}" do
+ context 'when pipeline for sha does not exists' do
+ it 'creates commit status and sets pipeline iid' do
+ post api(post_url, developer), params: { state: status }
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq(status)
+ expect(json_response['name']).to eq('default')
+ expect(json_response['ref']).not_to be_empty
+ expect(json_response['target_url']).to be_nil
+ expect(json_response['description']).to be_nil
+ expect(json_response['pipeline_id']).not_to be_nil
+
+ if status == 'failed'
+ expect(CommitStatus.find(json_response['id'])).to be_api_failure
+ end
+
+ expect(::Ci::Pipeline.last.iid).not_to be_nil
+ end
+ end
+ end
+ end
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response['sha']).to eq(commit.id)
- expect(json_response['status']).to eq(status)
- expect(json_response['name']).to eq('default')
- expect(json_response['ref']).not_to be_empty
- expect(json_response['target_url']).to be_nil
- expect(json_response['description']).to be_nil
- expect(json_response['pipeline_id']).not_to be_nil
-
- if status == 'failed'
- expect(CommitStatus.find(json_response['id'])).to be_api_failure
+ context 'when pipeline already exists for the specified sha' do
+ let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') }
+ let(:params) { { state: 'pending' } }
+
+ shared_examples_for 'creates a commit status for the existing pipeline with an external stage' do
+ it do
+ expect do
+ post api(post_url, developer), params: params
+ end.not_to change { Ci::Pipeline.count }
+
+ job = pipeline.statuses.find_by_name(json_response['name'])
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(job.ci_stage.name).to eq('external')
+ expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
+ expect(job.ci_stage.pipeline).to eq(pipeline)
+ expect(job.status).to eq('pending')
+ expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
end
+ end
- expect(::Ci::Pipeline.last.iid).not_to be_nil
+ shared_examples_for 'updates the commit status with an external stage' do
+ before do
+ post api(post_url, developer), params: { state: 'pending' }
+ end
+
+ it 'updates the commit status with the external stage' do
+ post api(post_url, developer), params: { state: 'running' }
+ job = pipeline.statuses.find_by_name(json_response['name'])
+
+ expect(job.ci_stage.name).to eq('external')
+ expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
+ expect(job.ci_stage.pipeline).to eq(pipeline)
+ expect(job.status).to eq('running')
+ expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
+ end
end
- end
- end
- end
- context 'when pipeline already exists for the specified sha' do
- let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') }
- let(:params) { { state: 'pending' } }
+ context 'with pipeline for merge request' do
+ let!(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline, source_project: project) }
+ let!(:pipeline) { merge_request.all_pipelines.last }
+ let(:sha) { pipeline.sha }
- shared_examples_for 'creates a commit status for the existing pipeline with an external stage' do
- it do
- expect do
- post api(post_url, developer), params: params
- end.not_to change { Ci::Pipeline.count }
+ it_behaves_like 'creates a commit status for the existing pipeline with an external stage'
+ end
- job = pipeline.statuses.find_by_name(json_response['name'])
+ context 'when an external stage does not exist' do
+ context 'when the commit status does not exist' do
+ it_behaves_like 'creates a commit status for the existing pipeline with an external stage'
+ end
- expect(response).to have_gitlab_http_status(:created)
- expect(job.ci_stage.name).to eq('external')
- expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
- expect(job.ci_stage.pipeline).to eq(pipeline)
- expect(job.status).to eq('pending')
- expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
+ context 'when the commit status exists' do
+ it_behaves_like 'updates the commit status with an external stage'
+ end
+ end
+
+ context 'when an external stage already exists' do
+ let(:stage) { create(:ci_stage, name: 'external', pipeline: pipeline, position: 1_000_000) }
+
+ context 'when the commit status exists' do
+ it_behaves_like 'updates the commit status with an external stage'
+ end
+
+ context 'when the commit status does not exist' do
+ it_behaves_like 'creates a commit status for the existing pipeline with an external stage'
+ end
+ end
+ end
+
+ context 'when the pipeline does not exist' do
+ it 'creates a commit status and a stage' do
+ expect do
+ post api(post_url, developer), params: { state: 'pending' }
+ end.to change { Ci::Pipeline.count }.by(1)
+ job = Ci::Pipeline.last.statuses.find_by_name(json_response['name'])
+
+ expect(job.ci_stage.name).to eq('external')
+ expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
+ expect(job.status).to eq('pending')
+ expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
+ end
end
end
- shared_examples_for 'updates the commit status with an external stage' do
+ context 'when status transitions from pending' do
before do
post api(post_url, developer), params: { state: 'pending' }
end
- it 'updates the commit status with the external stage' do
- post api(post_url, developer), params: { state: 'running' }
- job = pipeline.statuses.find_by_name(json_response['name'])
+ valid_statues = %w[running success failed canceled]
+ valid_statues.each do |status|
+ it "to #{status}" do
+ expect { post api(post_url, developer), params: { state: status } }.not_to change { CommitStatus.count }
- expect(job.ci_stage.name).to eq('external')
- expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
- expect(job.ci_stage.pipeline).to eq(pipeline)
- expect(job.status).to eq('running')
- expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['status']).to eq(status)
+ end
end
end
- context 'with pipeline for merge request' do
- let!(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline, source_project: project) }
- let!(:pipeline) { merge_request.all_pipelines.last }
- let(:sha) { pipeline.sha }
+ context 'with all optional parameters' do
+ context 'when creating a commit status' do
+ subject do
+ post api(post_url, developer), params: {
+ state: 'success',
+ context: 'coverage',
+ ref: 'master',
+ description: 'test',
+ coverage: 80.0,
+ target_url: 'http://gitlab.com/status'
+ }
+ end
- it_behaves_like 'creates a commit status for the existing pipeline with an external stage'
- end
+ it 'creates commit status' do
+ subject
- context 'when an external stage does not exist' do
- context 'when the commit status does not exist' do
- it_behaves_like 'creates a commit status for the existing pipeline with an external stage'
- end
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq('success')
+ expect(json_response['name']).to eq('coverage')
+ expect(json_response['ref']).to eq('master')
+ expect(json_response['coverage']).to eq(80.0)
+ expect(json_response['description']).to eq('test')
+ expect(json_response['target_url']).to eq('http://gitlab.com/status')
+ end
- context 'when the commit status exists' do
- it_behaves_like 'updates the commit status with an external stage'
- end
- end
+ context 'when merge request exists for given branch' do
+ let!(:merge_request) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'develop') }
- context 'when an external stage already exists' do
- let(:stage) { create(:ci_stage, name: 'external', pipeline: pipeline, position: 1_000_000) }
+ it 'sets head pipeline' do
+ subject
- context 'when the commit status exists' do
- it_behaves_like 'updates the commit status with an external stage'
+ expect(response).to have_gitlab_http_status(:created)
+ expect(merge_request.reload.head_pipeline).not_to be_nil
+ end
+ end
end
- context 'when the commit status does not exist' do
- it_behaves_like 'creates a commit status for the existing pipeline with an external stage'
- end
- end
- end
+ context 'when updating a commit status' do
+ let(:parameters) do
+ {
+ state: 'success',
+ name: 'coverage',
+ ref: 'master'
+ }
+ end
- context 'when the pipeline does not exist' do
- it 'creates a commit status and a stage' do
- expect do
- post api(post_url, developer), params: { state: 'pending' }
- end.to change { Ci::Pipeline.count }.by(1)
- job = Ci::Pipeline.last.statuses.find_by_name(json_response['name'])
+ let(:updatable_optional_attributes) do
+ {
+ description: 'new description',
+ coverage: 90.0
+ }
+ end
- expect(job.ci_stage.name).to eq('external')
- expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
- expect(job.status).to eq('pending')
- expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
- end
- end
- end
+ # creating the initial commit status
+ before do
+ post api(post_url, developer), params: {
+ state: 'running',
+ context: 'coverage',
+ ref: 'master',
+ description: 'coverage test',
+ coverage: 10.0,
+ target_url: 'http://gitlab.com/status'
+ }
+ end
- context 'transitions status from pending' do
- before do
- post api(post_url, developer), params: { state: 'pending' }
- end
+ subject(:send_request) do
+ post api(post_url, developer), params: {
+ **parameters,
+ **updatable_optional_attributes
+ }
+ end
- %w[running success failed canceled].each do |status|
- it "to #{status}" do
- expect { post api(post_url, developer), params: { state: status } }.not_to change { CommitStatus.count }
+ it 'updates a commit status' do
+ send_request
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response['status']).to eq(status)
- end
- end
- end
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq('success')
+ expect(json_response['name']).to eq('coverage')
+ expect(json_response['ref']).to eq('master')
+ expect(json_response['coverage']).to eq(90.0)
+ expect(json_response['description']).to eq('new description')
+ expect(json_response['target_url']).to eq('http://gitlab.com/status')
+ end
- context 'with all optional parameters' do
- context 'when creating a commit status' do
- subject do
- post api(post_url, developer), params: {
- state: 'success',
- context: 'coverage',
- ref: 'master',
- description: 'test',
- coverage: 80.0,
- target_url: 'http://gitlab.com/status'
- }
- end
+ it 'does not create a new commit status' do
+ expect { send_request }.not_to change { CommitStatus.count }
+ end
- it 'creates commit status' do
- subject
-
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response['sha']).to eq(commit.id)
- expect(json_response['status']).to eq('success')
- expect(json_response['name']).to eq('coverage')
- expect(json_response['ref']).to eq('master')
- expect(json_response['coverage']).to eq(80.0)
- expect(json_response['description']).to eq('test')
- expect(json_response['target_url']).to eq('http://gitlab.com/status')
- end
+ context 'when the `state` parameter is sent the same' do
+ let(:parameters) do
+ {
+ state: 'running',
+ name: 'coverage',
+ ref: 'master'
+ }
+ end
- context 'when merge request exists for given branch' do
- let!(:merge_request) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'develop') }
+ it 'does not update the commit status' do
+ send_request
- it 'sets head pipeline' do
- subject
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq("Cannot transition status via :run from :running (Reason(s): Status cannot transition via \"run\")")
- expect(response).to have_gitlab_http_status(:created)
- expect(merge_request.reload.head_pipeline).not_to be_nil
+ commit_status = project.commit_statuses.find_by!(name: 'coverage')
+
+ expect(commit_status.description).to eq('coverage test')
+ expect(commit_status.coverage).to eq(10.0)
+ end
+ end
end
- end
- end
- context 'when updating a commit status' do
- let(:parameters) do
- {
- state: 'success',
- name: 'coverage',
- ref: 'master'
- }
- end
+ context 'when a pipeline id is specified' do
+ let!(:first_pipeline) do
+ project.ci_pipelines.build(source: :push, sha: commit.id, ref: 'master', status: 'created').tap do |p|
+ p.ensure_project_iid! # Necessary to avoid cross-database modification error
+ p.save!
+ end
+ end
- let(:updatable_optional_attributes) do
- {
- description: 'new description',
- coverage: 90.0
- }
- end
+ let!(:other_pipeline) do
+ project.ci_pipelines.build(source: :push, sha: commit.id, ref: 'master', status: 'created').tap do |p|
+ p.ensure_project_iid! # Necessary to avoid cross-database modification error
+ p.save!
+ end
+ end
- # creating the initial commit status
- before do
- post api(post_url, developer), params: {
- state: 'running',
- context: 'coverage',
- ref: 'master',
- description: 'coverage test',
- coverage: 10.0,
- target_url: 'http://gitlab.com/status'
- }
- end
+ subject do
+ post api(post_url, developer), params: {
+ pipeline_id: other_pipeline.id,
+ state: 'success',
+ ref: 'master'
+ }
+ end
- subject(:send_request) do
- post api(post_url, developer), params: {
- **parameters,
- **updatable_optional_attributes
- }
- end
+ it 'update the correct pipeline', :sidekiq_might_not_need_inline do
+ subject
- it 'updates a commit status' do
- send_request
-
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response['sha']).to eq(commit.id)
- expect(json_response['status']).to eq('success')
- expect(json_response['name']).to eq('coverage')
- expect(json_response['ref']).to eq('master')
- expect(json_response['coverage']).to eq(90.0)
- expect(json_response['description']).to eq('new description')
- expect(json_response['target_url']).to eq('http://gitlab.com/status')
+ expect(first_pipeline.reload.status).to eq('created')
+ expect(other_pipeline.reload.status).to eq('success')
+ end
+ end
end
- it 'does not create a new commit status' do
- expect { send_request }.not_to change { CommitStatus.count }
- end
+ context 'when retrying a commit status' do
+ subject(:post_request) do
+ post api(post_url, developer),
+ params: { state: 'failed', name: 'test', ref: 'master' }
- context 'when the `state` parameter is sent the same' do
- let(:parameters) do
- {
- state: 'running',
- name: 'coverage',
- ref: 'master'
- }
+ post api(post_url, developer),
+ params: { state: 'success', name: 'test', ref: 'master' }
end
- it 'does not update the commit status' do
- send_request
+ it 'correctly posts a new commit status' do
+ post_request
- expect(response).to have_gitlab_http_status(:bad_request)
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq('success')
+ end
- commit_status = project.commit_statuses.find_by!(name: 'coverage')
+ it 'retries the commit status', :sidekiq_might_not_need_inline do
+ post_request
- expect(commit_status.description).to eq('coverage test')
- expect(commit_status.coverage).to eq(10.0)
+ expect(CommitStatus.count).to eq 2
+ expect(CommitStatus.first).to be_retried
+ expect(CommitStatus.last.pipeline).to be_success
end
end
- end
- context 'when a pipeline id is specified' do
- let!(:first_pipeline) do
- project.ci_pipelines.build(source: :push, sha: commit.id, ref: 'master', status: 'created').tap do |p|
- p.ensure_project_iid! # Necessary to avoid cross-database modification error
- p.save!
+ context 'when status is invalid' do
+ before do
+ post api(post_url, developer), params: { state: 'invalid' }
end
- end
- let!(:other_pipeline) do
- project.ci_pipelines.build(source: :push, sha: commit.id, ref: 'master', status: 'created').tap do |p|
- p.ensure_project_iid! # Necessary to avoid cross-database modification error
- p.save!
+ it 'does not create commit status' do
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq(nil)
end
end
- subject do
- post api(post_url, developer), params: {
- pipeline_id: other_pipeline.id,
- state: 'success',
- ref: 'master'
- }
- end
-
- it 'update the correct pipeline', :sidekiq_might_not_need_inline do
- subject
+ context 'when request without a state made' do
+ before do
+ post api(post_url, developer)
+ end
- expect(first_pipeline.reload.status).to eq('created')
- expect(other_pipeline.reload.status).to eq('success')
+ it 'does not create commit status' do
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq(nil)
+ end
end
- end
- end
-
- context 'when retrying a commit status' do
- subject(:post_request) do
- post api(post_url, developer),
- params: { state: 'failed', name: 'test', ref: 'master' }
-
- post api(post_url, developer),
- params: { state: 'success', name: 'test', ref: 'master' }
- end
-
- it 'correctly posts a new commit status' do
- post_request
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response['sha']).to eq(commit.id)
- expect(json_response['status']).to eq('success')
- end
-
- it 'retries the commit status', :sidekiq_might_not_need_inline do
- post_request
-
- expect(CommitStatus.count).to eq 2
- expect(CommitStatus.first).to be_retried
- expect(CommitStatus.last.pipeline).to be_success
- end
- end
+ context 'when updating a protected ref' do
+ before do
+ create(:protected_branch, project: project, name: 'master')
+ post api(post_url, user), params: { state: 'running', ref: 'master' }
+ end
- context 'when status is invalid' do
- before do
- post api(post_url, developer), params: { state: 'invalid' }
- end
+ context 'with user as developer' do
+ let(:user) { developer }
- it 'does not create commit status' do
- expect(response).to have_gitlab_http_status(:bad_request)
- end
- end
+ it 'does not create commit status' do
+ expect(response).to have_gitlab_http_status(:forbidden)
+ expect(json_response['message']).to eq('403 Forbidden')
+ end
+ end
- context 'when request without a state made' do
- before do
- post api(post_url, developer)
- end
+ context 'with user as maintainer' do
+ let(:user) { create_user(:maintainer) }
- it 'does not create commit status' do
- expect(response).to have_gitlab_http_status(:bad_request)
- end
- end
+ it 'creates commit status' do
+ expect(response).to have_gitlab_http_status(:created)
+ end
+ end
+ end
- context 'when updating a protected ref' do
- before do
- create(:protected_branch, project: project, name: 'master')
- post api(post_url, user), params: { state: 'running', ref: 'master' }
- end
+ context 'when commit SHA is invalid' do
+ let(:sha) { 'invalid_sha' }
- context 'with user as developer' do
- let(:user) { developer }
+ before do
+ post api(post_url, developer), params: { state: 'running' }
+ end
- it 'does not create commit status' do
- expect(response).to have_gitlab_http_status(:forbidden)
+ it 'returns not found error' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('404 Commit Not Found')
+ end
end
- end
- context 'with user as maintainer' do
- let(:user) { create_user(:maintainer) }
+ context 'when target URL is an invalid address' do
+ before do
+ post api(post_url, developer), params: {
+ state: 'pending',
+ target_url: 'invalid url'
+ }
+ end
- it 'creates commit status' do
- expect(response).to have_gitlab_http_status(:created)
+ it 'responds with bad request status and validation errors' do
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']['target_url'])
+ .to include 'is blocked: Only allowed schemes are http, https'
+ end
end
- end
- end
- context 'when commit SHA is invalid' do
- let(:sha) { 'invalid_sha' }
+ context 'when target URL is an unsupported scheme' do
+ before do
+ post api(post_url, developer), params: {
+ state: 'pending',
+ target_url: 'git://example.com'
+ }
+ end
- before do
- post api(post_url, developer), params: { state: 'running' }
- end
+ it 'responds with bad request status and validation errors' do
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']['target_url'])
+ .to include 'is blocked: Only allowed schemes are http, https'
+ end
+ end
- it 'returns not found error' do
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
+ context 'when trying to update a status of a different type' do
+ let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') }
+ let!(:ci_build) { create(:ci_build, pipeline: pipeline, name: 'test-job') }
+ let(:params) { { state: 'pending', name: 'test-job' } }
- context 'when target URL is an invalid address' do
- before do
- post api(post_url, developer), params: {
- state: 'pending',
- target_url: 'invalid url'
- }
- end
+ before do
+ post api(post_url, developer), params: params
+ end
- it 'responds with bad request status and validation errors' do
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']['target_url'])
- .to include 'is blocked: Only allowed schemes are http, https'
- end
- end
+ it 'responds with bad request status and validation errors' do
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']['name'])
+ .to include 'has already been taken'
+ end
+ end
- context 'when target URL is an unsupported scheme' do
- before do
- post api(post_url, developer), params: {
- state: 'pending',
- target_url: 'git://example.com'
- }
- end
+ context 'with partitions', :ci_partitionable do
+ let(:current_partition_id) { ci_testing_partition_id }
- it 'responds with bad request status and validation errors' do
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']['target_url'])
- .to include 'is blocked: Only allowed schemes are http, https'
- end
- end
+ before do
+ allow(Ci::Pipeline)
+ .to receive(:current_partition_value) { current_partition_id }
+ end
- context 'when trying to update a status of a different type' do
- let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') }
- let!(:ci_build) { create(:ci_build, pipeline: pipeline, name: 'test-job') }
- let(:params) { { state: 'pending', name: 'test-job' } }
+ it 'creates records in the current partition' do
+ expect { post api(post_url, developer), params: { state: 'running' } }
+ .to change(CommitStatus, :count).by(1)
+ .and change(Ci::Pipeline, :count).by(1)
- before do
- post api(post_url, developer), params: params
- end
+ status = CommitStatus.find(json_response['id'])
- it 'responds with bad request status and validation errors' do
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']['name'])
- .to include 'has already been taken'
+ expect(status.partition_id).to eq(current_partition_id)
+ expect(status.pipeline.partition_id).to eq(current_partition_id)
+ end
+ end
end
- end
- context 'with partitions', :ci_partitionable do
- let(:current_partition_id) { ci_testing_partition_id }
+ context 'reporter user' do
+ before do
+ post api(post_url, reporter), params: { state: 'running' }
+ end
- before do
- allow(Ci::Pipeline)
- .to receive(:current_partition_value) { current_partition_id }
+ it 'does not create commit status' do
+ expect(response).to have_gitlab_http_status(:forbidden)
+ expect(json_response['message']).to eq('403 Forbidden')
+ end
end
- it 'creates records in the current partition' do
- expect { post api(post_url, developer), params: { state: 'running' } }
- .to change(CommitStatus, :count).by(1)
- .and change(Ci::Pipeline, :count).by(1)
-
- status = CommitStatus.find(json_response['id'])
+ context 'guest user' do
+ before do
+ post api(post_url, guest), params: { state: 'running' }
+ end
- expect(status.partition_id).to eq(current_partition_id)
- expect(status.pipeline.partition_id).to eq(current_partition_id)
+ it 'does not create commit status' do
+ expect(response).to have_gitlab_http_status(:forbidden)
+ expect(json_response['message']).to eq('403 Forbidden')
+ end
end
- end
- end
-
- context 'reporter user' do
- before do
- post api(post_url, reporter), params: { state: 'running' }
- end
-
- it 'does not create commit status' do
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
- context 'guest user' do
- before do
- post api(post_url, guest), params: { state: 'running' }
- end
- it 'does not create commit status' do
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
- context 'unauthorized user' do
- before do
- post api(post_url)
- end
+ context 'unauthorized user' do
+ before do
+ post api(post_url)
+ end
- it 'does not create commit status' do
- expect(response).to have_gitlab_http_status(:unauthorized)
+ it 'does not create commit status' do
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
end
end
end
diff --git a/spec/requests/api/graphql/organizations/organization_query_spec.rb b/spec/requests/api/graphql/organizations/organization_query_spec.rb
index 2d09a535279..d02158382eb 100644
--- a/spec/requests/api/graphql/organizations/organization_query_spec.rb
+++ b/spec/requests/api/graphql/organizations/organization_query_spec.rb
@@ -7,16 +7,14 @@ RSpec.describe 'getting organization information', feature_category: :cell do
let(:query) { graphql_query_for(:organization, { id: organization.to_global_id }, organization_fields) }
let(:current_user) { user }
- let(:groups) { graphql_data_at(:organization, :groups, :edges, :node) }
+ let(:groups) { graphql_data_at(:organization, :groups, :nodes) }
let(:organization_fields) do
<<~FIELDS
id
path
groups {
- edges {
- node {
- id
- }
+ nodes {
+ id
}
}
FIELDS
@@ -52,7 +50,7 @@ RSpec.describe 'getting organization information', feature_category: :cell do
it 'returns the organization as all organizations are public' do
request_organization
- expect(graphql_data['organization']['id']).to eq(organization.to_global_id.to_s)
+ expect(graphql_data_at(:organization, :id)).to eq(organization.to_global_id.to_s)
end
end
@@ -71,8 +69,51 @@ RSpec.describe 'getting organization information', feature_category: :cell do
it 'returns no groups' do
request_organization
- expect(graphql_data['organization']).not_to be_nil
- expect(graphql_data['organization']['groups']['edges']).to be_empty
+ expect(graphql_data_at(:organization)).not_to be_nil
+ expect(graphql_data_at(:organization, :groups, :nodes)).to be_empty
+ end
+ end
+
+ context 'when requesting organization user' do
+ let(:organization_fields) do
+ <<~FIELDS
+ organizationUsers {
+ nodes {
+ badges
+ id
+ user {
+ id
+ }
+ }
+ }
+ FIELDS
+ end
+
+ it 'returns correct organization user fields' do
+ request_organization
+
+ organization_user_node = graphql_data_at(:organization, :organizationUsers, :nodes).first
+ expected_attributes = {
+ "badges" => ["It's you!"],
+ "id" => organization_user.to_global_id.to_s,
+ "user" => { "id" => user.to_global_id.to_s }
+ }
+ expect(organization_user_node).to match(expected_attributes)
+ end
+
+ it 'avoids N+1 queries for all the fields' do
+ base_query_count = ActiveRecord::QueryRecorder.new { run_query }
+
+ organization_user_2 = create(:organization_user, organization: organization)
+ other_group.add_developer(organization_user_2.user)
+
+ expect { run_query }.not_to exceed_query_limit(base_query_count)
+ end
+
+ private
+
+ def run_query
+ run_with_clean_state(query, context: { current_user: current_user })
end
end
@@ -83,11 +124,9 @@ RSpec.describe 'getting organization information', feature_category: :cell do
id
path
groups(search: "#{search}") {
- edges {
- node {
- id
- name
- }
+ nodes {
+ id
+ name
}
}
FIELDS
@@ -106,12 +145,12 @@ RSpec.describe 'getting organization information', feature_category: :cell do
let(:authorized_groups) { [public_group, private_group, other_group] }
where(:field, :direction, :sorted_groups) do
- 'id' | 'asc' | lazy { authorized_groups.sort_by(&:id) }
- 'id' | 'desc' | lazy { authorized_groups.sort_by(&:id).reverse }
- 'name' | 'asc' | lazy { authorized_groups.sort_by(&:name) }
- 'name' | 'desc' | lazy { authorized_groups.sort_by(&:name).reverse }
- 'path' | 'asc' | lazy { authorized_groups.sort_by(&:path) }
- 'path' | 'desc' | lazy { authorized_groups.sort_by(&:path).reverse }
+ 'id' | 'asc' | lazy { authorized_groups.sort_by(&:id) }
+ 'id' | 'desc' | lazy { authorized_groups.sort_by(&:id).reverse }
+ 'name' | 'asc' | lazy { authorized_groups.sort_by(&:name) }
+ 'name' | 'desc' | lazy { authorized_groups.sort_by(&:name).reverse }
+ 'path' | 'asc' | lazy { authorized_groups.sort_by(&:path) }
+ 'path' | 'desc' | lazy { authorized_groups.sort_by(&:path).reverse }
end
with_them do
@@ -121,10 +160,8 @@ RSpec.describe 'getting organization information', feature_category: :cell do
id
path
groups(sort: #{sort}) {
- edges {
- node {
- id
- }
+ nodes {
+ id
}
}
FIELDS
diff --git a/spec/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb
index 8175d8e57b3..1e8397773be 100644
--- a/spec/requests/api/internal/kubernetes_spec.rb
+++ b/spec/requests/api/internal/kubernetes_spec.rb
@@ -496,73 +496,125 @@ RSpec.describe API::Internal::Kubernetes, feature_category: :deployment_manageme
Clusters::Agents::Authorizations::UserAccess::RefreshService.new(agent, config: user_access_config).execute
end
- it 'returns 400 when cookie is invalid' do
- send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: '123', csrf_token: mask_token(new_token) })
+ context 'when the access type is access_token' do
+ let(:personal_access_token) { create(:personal_access_token, user: user, scopes: [Gitlab::Auth::K8S_PROXY_SCOPE]) }
- expect(response).to have_gitlab_http_status(:bad_request)
- end
+ it 'returns 200 when the user has access' do
+ deployment_project.add_member(user, :developer)
- it 'returns 401 when session is not found' do
- access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id('abc')
- send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(new_token) })
+ send_request(params: { agent_id: agent.id, access_type: 'personal_access_token', access_key: personal_access_token.token })
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
+ expect(response).to have_gitlab_http_status(:success)
+ end
- it 'returns 401 when CSRF token does not match' do
- public_id = stub_user_session(user, new_token)
- access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
- send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(new_token) })
+ it 'returns 400 when the feature flag is disabled' do
+ deployment_project.add_member(user, :developer)
+ stub_feature_flags(k8s_proxy_pat: false)
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
+ send_request(params: { agent_id: agent.id, access_type: 'personal_access_token', access_key: personal_access_token.token })
- it 'returns 404 for non-existent agent' do
- token = new_token
- public_id = stub_user_session(user, token)
- access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
- send_request(params: { agent_id: non_existing_record_id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ it 'returns 403 when user has no access' do
+ send_request(params: { agent_id: agent.id, access_type: 'personal_access_token', access_key: personal_access_token.token })
- it 'returns 403 when user has no access' do
- token = new_token
- public_id = stub_user_session(user, token)
- access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
- send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it 'returns 403 when user has incorrect token scope' do
+ personal_access_token.update!(scopes: [Gitlab::Auth::READ_API_SCOPE])
+ deployment_project.add_member(user, :developer)
- it 'returns 200 when user has access' do
- deployment_project.add_member(user, :developer)
- token = new_token
- public_id = stub_user_session(user, token)
- access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
- send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+ send_request(params: { agent_id: agent.id, access_type: 'personal_access_token', access_key: personal_access_token.token })
- expect(response).to have_gitlab_http_status(:success)
- end
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'returns 403 when user has no access to requested agent' do
+ deployment_project.add_member(user, :developer)
+
+ send_request(params: { agent_id: another_agent.id, access_type: 'personal_access_token', access_key: personal_access_token.token })
- it 'returns 401 when user has valid KAS cookie and CSRF token but has no access to requested agent' do
- deployment_project.add_member(user, :developer)
- token = new_token
- public_id = stub_user_session(user, token)
- access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
- send_request(params: { agent_id: another_agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'returns 404 for non-existent agent' do
+ send_request(params: { agent_id: non_existing_record_id, access_type: 'personal_access_token', access_key: personal_access_token.token })
- expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
end
- it 'returns 401 when user id is not found in session' do
- deployment_project.add_member(user, :developer)
- token = new_token
- public_id = stub_user_session_with_no_user_id(user, token)
- access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
- send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+ context 'when the access type is session_cookie' do
+ it 'returns 400 when cookie is invalid' do
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: '123', csrf_token: mask_token(new_token) })
- expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+
+ it 'returns 401 when session is not found' do
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id('abc')
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(new_token) })
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ it 'returns 401 when CSRF token does not match' do
+ public_id = stub_user_session(user, new_token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(new_token) })
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ it 'returns 404 for non-existent agent' do
+ token = new_token
+ public_id = stub_user_session(user, token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: non_existing_record_id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns 403 when user has no access' do
+ token = new_token
+ public_id = stub_user_session(user, token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'returns 200 when user has access' do
+ deployment_project.add_member(user, :developer)
+ token = new_token
+ public_id = stub_user_session(user, token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+
+ it 'returns 401 when user has valid KAS cookie and CSRF token but has no access to requested agent' do
+ deployment_project.add_member(user, :developer)
+ token = new_token
+ public_id = stub_user_session(user, token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: another_agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'returns 401 when user id is not found in session' do
+ deployment_project.add_member(user, :developer)
+ token = new_token
+ public_id = stub_user_session_with_no_user_id(user, token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
end
end
end
diff --git a/spec/requests/groups/email_campaigns_controller_spec.rb b/spec/requests/groups/email_campaigns_controller_spec.rb
deleted file mode 100644
index b6e765eba37..00000000000
--- a/spec/requests/groups/email_campaigns_controller_spec.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Groups::EmailCampaignsController, feature_category: :navigation do
- using RSpec::Parameterized::TableSyntax
-
- describe 'GET #index', :snowplow do
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, group: group) }
- let_it_be(:user) { create(:user) }
-
- let(:track) { 'create' }
- let(:series) { '0' }
- let(:schema) { described_class::EMAIL_CAMPAIGNS_SCHEMA_URL }
- let(:subject_line_text) { Gitlab::Email::Message::InProductMarketing.for(track.to_sym).new(group: group, user: user, series: series.to_i).subject_line }
- let(:data) do
- {
- namespace_id: group.id,
- track: track.to_sym,
- series: series.to_i,
- subject_line: subject_line_text
- }
- end
-
- before do
- sign_in(user)
- group.add_developer(user)
- end
-
- subject do
- get group_email_campaigns_url(group, track: track, series: series)
- response
- end
-
- shared_examples 'track and redirect' do
- it 'redirects' do
- expect(subject).to have_gitlab_http_status(:redirect)
- end
-
- context 'on SaaS', :saas do
- it 'emits a snowplow event', :snowplow do
- subject
-
- expect_snowplow_event(
- category: described_class.name,
- action: 'click',
- context: [{
- schema: described_class::EMAIL_CAMPAIGNS_SCHEMA_URL,
- data: { namespace_id: group.id, series: series.to_i, subject_line: subject_line_text, track: track.to_s }
- }],
- user: user,
- namespace: group
- )
- end
-
- it 'does not save the cta_click' do
- expect(Users::InProductMarketingEmail).not_to receive(:save_cta_click)
-
- subject
- end
- end
-
- context 'when not on.com' do
- it 'saves the cta_click' do
- expect(Users::InProductMarketingEmail).to receive(:save_cta_click)
-
- subject
- end
-
- it 'does not track snowplow events' do
- subject
-
- expect_no_snowplow_event
- end
- end
- end
-
- shared_examples 'no track and 404' do
- it 'returns 404' do
- expect(subject).to have_gitlab_http_status(:not_found)
- end
-
- it 'does not emit a snowplow event', :snowplow do
- subject
-
- expect_no_snowplow_event
- end
- end
-
- describe 'track parameter' do
- context 'when valid' do
- where(track: Namespaces::InProductMarketingEmailsService::TRACKS.keys.without(:experience))
-
- with_them do
- it_behaves_like 'track and redirect'
- end
- end
-
- context 'when invalid' do
- where(track: [nil, 'xxxx'])
-
- with_them do
- it_behaves_like 'no track and 404'
- end
- end
- end
-
- describe 'series parameter' do
- context 'when valid' do
- where(series: (0..Namespaces::InProductMarketingEmailsService::TRACKS[:create][:interval_days].length - 1).to_a)
-
- with_them do
- it_behaves_like 'track and redirect'
- end
- end
-
- context 'when invalid' do
- where(series: [-1, nil, Namespaces::InProductMarketingEmailsService::TRACKS[:create][:interval_days].length])
-
- with_them do
- it_behaves_like 'no track and 404'
- end
- end
- end
- end
-end
diff --git a/spec/serializers/activity_pub/activity_streams_serializer_spec.rb b/spec/serializers/activity_pub/activity_streams_serializer_spec.rb
new file mode 100644
index 00000000000..c74beba7a81
--- /dev/null
+++ b/spec/serializers/activity_pub/activity_streams_serializer_spec.rb
@@ -0,0 +1,157 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ActivityPub::ActivityStreamsSerializer, feature_category: :integrations do
+ let(:implementer_class) do
+ Class.new(described_class) do
+ include WithPagination
+ end
+ end
+
+ let(:entity_class) do
+ Class.new(Grape::Entity) do
+ expose :id do |*|
+ 'https://example.com/unique/url'
+ end
+
+ expose :type do |*|
+ 'Person'
+ end
+
+ expose :name do |*|
+ 'Alice'
+ end
+ end
+ end
+
+ shared_examples_for 'ActivityStreams document' do
+ it 'belongs to the ActivityStreams namespace' do
+ expect(subject['@context']).to eq 'https://www.w3.org/ns/activitystreams'
+ end
+
+ it 'has a unique identifier' do
+ expect(subject).to have_key 'id'
+ end
+
+ it 'has a type' do
+ expect(subject).to have_key 'type'
+ end
+ end
+
+ before do
+ implementer_class.entity entity_class
+ end
+
+ context 'when the serializer is not paginated' do
+ let(:resource) { build_stubbed(:release) }
+ let(:outbox_url) { 'https://example.com/unique/url/outbox' }
+
+ context 'with a valid represented entity' do
+ subject { implementer_class.new.represent(resource, outbox: outbox_url) }
+
+ it_behaves_like 'ActivityStreams document'
+
+ it 'exposes an outbox' do
+ expect(subject['outbox']).to eq 'https://example.com/unique/url/outbox'
+ end
+
+ it 'includes serialized data' do
+ expect(subject['name']).to eq 'Alice'
+ end
+ end
+
+ context 'when the represented entity provides no identifier' do
+ subject { implementer_class.new.represent(resource, outbox: outbox_url) }
+
+ before do
+ allow(entity_class).to receive(:represent).and_return({ type: 'Person' })
+ end
+
+ it 'raises an exception' do
+ expect { subject }.to raise_error(ActivityPub::ActivityStreamsSerializer::MissingIdentifierError)
+ end
+ end
+
+ context 'when the represented entity provides no type' do
+ subject { implementer_class.new.represent(resource, outbox: outbox_url) }
+
+ before do
+ allow(entity_class).to receive(:represent).and_return({ id: 'https://example.com/unique/url' })
+ end
+
+ it 'raises an exception' do
+ expect { subject }.to raise_error(ActivityPub::ActivityStreamsSerializer::MissingTypeError)
+ end
+ end
+
+ context 'when the caller provides no outbox parameter' do
+ subject { implementer_class.new.represent(resource) }
+
+ it 'raises an exception' do
+ expect { subject }.to raise_error(ActivityPub::ActivityStreamsSerializer::MissingOutboxError)
+ end
+ end
+ end
+
+ context 'when the serializer is paginated' do
+ let(:resources) { build_stubbed_list(:release, 3) }
+ let(:request) { ActionDispatch::Request.new(request_data) }
+ let(:response) { ActionDispatch::Response.new }
+ let(:url) { 'https://example.com/resource/url' }
+ let(:decorated) { implementer_class.new.with_pagination(request, response) }
+
+ before do
+ allow(resources).to receive(:page).and_return(resources)
+ allow(resources).to receive(:per).and_return(resources)
+ allow(resources).to receive(:current_page).and_return(2)
+ allow(resources).to receive(:total_pages).and_return(3)
+ allow(resources).to receive(:total_count).and_return(10)
+ allow(decorated.paginator).to receive(:paginate).and_return(resources)
+ end
+
+ context 'when no page parameter is provided' do
+ subject { decorated.represent(resources) }
+
+ let(:request_data) do
+ { "rack.url_scheme" => "https", "HTTP_HOST" => "example.com", "PATH_INFO" => '/resource/url' }
+ end
+
+ it_behaves_like 'ActivityStreams document'
+
+ it 'is an index document for the pagination' do
+ expect(subject['type']).to eq 'OrderedCollection'
+ end
+
+ it 'contains the total amount of items' do
+ expect(subject['totalItems']).to eq 10
+ end
+
+ it 'contains links to first and last page' do
+ expect(subject['first']).to eq "#{url}?page=1"
+ expect(subject['last']).to eq "#{url}?page=3"
+ end
+ end
+
+ context 'when a page parameter is provided' do
+ subject { decorated.represent(resources) }
+
+ let(:request_data) do
+ { 'rack.url_scheme' => 'https', 'HTTP_HOST' => 'example.com', 'PATH_INFO' => '/resource/url',
+ 'QUERY_STRING' => 'page=2&per_page=1' }
+ end
+
+ it_behaves_like 'ActivityStreams document'
+
+ it 'is a page document' do
+ expect(subject['type']).to eq 'OrderedCollectionPage'
+ end
+
+ it 'contains navigation links' do
+ expect(subject['prev']).to be_present
+ expect(subject['next']).to be_present
+ expect(subject['partOf']).to be_present
+ end
+ end
+ end
+end
diff --git a/spec/serializers/activity_pub/project_entity_spec.rb b/spec/serializers/activity_pub/project_entity_spec.rb
new file mode 100644
index 00000000000..f273acace73
--- /dev/null
+++ b/spec/serializers/activity_pub/project_entity_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ActivityPub::ProjectEntity, feature_category: :groups_and_projects do
+ let(:project) { build_stubbed(:project, name: 'Fooify', path: 'fooify') }
+ let(:entity) { described_class.new(project) }
+
+ context 'as json' do
+ subject { entity.as_json }
+
+ it 'has releases page as id' do
+ expect(subject[:id]).to match(%r{/fooify$})
+ end
+
+ it 'is an Application actor' do
+ expect(subject[:type]).to eq 'Application'
+ end
+
+ it 'provides project name' do
+ expect(subject[:name]).to eq project.name
+ end
+
+ it 'provides a description of the project' do
+ expect(subject[:summary]).to eq project.description
+ end
+
+ it 'provides an url for web content' do
+ expect(subject[:url]).to match(%r{/fooify$})
+ end
+ end
+end
diff --git a/spec/serializers/activity_pub/release_entity_spec.rb b/spec/serializers/activity_pub/release_entity_spec.rb
new file mode 100644
index 00000000000..a473fbcc2bd
--- /dev/null
+++ b/spec/serializers/activity_pub/release_entity_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ActivityPub::ReleaseEntity, feature_category: :groups_and_projects do
+ let(:release) { build_stubbed(:release) }
+ let(:entity) { described_class.new(release, url: '/outbox') }
+
+ context 'as json' do
+ subject { entity.as_json }
+
+ it 'has tag as id' do
+ expect(subject[:id]).to match(/##{release.tag}$/)
+ end
+
+ it 'is a Create activity' do
+ expect(subject[:type]).to eq 'Create'
+ end
+
+ it 'is addressed to public' do
+ expect(subject[:to]).to eq 'https://www.w3.org/ns/activitystreams#Public'
+ end
+
+ it 'has an author' do
+ expect(subject[:actor]).to include(:id, :type, :name, :preferredUsername, :url)
+ end
+
+ it 'embeds the release as an Application actor' do
+ expect(subject[:object][:type]).to eq 'Application'
+ end
+
+ it 'provides release name' do
+ expect(subject[:object][:name]).to eq release.name
+ end
+
+ it 'provides release description' do
+ expect(subject[:object][:content]).to eq release.description
+ end
+
+ it 'provides an url for web content' do
+ expect(subject[:object][:url]).to include release.tag
+ end
+
+ it 'provides project data as context' do
+ expect(subject[:object][:context]).to include(:id, :type, :name, :summary, :url)
+ end
+ end
+end
diff --git a/spec/serializers/activity_pub/releases_actor_entity_spec.rb b/spec/serializers/activity_pub/releases_actor_entity_spec.rb
new file mode 100644
index 00000000000..fe388968867
--- /dev/null
+++ b/spec/serializers/activity_pub/releases_actor_entity_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ActivityPub::ReleasesActorEntity, feature_category: :groups_and_projects do
+ let(:project) { build_stubbed(:project, name: 'Fooify', path: 'fooify') }
+ let(:releases) { build_stubbed_list(:release, 3, project: project) }
+
+ let(:entity) { described_class.new(project) }
+
+ context 'as json' do
+ subject { entity.as_json }
+
+ it 'has releases page as id' do
+ expect(subject[:id]).to include "/fooify/-/releases"
+ end
+
+ it 'is an Application actor' do
+ expect(subject[:type]).to eq 'Application'
+ end
+
+ it 'has a recognizable username' do
+ expect(subject[:preferredUsername]).to include 'releases'
+ end
+
+ it 'has a recognizable full name' do
+ expect(subject[:name]).to eq 'Releases - Fooify'
+ end
+
+ it 'provides a description of the project' do
+ expect(subject[:content]).to eq project.description
+ end
+
+ it 'provides project data as context' do
+ expect(subject[:context]).to include(:id, :type, :name, :summary, :url)
+ expect(subject[:context][:id]).to match(%r{/fooify$})
+ end
+ end
+end
diff --git a/spec/serializers/activity_pub/releases_actor_serializer_spec.rb b/spec/serializers/activity_pub/releases_actor_serializer_spec.rb
new file mode 100644
index 00000000000..bc754eabe5c
--- /dev/null
+++ b/spec/serializers/activity_pub/releases_actor_serializer_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ActivityPub::ReleasesActorSerializer, feature_category: :groups_and_projects do
+ let(:project) { build_stubbed(:project, name: 'Fooify', path: 'fooify') }
+ let(:releases) { build_stubbed_list(:release, 3, project: project) }
+
+ context 'when there is a single object provided' do
+ subject { described_class.new.represent(project, outbox: '/outbox') }
+
+ it 'serializes the actor attributes' do
+ expect(subject).to include(:id, :type, :preferredUsername, :name, :content, :context)
+ end
+ end
+end
diff --git a/spec/serializers/activity_pub/releases_outbox_serializer_spec.rb b/spec/serializers/activity_pub/releases_outbox_serializer_spec.rb
new file mode 100644
index 00000000000..606b0130e0f
--- /dev/null
+++ b/spec/serializers/activity_pub/releases_outbox_serializer_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ActivityPub::ReleasesOutboxSerializer, feature_category: :groups_and_projects do
+ let(:decorated) { described_class.new.with_pagination(request, response) }
+
+ let(:project) { build_stubbed(:project, name: 'Fooify', path: 'fooify') }
+ let(:releases) { build_stubbed_list(:release, 3, project: project) }
+
+ before do
+ allow(releases).to receive(:page).and_return(releases)
+ allow(releases).to receive(:per).and_return(releases)
+ allow(releases).to receive(:current_page).and_return(1)
+ allow(releases).to receive(:total_pages).and_return(1)
+ allow(decorated.paginator).to receive(:paginate).and_return(releases)
+ end
+
+ context 'when there is a list of objects provided' do
+ subject { decorated.represent(releases, url: '/outbox') }
+
+ let(:request) { ActionDispatch::Request.new({ 'QUERY_STRING' => 'page=1' }) }
+ let(:response) { ActionDispatch::Response.new }
+
+ it 'is a OrderedCollection document' do
+ expect(subject[:type]).to eq 'OrderedCollectionPage'
+ end
+
+ it 'serializes the releases' do
+ expect(subject[:orderedItems].count).to eq 3
+ expect(subject[:orderedItems][0]).to include(:id, :type, :to, :actor, :object)
+ end
+ end
+end
diff --git a/spec/serializers/activity_pub/user_entity_spec.rb b/spec/serializers/activity_pub/user_entity_spec.rb
new file mode 100644
index 00000000000..d9ab7a11ecf
--- /dev/null
+++ b/spec/serializers/activity_pub/user_entity_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ActivityPub::UserEntity, feature_category: :user_profile do
+ let(:user) { build_stubbed(:user, name: 'Alice', username: 'alice') }
+ let(:entity) { described_class.new(user) }
+
+ context 'as json' do
+ subject { entity.as_json }
+
+ it 'has releases page as id' do
+ expect(subject[:id]).to match(%r{/alice$})
+ end
+
+ it 'is a Person actor' do
+ expect(subject[:type]).to eq 'Person'
+ end
+
+ it 'provides project name' do
+ expect(subject[:name]).to eq 'Alice'
+ end
+
+ it 'provides an url for web content' do
+ expect(subject[:url]).to match(%r{/alice$})
+ end
+ end
+end
diff --git a/spec/services/ci/create_commit_status_service_spec.rb b/spec/services/ci/create_commit_status_service_spec.rb
new file mode 100644
index 00000000000..ec200e24c8f
--- /dev/null
+++ b/spec/services/ci/create_commit_status_service_spec.rb
@@ -0,0 +1,461 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::CreateCommitStatusService, :clean_gitlab_redis_cache, feature_category: :continuous_integration do
+ using RSpec::Parameterized::TableSyntax
+
+ subject(:response) { execute_service(params) }
+
+ let_it_be_with_refind(:project) { create(:project, :repository) }
+ let_it_be(:commit) { project.repository.commit }
+ let_it_be(:guest) { create_user(:guest) }
+ let_it_be(:reporter) { create_user(:reporter) }
+ let_it_be(:developer) { create_user(:developer) }
+
+ let(:user) { developer }
+ let(:sha) { commit.id }
+ let(:params) { { state: 'pending' } }
+ let(:job) { response.payload[:job] }
+
+ %w[pending running success failed canceled].each do |status|
+ context "for #{status}" do
+ let(:params) { { state: status } }
+
+ context 'when pipeline for sha does not exists' do
+ it 'creates commit status and sets pipeline iid' do
+ expect(response).to be_success
+ expect(job.sha).to eq(commit.id)
+ expect(job.status).to eq(status)
+ expect(job.name).to eq('default')
+ expect(job.ref).not_to be_empty
+ expect(job.target_url).to be_nil
+ expect(job.description).to be_nil
+ expect(job.pipeline_id).not_to be_nil
+
+ expect(CommitStatus.find(job.id)).to be_api_failure if status == 'failed'
+
+ expect(::Ci::Pipeline.last.iid).not_to be_nil
+ end
+ end
+ end
+ end
+
+ context 'when status transitions from pending' do
+ before do
+ execute_service(state: 'pending')
+ end
+
+ %w[running success failed canceled].each do |status|
+ context "for #{status}" do
+ let(:params) { { state: status } }
+
+ it "changes to #{status}" do
+ expect { response }
+ .to not_change { ::Ci::Pipeline.count }.from(1)
+ .and not_change { ::Ci::Stage.count }.from(1)
+ .and not_change { ::CommitStatus.count }.from(1)
+
+ expect(response).to be_success
+ expect(job.status).to eq(status)
+ end
+ end
+ end
+
+ context 'for invalid transition' do
+ let(:params) { { state: 'pending' } }
+
+ it 'returns bad request and error message' do
+ expect { response }
+ .to not_change { ::Ci::Pipeline.count }.from(1)
+ .and not_change { ::Ci::Stage.count }.from(1)
+ .and not_change { ::CommitStatus.count }.from(1)
+
+ expect(response).to be_error
+ expect(response.http_status).to eq(:bad_request)
+ expect(response.message).to eq(
+ "Cannot transition status via :enqueue from :pending (Reason(s): Status cannot transition via \"enqueue\")"
+ )
+ end
+ end
+ end
+
+ context 'with all optional parameters' do
+ context 'when creating a commit status' do
+ let(:params) do
+ {
+ sha: sha,
+ state: 'success',
+ context: 'coverage',
+ ref: 'master',
+ description: 'test',
+ coverage: 80.0,
+ target_url: 'http://gitlab.com/status'
+ }
+ end
+
+ it 'creates commit status' do
+ expect { response }
+ .to change { ::Ci::Pipeline.count }.by(1)
+ .and change { ::Ci::Stage.count }.by(1)
+ .and change { ::CommitStatus.count }.by(1)
+
+ expect(response).to be_success
+ expect(job.sha).to eq(commit.id)
+ expect(job.status).to eq('success')
+ expect(job.name).to eq('coverage')
+ expect(job.ref).to eq('master')
+ expect(job.coverage).to eq(80.0)
+ expect(job.description).to eq('test')
+ expect(job.target_url).to eq('http://gitlab.com/status')
+ end
+
+ context 'when merge request exists for given branch' do
+ let!(:merge_request) do
+ create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'develop')
+ end
+
+ it 'sets head pipeline' do
+ expect { response }
+ .to change { ::Ci::Pipeline.count }.by(1)
+ .and change { ::Ci::Stage.count }.by(1)
+ .and change { ::CommitStatus.count }.by(1)
+
+ expect(response).to be_success
+ expect(merge_request.reload.head_pipeline).not_to be_nil
+ end
+ end
+ end
+
+ context 'when updating a commit status' do
+ let(:parameters) do
+ {
+ state: 'success',
+ name: 'coverage',
+ ref: 'master'
+ }
+ end
+
+ let(:updatable_optional_attributes) do
+ {
+ description: 'new description',
+ coverage: 90.0
+ }
+ end
+
+ let(:params) { parameters.merge(updatable_optional_attributes) }
+
+ # creating the initial commit status
+ before do
+ execute_service(
+ sha: sha,
+ state: 'running',
+ context: 'coverage',
+ ref: 'master',
+ description: 'coverage test',
+ coverage: 10.0,
+ target_url: 'http://gitlab.com/status'
+ )
+ end
+
+ it 'updates a commit status' do
+ expect { response }
+ .to not_change { ::Ci::Pipeline.count }.from(1)
+ .and not_change { ::Ci::Stage.count }.from(1)
+ .and not_change { ::CommitStatus.count }.from(1)
+
+ expect(response).to be_success
+ expect(job.sha).to eq(commit.id)
+ expect(job.status).to eq('success')
+ expect(job.name).to eq('coverage')
+ expect(job.ref).to eq('master')
+ expect(job.coverage).to eq(90.0)
+ expect(job.description).to eq('new description')
+ expect(job.target_url).to eq('http://gitlab.com/status')
+ end
+
+ context 'when the `state` parameter is sent the same' do
+ let(:parameters) do
+ {
+ sha: sha,
+ state: 'running',
+ name: 'coverage',
+ ref: 'master'
+ }
+ end
+
+ it 'does not update the commit status' do
+ expect { response }
+ .to not_change { ::Ci::Pipeline.count }.from(1)
+ .and not_change { ::Ci::Stage.count }.from(1)
+ .and not_change { ::CommitStatus.count }.from(1)
+
+ expect(response).to be_error
+ expect(response.http_status).to eq(:bad_request)
+ expect(response.message).to eq(
+ "Cannot transition status via :run from :running (Reason(s): Status cannot transition via \"run\")"
+ )
+
+ commit_status = project.commit_statuses.find_by!(name: 'coverage')
+
+ expect(commit_status.description).to eq('coverage test')
+ expect(commit_status.coverage).to eq(10.0)
+ end
+ end
+ end
+
+ context 'when a pipeline id is specified' do
+ let!(:first_pipeline) do
+ project.ci_pipelines.build(source: :push, sha: commit.id, ref: 'master', status: 'created').tap do |p|
+ p.ensure_project_iid! # Necessary to avoid cross-database modification error
+ p.save!
+ end
+ end
+
+ let!(:other_pipeline) do
+ project.ci_pipelines.build(source: :push, sha: commit.id, ref: 'master', status: 'created').tap do |p|
+ p.ensure_project_iid! # Necessary to avoid cross-database modification error
+ p.save!
+ end
+ end
+
+ let(:params) do
+ {
+ sha: sha,
+ pipeline_id: other_pipeline.id,
+ state: 'success',
+ ref: 'master'
+ }
+ end
+
+ it 'update the correct pipeline', :sidekiq_might_not_need_inline do
+ expect { response }
+ .to not_change { ::Ci::Pipeline.count }.from(2)
+ .and change { ::Ci::Stage.count }.by(1)
+ .and change { ::CommitStatus.count }.by(1)
+
+ expect(first_pipeline.reload.status).to eq('created')
+ expect(other_pipeline.reload.status).to eq('success')
+ end
+ end
+ end
+
+ context 'when retrying a commit status' do
+ subject(:response) do
+ execute_service(state: 'failed', name: 'test', ref: 'master')
+
+ execute_service(state: 'success', name: 'test', ref: 'master')
+ end
+
+ it 'correctly posts a new commit status' do
+ expect { response }
+ .to change { ::Ci::Pipeline.count }.by(1)
+ .and change { ::Ci::Stage.count }.by(1)
+ .and change { ::CommitStatus.count }.by(2)
+
+ expect(response).to be_success
+ expect(job.sha).to eq(commit.id)
+ expect(job.status).to eq('success')
+ end
+
+ it 'retries the commit status', :sidekiq_might_not_need_inline do
+ response
+
+ expect(CommitStatus.count).to eq 2
+ expect(CommitStatus.first).to be_retried
+ expect(CommitStatus.last.pipeline).to be_success
+ end
+ end
+
+ context 'when status is invalid' do
+ let(:params) { { state: 'invalid' } }
+
+ it 'does not create commit status' do
+ expect { response }
+ .to change { ::Ci::Pipeline.count }.by(1)
+ .and change { ::Ci::Stage.count }.by(1)
+ .and not_change { ::CommitStatus.count }.from(0)
+
+ expect(response).to be_error
+ expect(response.http_status).to eq(:bad_request)
+ expect(response.message).to eq('invalid state')
+ end
+ end
+
+ context 'when request without a state made' do
+ let(:params) { {} }
+
+ it 'does not create commit status' do
+ expect { response }
+ .to not_change { ::Ci::Pipeline.count }.from(0)
+ .and not_change { ::Ci::Stage.count }.from(0)
+ .and not_change { ::CommitStatus.count }.from(0)
+
+ expect(response).to be_error
+ expect(response.http_status).to eq(:bad_request)
+ expect(response.message).to eq('State is required')
+ end
+ end
+
+ context 'when updating a protected ref' do
+ let(:params) { { state: 'running', ref: 'master' } }
+
+ before do
+ create(:protected_branch, project: project, name: 'master')
+ end
+
+ context 'with user as developer' do
+ let(:user) { developer }
+
+ it 'does not create commit status' do
+ expect { response }
+ .to change { ::Ci::Pipeline.count }.by(1)
+ .and not_change { ::Ci::Stage.count }.from(0)
+ .and not_change { ::CommitStatus.count }.from(0)
+
+ expect(response).to be_error
+ expect(response.http_status).to eq(:forbidden)
+ expect(response.message).to eq('403 Forbidden')
+ end
+ end
+
+ context 'with user as maintainer' do
+ let(:user) { create_user(:maintainer) }
+
+ it 'creates commit status' do
+ expect { response }
+ .to change { ::Ci::Pipeline.count }.by(1)
+ .and change { ::Ci::Stage.count }.by(1)
+ .and change { ::CommitStatus.count }.by(1)
+
+ expect(response).to be_success
+ end
+ end
+ end
+
+ context 'when commit SHA is invalid' do
+ let(:sha) { 'invalid_sha' }
+ let(:params) { { state: 'running', sha: sha } }
+
+ it 'returns not found error' do
+ expect { response }
+ .to not_change { ::Ci::Pipeline.count }.from(0)
+ .and not_change { ::Ci::Stage.count }.from(0)
+ .and not_change { ::CommitStatus.count }.from(0)
+
+ expect(response).to be_error
+ expect(response.http_status).to eq(:not_found)
+ expect(response.message).to eq('404 Commit Not Found')
+ end
+ end
+
+ context 'when target URL is an invalid address' do
+ let(:params) { { state: 'pending', target_url: 'invalid url' } }
+
+ it 'responds with bad request status and validation errors' do
+ expect { response }
+ .to change { ::Ci::Pipeline.count }.by(1)
+ .and change { ::Ci::Stage.count }.by(1)
+ .and not_change { ::CommitStatus.count }.from(0)
+
+ expect(response).to be_error
+ expect(response.http_status).to eq(:bad_request)
+ expect(response.message[:target_url])
+ .to include 'is blocked: Only allowed schemes are http, https'
+ end
+ end
+
+ context 'when target URL is an unsupported scheme' do
+ let(:params) { { state: 'pending', target_url: 'git://example.com' } }
+
+ it 'responds with bad request status and validation errors' do
+ expect { response }
+ .to change { ::Ci::Pipeline.count }.by(1)
+ .and change { ::Ci::Stage.count }.by(1)
+ .and not_change { ::CommitStatus.count }.from(0)
+
+ expect(response).to be_error
+ expect(response.http_status).to eq(:bad_request)
+ expect(response.message[:target_url])
+ .to include 'is blocked: Only allowed schemes are http, https'
+ end
+ end
+
+ context 'when trying to update a status of a different type' do
+ let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') }
+ let!(:ci_build) { create(:ci_build, pipeline: pipeline, name: 'test-job') }
+ let(:params) { { state: 'pending', name: 'test-job' } }
+
+ before do
+ execute_service(params)
+ end
+
+ it 'responds with bad request status and validation errors' do
+ expect { response }
+ .to not_change { ::Ci::Pipeline.count }.from(1)
+ .and not_change { ::Ci::Stage.count }.from(2)
+ .and not_change { ::CommitStatus.count }.from(1)
+
+ expect(response).to be_error
+ expect(response.http_status).to eq(:bad_request)
+ expect(response.message[:name])
+ .to include 'has already been taken'
+ end
+ end
+
+ context 'with partitions', :ci_partitionable do
+ let(:current_partition_id) { ci_testing_partition_id }
+ let(:params) { { state: 'running' } }
+
+ before do
+ allow(Ci::Pipeline)
+ .to receive(:current_partition_value) { current_partition_id }
+ end
+
+ it 'creates records in the current partition' do
+ expect { response }
+ .to change { ::Ci::Pipeline.count }.by(1)
+ .and change { ::Ci::Stage.count }.by(1)
+ .and change { ::CommitStatus.count }.by(1)
+
+ expect(response).to be_success
+
+ status = CommitStatus.find(job.id)
+
+ expect(status.partition_id).to eq(current_partition_id)
+ expect(status.pipeline.partition_id).to eq(current_partition_id)
+ end
+ end
+
+ context 'for race condition' do
+ let(:licenses_snyk_params) { { state: 'running', name: 'licenses', description: 'testing' } }
+ let(:security_snyk_params) { { state: 'running', name: 'security', description: 'testing' } }
+ let(:snyk_params_list) { [licenses_snyk_params, security_snyk_params] }
+
+ it 'creates one pipeline and two jobs (one for licenses, one for security)' do
+ expect do
+ snyk_params_list.map do |snyk_params|
+ Thread.new do
+ response = execute_service(snyk_params)
+ expect(response).to be_success
+ end
+ end.each(&:join)
+ end
+ .to change { ::Ci::Pipeline.count }.by(1)
+ .and change { ::Ci::Stage.count }.by(1)
+ .and change { ::CommitStatus.count }.by(2)
+ end
+ end
+
+ def create_user(access_level_trait)
+ user = create(:user)
+ create(:project_member, access_level_trait, user: user, project: project)
+ user
+ end
+
+ def execute_service(params = self.params)
+ described_class
+ .new(project, user, params)
+ .execute(optional_commit_status_params: params.slice(*%i[target_url description coverage]))
+ end
+end
diff --git a/spec/services/concerns/services/return_service_responses_spec.rb b/spec/services/concerns/services/return_service_responses_spec.rb
new file mode 100644
index 00000000000..3589b952e87
--- /dev/null
+++ b/spec/services/concerns/services/return_service_responses_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Services::ReturnServiceResponses, feature_category: :rate_limiting do
+ subject(:object) { Class.new { include Services::ReturnServiceResponses }.new }
+
+ let(:message) { 'a delivering message' }
+ let(:payload) { 'string payload' }
+
+ describe '#success' do
+ it 'returns a ServiceResponse instance' do
+ response = object.success(payload)
+ expect(response).to be_an(ServiceResponse)
+ expect(response).to be_success
+ expect(response.message).to be_nil
+ expect(response.payload).to eq(payload)
+ expect(response.http_status).to eq(:ok)
+ end
+ end
+
+ describe '#error' do
+ it 'returns a ServiceResponse instance' do
+ response = object.error(message, :not_found, pass_back: payload)
+ expect(response).to be_an(ServiceResponse)
+ expect(response).to be_error
+ expect(response.message).to eq(message)
+ expect(response.payload).to eq(payload)
+ expect(response.http_status).to eq(:not_found)
+ end
+ end
+end
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 1c5dde96b62..6ac1a27745f 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -8573,7 +8573,6 @@
- './spec/requests/groups/crm/contacts_controller_spec.rb'
- './spec/requests/groups/crm/organizations_controller_spec.rb'
- './spec/requests/groups/deploy_tokens_controller_spec.rb'
-- './spec/requests/groups/email_campaigns_controller_spec.rb'
- './spec/requests/groups/harbor/artifacts_controller_spec.rb'
- './spec/requests/groups/harbor/repositories_controller_spec.rb'
- './spec/requests/groups/harbor/tags_controller_spec.rb'
diff --git a/spec/support/shared_examples/config/metrics/every_metric_definition_shared_examples.rb b/spec/support/shared_examples/config/metrics/every_metric_definition_shared_examples.rb
index e61c884cd2b..14d0ac81250 100644
--- a/spec/support/shared_examples/config/metrics/every_metric_definition_shared_examples.rb
+++ b/spec/support/shared_examples/config/metrics/every_metric_definition_shared_examples.rb
@@ -121,8 +121,7 @@ RSpec.shared_examples 'every metric definition' do
let(:ignored_classes) do
[
Gitlab::Usage::Metrics::Instrumentations::IssuesWithAlertManagementAlertsMetric,
- Gitlab::Usage::Metrics::Instrumentations::IssuesWithPrometheusAlertEvents,
- Gitlab::Usage::Metrics::Instrumentations::IssuesWithSelfManagedPrometheusAlertEvents
+ Gitlab::Usage::Metrics::Instrumentations::IssuesWithPrometheusAlertEvents
].freeze
end
diff --git a/spec/support/shared_examples/lib/gitlab/bitbucket_server_import/object_import_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/bitbucket_server_import/object_import_shared_examples.rb
index ec2ae0b8a73..4eae8632467 100644
--- a/spec/support/shared_examples/lib/gitlab/bitbucket_server_import/object_import_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/bitbucket_server_import/object_import_shared_examples.rb
@@ -7,7 +7,7 @@ RSpec.shared_examples Gitlab::BitbucketServerImport::ObjectImporter do
let(:job) { { 'args' => [1, {}, 'key'], 'jid' => 'jid' } }
it 'notifies the waiter' do
- expect(Gitlab::JobWaiter).to receive(:notify).with('key', 'jid')
+ expect(Gitlab::JobWaiter).to receive(:notify).with('key', 'jid', ttl: Gitlab::Import::JOB_WAITER_TTL)
described_class.sidekiq_retries_exhausted_block.call(job, StandardError.new)
end
@@ -23,7 +23,7 @@ RSpec.shared_examples Gitlab::BitbucketServerImport::ObjectImporter do
specify do
allow_next(worker.importer_class).to receive(:execute)
- expect(Gitlab::JobWaiter).to receive(:notify).with(waiter_key, anything)
+ expect(Gitlab::JobWaiter).to receive(:notify).with(waiter_key, anything, ttl: Gitlab::Import::JOB_WAITER_TTL)
worker.perform(project_id, {}, waiter_key)
end
diff --git a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
index 3b7bbfc8a7b..27e1077b138 100644
--- a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
+++ b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
@@ -273,7 +273,8 @@ RSpec.describe Gitlab::GithubImport::ObjectImporter, :aggregate_failures, featur
.to receive(:notify)
.with(
job['args'].last,
- job['jid']
+ job['jid'],
+ ttl: Gitlab::Import::JOB_WAITER_TTL
)
sidekiq_retries_exhausted
diff --git a/spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb b/spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb
index 6475be0243c..c76ce6b555f 100644
--- a/spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb
+++ b/spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb
@@ -110,7 +110,7 @@ RSpec.describe Gitlab::GithubImport::ReschedulingMethods, feature_category: :imp
expect(Gitlab::JobWaiter)
.to receive(:notify)
- .with('123', 'abc123')
+ .with('123', 'abc123', ttl: Gitlab::Import::JOB_WAITER_TTL)
worker.notify_waiter('123')
end
diff --git a/spec/workers/concerns/gitlab/notify_upon_death_spec.rb b/spec/workers/concerns/gitlab/import/notify_upon_death_spec.rb
index 36faf3ee296..1f760e8542b 100644
--- a/spec/workers/concerns/gitlab/notify_upon_death_spec.rb
+++ b/spec/workers/concerns/gitlab/import/notify_upon_death_spec.rb
@@ -2,11 +2,11 @@
require 'spec_helper'
-RSpec.describe Gitlab::NotifyUponDeath, feature_category: :shared do
+RSpec.describe Gitlab::Import::NotifyUponDeath, feature_category: :importers do
let(:worker_class) do
Class.new do
include Sidekiq::Worker
- include Gitlab::NotifyUponDeath
+ include Gitlab::Import::NotifyUponDeath
end
end
@@ -16,13 +16,13 @@ RSpec.describe Gitlab::NotifyUponDeath, feature_category: :shared do
expect(Gitlab::JobWaiter)
.to receive(:notify)
- .with('123abc', '123')
+ .with('123abc', '123', ttl: Gitlab::Import::JOB_WAITER_TTL)
worker_class.sidekiq_retries_exhausted_block.call(job)
end
it 'does not notify the JobWaiter when only 2 arguments are given' do
- job = { 'args' => [12, {}], 'jid' => '123' }
+ job = { 'args' => [12, '123abc'], 'jid' => '123' }
expect(Gitlab::JobWaiter)
.not_to receive(:notify)
@@ -31,7 +31,7 @@ RSpec.describe Gitlab::NotifyUponDeath, feature_category: :shared do
end
it 'does not notify the JobWaiter when only 1 argument is given' do
- job = { 'args' => [12], 'jid' => '123' }
+ job = { 'args' => ['123abc'], 'jid' => '123' }
expect(Gitlab::JobWaiter)
.not_to receive(:notify)
diff --git a/spec/workers/gitlab/bitbucket_server_import/import_pull_request_worker_spec.rb b/spec/workers/gitlab/bitbucket_server_import/import_pull_request_worker_spec.rb
index dd3235f846c..376078532cd 100644
--- a/spec/workers/gitlab/bitbucket_server_import/import_pull_request_worker_spec.rb
+++ b/spec/workers/gitlab/bitbucket_server_import/import_pull_request_worker_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe Gitlab::BitbucketServerImport::ImportPullRequestWorker, feature_c
end
it 'notifies job waiter' do
- expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid')
+ expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid', ttl: Gitlab::Import::JOB_WAITER_TTL)
worker.perform(project.id, {}, job_waiter_key)
end
@@ -44,7 +44,7 @@ RSpec.describe Gitlab::BitbucketServerImport::ImportPullRequestWorker, feature_c
context 'when project does not exists' do
it 'does not call importer and notifies job waiter' do
expect(importer_class).not_to receive(:new)
- expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid')
+ expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid', ttl: Gitlab::Import::JOB_WAITER_TTL)
worker.perform(-1, {}, job_waiter_key)
end
@@ -55,7 +55,7 @@ RSpec.describe Gitlab::BitbucketServerImport::ImportPullRequestWorker, feature_c
project = create(:project, :import_canceled)
expect(importer_class).not_to receive(:new)
- expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid')
+ expect(Gitlab::JobWaiter).to receive(:notify).with(job_waiter_key, 'jid', ttl: Gitlab::Import::JOB_WAITER_TTL)
worker.perform(project.id, {}, job_waiter_key)
end
diff --git a/spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb b/spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb
index 2e89263bcf3..dc715c3026b 100644
--- a/spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb
+++ b/spec/workers/gitlab/github_gists_import/import_gist_worker_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe Gitlab::GithubGistsImport::ImportGistWorker, feature_category: :i
.to receive(:info)
.with(log_attributes.merge('message' => 'start importer'))
expect(importer).to receive(:execute).and_return(importer_result)
- expect(Gitlab::JobWaiter).to receive(:notify).with('some_key', subject.jid)
+ expect(Gitlab::JobWaiter).to receive(:notify).with('some_key', subject.jid, ttl: Gitlab::Import::JOB_WAITER_TTL)
expect(Gitlab::GithubImport::Logger)
.to receive(:info)
.with(log_attributes.merge('message' => 'importer finished'))
@@ -114,7 +114,9 @@ RSpec.describe Gitlab::GithubGistsImport::ImportGistWorker, feature_category: :i
expect(Gitlab::GithubImport::Logger)
.to receive(:error)
.with(log_attributes.merge('message' => 'importer failed', 'error.message' => 'error_message'))
- expect(Gitlab::JobWaiter).to receive(:notify).with('some_key', subject.jid)
+ expect(Gitlab::JobWaiter)
+ .to receive(:notify)
+ .with('some_key', subject.jid, ttl: Gitlab::Import::JOB_WAITER_TTL)
subject.perform(user.id, gist_hash, 'some_key')
@@ -189,7 +191,7 @@ RSpec.describe Gitlab::GithubGistsImport::ImportGistWorker, feature_category: :i
it 'notifies the JobWaiter' do
expect(Gitlab::JobWaiter)
.to receive(:notify)
- .with(job['args'].last, job['jid'])
+ .with(job['args'].last, job['jid'], ttl: Gitlab::Import::JOB_WAITER_TTL)
sidekiq_retries_exhausted
end
diff --git a/spec/workers/gitlab/github_import/advance_stage_worker_spec.rb b/spec/workers/gitlab/github_import/advance_stage_worker_spec.rb
index 121f30ea9d5..2faa28a365e 100644
--- a/spec/workers/gitlab/github_import/advance_stage_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/advance_stage_worker_spec.rb
@@ -35,6 +35,17 @@ RSpec.describe Gitlab::GithubImport::AdvanceStageWorker, :clean_gitlab_redis_sha
worker.perform(project.id, { '123' => 2 }, :finish)
end
+
+ context 'when import state is nil' do
+ let(:import_state) { nil }
+
+ it 'clears the JobWaiter cache and does not perform any work' do
+ expect(Gitlab::JobWaiter).to receive(:delete_key).with('123')
+ expect(worker).not_to receive(:wait_for_jobs)
+
+ worker.perform(project.id, { '123' => 2 }, :finish)
+ end
+ end
end
context 'when there are no remaining jobs' do
diff --git a/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb b/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb
index 5209395923f..6dfab44b228 100644
--- a/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb
+++ b/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb
@@ -12,9 +12,9 @@ RSpec.describe Gitlab::JiraImport::ImportIssueWorker, feature_category: :importe
describe 'modules' do
it { expect(described_class).to include_module(ApplicationWorker) }
- it { expect(described_class).to include_module(Gitlab::NotifyUponDeath) }
it { expect(described_class).to include_module(Gitlab::JiraImport::QueueOptions) }
it { expect(described_class).to include_module(Gitlab::Import::DatabaseHelpers) }
+ it { expect(described_class).to include_module(Gitlab::Import::NotifyUponDeath) }
end
subject { described_class.new }