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>2021-04-23 15:09:52 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-04-23 15:09:52 +0300
commitecf1ffc19875a94c9de675b0559adc408b202515 (patch)
tree92f76d0b7cbd9161eb4dff35ca4753f45f4bc6d2 /spec
parent65f7976d0cd11d91a4c0945b2c63a1aa2f888b07 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/admin/cohorts_controller_spec.rb2
-rw-r--r--spec/controllers/admin/users_controller_spec.rb11
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb55
-rw-r--r--spec/features/admin/admin_users_spec.rb21
-rw-r--r--spec/fixtures/api/schemas/entities/downloadable_artifact.json19
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/environment.json3
-rw-r--r--spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event.yml25
-rw-r--r--spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event_ee.yml23
-rw-r--r--spec/frontend/admin/users/tabs_spec.js37
-rw-r--r--spec/frontend/experimentation/utils_spec.js20
-rw-r--r--spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js26
-rw-r--r--spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js31
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js26
-rw-r--r--spec/frontend/pipelines/graph/linked_pipelines_column_spec.js20
-rw-r--r--spec/frontend/security_configuration/configuration_table_spec.js16
-rw-r--r--spec/frontend/security_configuration/manage_sast_spec.js136
-rw-r--r--spec/frontend/security_configuration/upgrade_spec.js2
-rw-r--r--spec/frontend/vue_shared/security_reports/components/apollo_mocks.js12
-rw-r--r--spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js145
-rw-r--r--spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb1
-rw-r--r--spec/graphql/mutations/alert_management/create_alert_issue_spec.rb1
-rw-r--r--spec/graphql/mutations/alert_management/http_integration/create_spec.rb1
-rw-r--r--spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb1
-rw-r--r--spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb1
-rw-r--r--spec/graphql/mutations/alert_management/http_integration/update_spec.rb1
-rw-r--r--spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb1
-rw-r--r--spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb1
-rw-r--r--spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb1
-rw-r--r--spec/graphql/mutations/alert_management/update_alert_status_spec.rb1
-rw-r--r--spec/graphql/types/blob_viewer_type_spec.rb12
-rw-r--r--spec/graphql/types/repository/blob_type_spec.rb23
-rw-r--r--spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb44
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb13
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb18
-rw-r--r--spec/lib/gitlab/hook_data/key_builder_spec.rb73
-rw-r--r--spec/lib/sidebars/concerns/positionable_list_spec.rb59
-rw-r--r--spec/lib/sidebars/menu_spec.rb91
-rw-r--r--spec/lib/sidebars/panel_spec.rb70
-rw-r--r--spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb (renamed from spec/lib/sidebars/projects/menus/learn_gitlab/menu_spec.rb)2
-rw-r--r--spec/lib/sidebars/projects/menus/project_overview/menu_items/releases_spec.rb38
-rw-r--r--spec/lib/sidebars/projects/menus/project_overview/menu_spec.rb18
-rw-r--r--spec/lib/sidebars/projects/menus/project_overview_menu_spec.rb38
-rw-r--r--spec/lib/sidebars/projects/menus/repository_menu_spec.rb (renamed from spec/lib/sidebars/projects/menus/repository/menu_spec.rb)4
-rw-r--r--spec/lib/sidebars/projects/panel_spec.rb2
-rw-r--r--spec/models/pages/lookup_path_spec.rb18
-rw-r--r--spec/models/todo_spec.rb12
-rw-r--r--spec/models/user_spec.rb4
-rw-r--r--spec/presenters/blob_presenter_spec.rb61
-rw-r--r--spec/requests/api/environments_spec.rb52
-rw-r--r--spec/serializers/ci/downloadable_artifact_entity_spec.rb28
-rw-r--r--spec/serializers/ci/downloadable_artifact_serializer_spec.rb17
-rw-r--r--spec/services/system_hooks_service_spec.rb159
-rw-r--r--spec/services/todo_service_spec.rb4
-rw-r--r--spec/services/users/update_todo_count_cache_service_spec.rb32
-rw-r--r--spec/views/shared/nav/_sidebar.html.haml_spec.rb2
55 files changed, 989 insertions, 545 deletions
diff --git a/spec/controllers/admin/cohorts_controller_spec.rb b/spec/controllers/admin/cohorts_controller_spec.rb
index 77a9c8eb223..ba5406f25ab 100644
--- a/spec/controllers/admin/cohorts_controller_spec.rb
+++ b/spec/controllers/admin/cohorts_controller_spec.rb
@@ -12,6 +12,6 @@ RSpec.describe Admin::CohortsController do
it 'redirects to Overview->Users' do
get :index
- expect(response).to redirect_to(admin_users_path(tab: 'cohorts'))
+ expect(response).to redirect_to(cohorts_admin_users_path)
end
end
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 6d64ef86452..1afd20f5021 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -30,11 +30,6 @@ RSpec.describe Admin::UsersController do
expect(assigns(:users).first.association(:authorized_projects)).to be_loaded
end
- it_behaves_like 'tracking unique visits', :index do
- let(:target_id) { 'i_analytics_cohorts' }
- let(:request_params) { { tab: 'cohorts' } }
- end
-
context 'pagination' do
context 'when number of users is over the pagination limit' do
before do
@@ -59,6 +54,12 @@ RSpec.describe Admin::UsersController do
end
end
+ describe 'GET #cohorts' do
+ it_behaves_like 'tracking unique visits', :cohorts do
+ let(:target_id) { 'i_analytics_cohorts' }
+ end
+ end
+
describe 'GET :id' do
it 'finds a user case-insensitively' do
user = create(:user, username: 'CaseSensitive')
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 753223c5a4f..6236a47cde1 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -1247,4 +1247,59 @@ RSpec.describe Projects::PipelinesController do
format: :json
end
end
+
+ describe 'GET downloadable_artifacts.json' do
+ context 'when pipeline is empty' do
+ let(:pipeline) { create(:ci_empty_pipeline) }
+
+ it 'returns status not_found' do
+ get_downloadable_artifacts_json
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when pipeline exists' do
+ context 'when pipeline does not have any downloadable artifacts' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ it 'returns an empty array' do
+ get_downloadable_artifacts_json
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['artifacts']).to be_empty
+ end
+ end
+
+ context 'when pipeline has downloadable artifacts' do
+ let(:pipeline) { create(:ci_pipeline, :with_codequality_reports, project: project) }
+
+ before do
+ create(:ci_build, name: 'rspec', pipeline: pipeline).tap do |build|
+ create(:ci_job_artifact, :junit, job: build)
+ end
+ end
+
+ it 'returns an array of artifacts' do
+ get_downloadable_artifacts_json
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['artifacts']).to be_kind_of(Array)
+ expect(json_response['artifacts'].size).to eq(2)
+ end
+ end
+ end
+
+ private
+
+ def get_downloadable_artifacts_json
+ get :downloadable_artifacts,
+ params: {
+ namespace_id: project.namespace,
+ project_id: project,
+ id: pipeline.id
+ },
+ format: :json
+ end
+ end
end
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index e4b783a81fa..6d5944002a1 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -10,36 +10,39 @@ RSpec.describe "Admin::Users" do
gitlab_enable_admin_mode_sign_in(current_user)
end
- describe 'Tabs', :js do
+ describe 'Tabs' do
let(:tabs_selector) { '.js-users-tabs' }
let(:active_tab_selector) { '.nav-link.active' }
- it 'does not add the tab param when the Users tab is selected' do
- visit admin_users_path
+ it 'links to the Users tab' do
+ visit cohorts_admin_users_path
within tabs_selector do
click_link 'Users'
+
+ expect(page).to have_selector active_tab_selector, text: 'Users'
end
expect(page).to have_current_path(admin_users_path)
end
- it 'adds the ?tab=cohorts param when the Cohorts tab is selected' do
+ it 'links to the Cohorts tab' do
visit admin_users_path
within tabs_selector do
click_link 'Cohorts'
+
+ expect(page).to have_selector active_tab_selector, text: 'Cohorts'
end
- expect(page).to have_current_path(admin_users_path(tab: 'cohorts'))
+ expect(page).to have_current_path(cohorts_admin_users_path)
+ expect(page).to have_selector active_tab_selector, text: 'Cohorts'
end
- it 'shows the cohorts tab when the tab param is set' do
+ it 'redirects legacy route' do
visit admin_users_path(tab: 'cohorts')
- within tabs_selector do
- expect(page).to have_selector active_tab_selector, text: 'Cohorts'
- end
+ expect(page).to have_current_path(cohorts_admin_users_path)
end
end
diff --git a/spec/fixtures/api/schemas/entities/downloadable_artifact.json b/spec/fixtures/api/schemas/entities/downloadable_artifact.json
new file mode 100644
index 00000000000..01f355f8b55
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/downloadable_artifact.json
@@ -0,0 +1,19 @@
+{
+ "type": "object",
+ "required": ["artifacts"],
+ "properties": {
+ "artifacts": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "expire_at": { "type": ["string", "null"], "format": "date-time" },
+ "expired": { "type": "boolean" },
+ "path": { "type": "string" }
+ }
+ }
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/environment.json b/spec/fixtures/api/schemas/public_api/v4/environment.json
index 57352017f03..dfb6e78cbda 100644
--- a/spec/fixtures/api/schemas/public_api/v4/environment.json
+++ b/spec/fixtures/api/schemas/public_api/v4/environment.json
@@ -18,7 +18,8 @@
{ "$ref": "deployment.json" }
]
},
- "state": { "type": "string" }
+ "state": { "type": "string" },
+ "enable_advanced_logs_querying": { "type": "boolean" }
},
"additionalProperties": false
}
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
new file mode 100644
index 00000000000..704e94a04d8
--- /dev/null
+++ b/spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event.yml
@@ -0,0 +1,25 @@
+---
+description:
+category: Groups::EmailCampaignsController
+action: click
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+#- project
+#- user
+#- namespace
+product_section:
+product_stage:
+product_group:
+product_category:
+milestone: "13.11"
+introduced_by_url:
+distributions:
+- ce
+- ee
+tiers:
+- 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
new file mode 100644
index 00000000000..b20bb9702d2
--- /dev/null
+++ b/spec/fixtures/lib/generators/gitlab/snowplow_event_definition_generator/sample_event_ee.yml
@@ -0,0 +1,23 @@
+---
+description:
+category: Groups::EmailCampaignsController
+action: click
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+#- project
+#- user
+#- namespace
+product_section:
+product_stage:
+product_group:
+product_category:
+milestone: "13.11"
+introduced_by_url:
+distributions:
+- ee
+tiers:
+#- premium
+- ultimate
diff --git a/spec/frontend/admin/users/tabs_spec.js b/spec/frontend/admin/users/tabs_spec.js
deleted file mode 100644
index 39ba8618486..00000000000
--- a/spec/frontend/admin/users/tabs_spec.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import initTabs from '~/admin/users/tabs';
-import Api from '~/api';
-
-jest.mock('~/api.js');
-jest.mock('~/lib/utils/common_utils');
-
-describe('tabs', () => {
- beforeEach(() => {
- setFixtures(`
- <div>
- <div class="js-users-tab-item">
- <a href="#users" data-testid='users-tab'>Users</a>
- </div>
- <div class="js-users-tab-item">
- <a href="#cohorts" data-testid='cohorts-tab'>Cohorts</a>
- </div>
- </div`);
-
- initTabs();
- });
-
- afterEach(() => {});
-
- describe('tracking', () => {
- it('tracks event when cohorts tab is clicked', () => {
- document.querySelector('[data-testid="cohorts-tab"]').click();
-
- expect(Api.trackRedisHllUserEvent).toHaveBeenCalledWith('i_analytics_cohorts');
- });
-
- it('does not track an event when users tab is clicked', () => {
- document.querySelector('[data-testid="users-tab"]').click();
-
- expect(Api.trackRedisHllUserEvent).not.toHaveBeenCalled();
- });
- });
-});
diff --git a/spec/frontend/experimentation/utils_spec.js b/spec/frontend/experimentation/utils_spec.js
index ec09bbab349..2ba8c65a252 100644
--- a/spec/frontend/experimentation/utils_spec.js
+++ b/spec/frontend/experimentation/utils_spec.js
@@ -1,5 +1,9 @@
import { assignGitlabExperiment } from 'helpers/experimentation_helper';
-import { DEFAULT_VARIANT, CANDIDATE_VARIANT } from '~/experimentation/constants';
+import {
+ DEFAULT_VARIANT,
+ CANDIDATE_VARIANT,
+ TRACKING_CONTEXT_SCHEMA,
+} from '~/experimentation/constants';
import * as experimentUtils from '~/experimentation/utils';
describe('experiment Utilities', () => {
@@ -19,6 +23,20 @@ describe('experiment Utilities', () => {
});
});
+ describe('getExperimentContexts', () => {
+ describe.each`
+ gon | input | output
+ ${[TEST_KEY, '_data_']} | ${[TEST_KEY]} | ${[{ schema: TRACKING_CONTEXT_SCHEMA, data: { variant: '_data_' } }]}
+ ${[]} | ${[TEST_KEY]} | ${[]}
+ `('with input=$input and gon=$gon', ({ gon, input, output }) => {
+ assignGitlabExperiment(...gon);
+
+ it(`returns ${output}`, () => {
+ expect(experimentUtils.getExperimentContexts(...input)).toEqual(output);
+ });
+ });
+ });
+
describe('isExperimentVariant', () => {
describe.each`
gon | input | output
diff --git a/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
index af5434f7068..1bb9c429a2f 100644
--- a/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
+++ b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
@@ -146,6 +146,21 @@ describe('MembersFilteredSearchBar', () => {
},
]);
});
+
+ it('parses and passes search param with multiple words to `FilteredSearchBar` component as `initialFilterValue` prop', () => {
+ window.location.search = '?search=foo+bar+baz';
+
+ createComponent();
+
+ expect(findFilteredSearchBar().props('initialFilterValue')).toEqual([
+ {
+ type: 'filtered-search-term',
+ value: {
+ data: 'foo bar baz',
+ },
+ },
+ ]);
+ });
});
describe('when filter bar is submitted', () => {
@@ -175,6 +190,17 @@ describe('MembersFilteredSearchBar', () => {
expect(window.location.href).toBe('https://localhost/?two_factor=enabled&search=foobar');
});
+ it('adds search query param with multiple words', () => {
+ createComponent();
+
+ findFilteredSearchBar().vm.$emit('onFilter', [
+ { type: 'two_factor', value: { data: 'enabled', operator: '=' } },
+ { type: 'filtered-search-term', value: { data: 'foo bar baz' } },
+ ]);
+
+ expect(window.location.href).toBe('https://localhost/?two_factor=enabled&search=foo+bar+baz');
+ });
+
it('adds sort query param', () => {
window.location.search = '?sort=name_asc';
diff --git a/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js b/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
index fa937100982..0400547b917 100644
--- a/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
+++ b/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
@@ -120,4 +120,35 @@ describe('Pipeline editor branch switcher', () => {
]);
});
});
+
+ describe('when switching branches', () => {
+ beforeEach(async () => {
+ mockAvailableBranchQuery.mockResolvedValue(mockProjectBranches);
+ createComponentWithApollo();
+ await waitForPromises();
+ });
+
+ it('emits the refetchContent event when selecting a different branch', async () => {
+ const branch = findDropdownItems().at(1);
+
+ expect(branch.text()).not.toBe(mockDefaultBranch);
+ expect(wrapper.emitted('refetchContent')).toBeUndefined();
+
+ await branch.vm.$emit('click');
+
+ expect(wrapper.emitted('refetchContent')).toBeDefined();
+ expect(wrapper.emitted('refetchContent')).toHaveLength(1);
+ });
+
+ it('does not emit the refetchContent event when selecting the current branch', async () => {
+ const branch = findDropdownItems().at(0);
+
+ expect(branch.text()).toBe(mockDefaultBranch);
+ expect(wrapper.emitted('refetchContent')).toBeUndefined();
+
+ await branch.vm.$emit('click');
+
+ expect(wrapper.emitted('refetchContent')).toBeUndefined();
+ });
+ });
});
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index d8e3436479c..adb8c8836bc 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -92,6 +92,11 @@ describe('Pipeline editor app component', () => {
const options = {
localVue,
+ data() {
+ return {
+ currentBranch: mockDefaultBranch,
+ };
+ },
mocks: {},
apolloProvider: mockApollo,
};
@@ -116,9 +121,6 @@ describe('Pipeline editor app component', () => {
});
afterEach(() => {
- mockBlobContentData.mockReset();
- mockCiConfigData.mockReset();
-
wrapper.destroy();
});
@@ -337,4 +339,22 @@ describe('Pipeline editor app component', () => {
});
});
});
+
+ describe('when refetching content', () => {
+ beforeEach(async () => {
+ await createComponentWithApollo();
+
+ jest
+ .spyOn(wrapper.vm.$apollo.queries.initialCiFileContent, 'refetch')
+ .mockImplementation(jest.fn());
+ });
+
+ it('refetches blob content', async () => {
+ expect(wrapper.vm.$apollo.queries.initialCiFileContent.refetch).toHaveBeenCalledTimes(0);
+
+ await wrapper.vm.refetchContent();
+
+ expect(wrapper.vm.$apollo.queries.initialCiFileContent.refetch).toHaveBeenCalledTimes(1);
+ });
+ });
});
diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
index 8a37c3cae4e..24cc6e76098 100644
--- a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
+++ b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
@@ -121,6 +121,26 @@ describe('Linked Pipelines Column', () => {
});
});
+ describe('when graph does not use needs', () => {
+ beforeEach(() => {
+ const nonNeedsResponse = { ...wrappedPipelineReturn };
+ nonNeedsResponse.data.project.pipeline.usesNeeds = false;
+
+ createComponentWithApollo({
+ props: {
+ viewType: LAYER_VIEW,
+ },
+ getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse),
+ mountFn: mount,
+ });
+ });
+
+ it('shows the stage view, even when the main graph view type is layers', async () => {
+ await clickExpandButtonAndAwaitTimers();
+ expect(findPipelineGraph().props('viewType')).toBe(STAGE_VIEW);
+ });
+ });
+
describe('downstream', () => {
describe('when successful', () => {
beforeEach(() => {
diff --git a/spec/frontend/security_configuration/configuration_table_spec.js b/spec/frontend/security_configuration/configuration_table_spec.js
index a1789052c92..fbd72265c4b 100644
--- a/spec/frontend/security_configuration/configuration_table_spec.js
+++ b/spec/frontend/security_configuration/configuration_table_spec.js
@@ -1,7 +1,7 @@
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ConfigurationTable from '~/security_configuration/components/configuration_table.vue';
-import { scanners, UPGRADE_CTA } from '~/security_configuration/components/scanners_constants';
+import { scanners, UPGRADE_CTA } from '~/security_configuration/components/constants';
import {
REPORT_TYPE_SAST,
@@ -12,7 +12,13 @@ describe('Configuration Table Component', () => {
let wrapper;
const createComponent = () => {
- wrapper = extendedWrapper(mount(ConfigurationTable, {}));
+ wrapper = extendedWrapper(
+ mount(ConfigurationTable, {
+ provide: {
+ projectPath: 'testProjectPath',
+ },
+ }),
+ );
};
const findHelpLinks = () => wrapper.findAll('[data-testid="help-link"]');
@@ -30,8 +36,10 @@ describe('Configuration Table Component', () => {
expect(wrapper.text()).toContain(scanner.name);
expect(wrapper.text()).toContain(scanner.description);
if (scanner.type === REPORT_TYPE_SAST) {
- expect(wrapper.findByTestId(scanner.type).text()).toBe('Configure via merge request');
- } else if (scanner.type !== REPORT_TYPE_SECRET_DETECTION) {
+ expect(wrapper.findByTestId(scanner.type).text()).toBe('Configure via Merge Request');
+ } else if (scanner.type === REPORT_TYPE_SECRET_DETECTION) {
+ expect(wrapper.findByTestId(scanner.type).exists()).toBe(false);
+ } else {
expect(wrapper.findByTestId(scanner.type).text()).toMatchInterpolatedText(UPGRADE_CTA);
}
});
diff --git a/spec/frontend/security_configuration/manage_sast_spec.js b/spec/frontend/security_configuration/manage_sast_spec.js
deleted file mode 100644
index 15a57210246..00000000000
--- a/spec/frontend/security_configuration/manage_sast_spec.js
+++ /dev/null
@@ -1,136 +0,0 @@
-import { GlButton } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { redirectTo } from '~/lib/utils/url_utility';
-import ManageSast from '~/security_configuration/components/manage_sast.vue';
-import configureSastMutation from '~/security_configuration/graphql/configure_sast.mutation.graphql';
-
-jest.mock('~/lib/utils/url_utility', () => ({
- redirectTo: jest.fn(),
-}));
-
-Vue.use(VueApollo);
-
-describe('Manage Sast Component', () => {
- let wrapper;
-
- const findButton = () => wrapper.findComponent(GlButton);
- const successHandler = async () => {
- return {
- data: {
- configureSast: {
- successPath: 'testSuccessPath',
- errors: [],
- __typename: 'ConfigureSastPayload',
- },
- },
- };
- };
-
- const noSuccessPathHandler = async () => {
- return {
- data: {
- configureSast: {
- successPath: '',
- errors: [],
- __typename: 'ConfigureSastPayload',
- },
- },
- };
- };
-
- const errorHandler = async () => {
- return {
- data: {
- configureSast: {
- successPath: 'testSuccessPath',
- errors: ['foo'],
- __typename: 'ConfigureSastPayload',
- },
- },
- };
- };
-
- const pendingHandler = () => new Promise(() => {});
-
- function createMockApolloProvider(handler) {
- const requestHandlers = [[configureSastMutation, handler]];
-
- return createMockApollo(requestHandlers);
- }
-
- function createComponent(options = {}) {
- const { mockApollo } = options;
- wrapper = extendedWrapper(
- mount(ManageSast, {
- apolloProvider: mockApollo,
- }),
- );
- }
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('should render Button with correct text', () => {
- createComponent();
- expect(findButton().text()).toContain('Configure via merge request');
- });
-
- describe('given a successful response', () => {
- beforeEach(() => {
- const mockApollo = createMockApolloProvider(successHandler);
- createComponent({ mockApollo });
- });
-
- it('should call redirect helper with correct value', async () => {
- await wrapper.trigger('click');
- await waitForPromises();
- expect(redirectTo).toHaveBeenCalledTimes(1);
- expect(redirectTo).toHaveBeenCalledWith('testSuccessPath');
- // This is done for UX reasons. If the loading prop is set to false
- // on success, then there's a period where the button is clickable
- // again. Instead, we want the button to display a loading indicator
- // for the remainder of the lifetime of the page (i.e., until the
- // browser can start painting the new page it's been redirected to).
- expect(findButton().props().loading).toBe(true);
- });
- });
-
- describe('given a pending response', () => {
- beforeEach(() => {
- const mockApollo = createMockApolloProvider(pendingHandler);
- createComponent({ mockApollo });
- });
-
- it('renders spinner correctly', async () => {
- expect(findButton().props('loading')).toBe(false);
- await wrapper.trigger('click');
- await waitForPromises();
- expect(findButton().props('loading')).toBe(true);
- });
- });
-
- describe.each`
- handler | message
- ${noSuccessPathHandler} | ${'SAST merge request creation mutation failed'}
- ${errorHandler} | ${'foo'}
- `('given an error response', ({ handler, message }) => {
- beforeEach(() => {
- const mockApollo = createMockApolloProvider(handler);
- createComponent({ mockApollo });
- });
-
- it('should catch and emit error', async () => {
- await wrapper.trigger('click');
- await waitForPromises();
- expect(wrapper.emitted('error')).toEqual([[message]]);
- expect(findButton().props('loading')).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/security_configuration/upgrade_spec.js b/spec/frontend/security_configuration/upgrade_spec.js
index 1f0cc795fc5..20bb38aa469 100644
--- a/spec/frontend/security_configuration/upgrade_spec.js
+++ b/spec/frontend/security_configuration/upgrade_spec.js
@@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
-import { UPGRADE_CTA } from '~/security_configuration/components/scanners_constants';
+import { UPGRADE_CTA } from '~/security_configuration/components/constants';
import Upgrade from '~/security_configuration/components/upgrade.vue';
const TEST_URL = 'http://www.example.test';
diff --git a/spec/frontend/vue_shared/security_reports/components/apollo_mocks.js b/spec/frontend/vue_shared/security_reports/components/apollo_mocks.js
new file mode 100644
index 00000000000..066f9a57bc6
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/components/apollo_mocks.js
@@ -0,0 +1,12 @@
+export const buildConfigureSecurityFeatureMockFactory = (mutationType) => ({
+ successPath = 'testSuccessPath',
+ errors = [],
+} = {}) => ({
+ data: {
+ [mutationType]: {
+ successPath,
+ errors,
+ __typename: `${mutationType}Payload`,
+ },
+ },
+});
diff --git a/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js b/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
new file mode 100644
index 00000000000..29e0da7f6ed
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
@@ -0,0 +1,145 @@
+import { GlButton } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import configureDependencyScanningMutation from 'ee/security_configuration/graphql/configure_dependency_scanning.mutation.graphql';
+import configureSecretDetectionMutation from 'ee/security_configuration/graphql/configure_secret_detection.mutation.graphql';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { redirectTo } from '~/lib/utils/url_utility';
+import ManageViaMr from '~/vue_shared/security_configuration/components/manage_via_mr.vue';
+import {
+ REPORT_TYPE_DEPENDENCY_SCANNING,
+ REPORT_TYPE_SECRET_DETECTION,
+} from '~/vue_shared/security_reports/constants';
+import { buildConfigureSecurityFeatureMockFactory } from './apollo_mocks';
+
+jest.mock('~/lib/utils/url_utility');
+
+Vue.use(VueApollo);
+
+describe('ManageViaMr component', () => {
+ let wrapper;
+
+ const findButton = () => wrapper.findComponent(GlButton);
+ describe.each`
+ featureName | featureType | mutation | mutationId
+ ${'Dependency Scanning'} | ${REPORT_TYPE_DEPENDENCY_SCANNING} | ${configureDependencyScanningMutation} | ${'configureDependencyScanning'}
+ ${'Secret Detection'} | ${REPORT_TYPE_SECRET_DETECTION} | ${configureSecretDetectionMutation} | ${'configureSecretDetection'}
+ `('$featureType', ({ featureName, mutation, featureType, mutationId }) => {
+ const buildConfigureSecurityFeatureMock = buildConfigureSecurityFeatureMockFactory(mutationId);
+ const successHandler = async () => buildConfigureSecurityFeatureMock();
+ const noSuccessPathHandler = async () =>
+ buildConfigureSecurityFeatureMock({
+ successPath: '',
+ });
+ const errorHandler = async () =>
+ buildConfigureSecurityFeatureMock({
+ errors: ['foo'],
+ });
+ const pendingHandler = () => new Promise(() => {});
+
+ function createMockApolloProvider(handler) {
+ const requestHandlers = [[mutation, handler]];
+
+ return createMockApollo(requestHandlers);
+ }
+
+ function createComponent({ mockApollo, isFeatureConfigured = false } = {}) {
+ wrapper = extendedWrapper(
+ mount(ManageViaMr, {
+ apolloProvider: mockApollo,
+ provide: {
+ projectPath: 'testProjectPath',
+ },
+ propsData: {
+ feature: {
+ name: featureName,
+ type: featureType,
+ configured: isFeatureConfigured,
+ },
+ },
+ }),
+ );
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when feature is configured', () => {
+ beforeEach(() => {
+ const mockApollo = createMockApolloProvider(successHandler);
+ createComponent({ mockApollo, isFeatureConfigured: true });
+ });
+
+ it('it does not render a button', () => {
+ expect(findButton().exists()).toBe(false);
+ });
+ });
+
+ describe('when feature is not configured', () => {
+ beforeEach(() => {
+ const mockApollo = createMockApolloProvider(successHandler);
+ createComponent({ mockApollo, isFeatureConfigured: false });
+ });
+
+ it('it does render a button', () => {
+ expect(findButton().exists()).toBe(true);
+ });
+ });
+
+ describe('given a pending response', () => {
+ beforeEach(() => {
+ const mockApollo = createMockApolloProvider(pendingHandler);
+ createComponent({ mockApollo });
+ });
+
+ it('renders spinner correctly', async () => {
+ const button = findButton();
+ expect(button.props('loading')).toBe(false);
+ await button.trigger('click');
+ expect(button.props('loading')).toBe(true);
+ });
+ });
+
+ describe('given a successful response', () => {
+ beforeEach(() => {
+ const mockApollo = createMockApolloProvider(successHandler);
+ createComponent({ mockApollo });
+ });
+
+ it('should call redirect helper with correct value', async () => {
+ await wrapper.trigger('click');
+ await waitForPromises();
+ expect(redirectTo).toHaveBeenCalledTimes(1);
+ expect(redirectTo).toHaveBeenCalledWith('testSuccessPath');
+ // This is done for UX reasons. If the loading prop is set to false
+ // on success, then there's a period where the button is clickable
+ // again. Instead, we want the button to display a loading indicator
+ // for the remainder of the lifetime of the page (i.e., until the
+ // browser can start painting the new page it's been redirected to).
+ expect(findButton().props().loading).toBe(true);
+ });
+ });
+
+ describe.each`
+ handler | message
+ ${noSuccessPathHandler} | ${`${featureName} merge request creation mutation failed`}
+ ${errorHandler} | ${'foo'}
+ `('given an error response', ({ handler, message }) => {
+ beforeEach(() => {
+ const mockApollo = createMockApolloProvider(handler);
+ createComponent({ mockApollo });
+ });
+
+ it('should catch and emit error', async () => {
+ await wrapper.trigger('click');
+ await waitForPromises();
+ expect(wrapper.emitted('error')).toEqual([[message]]);
+ expect(findButton().props('loading')).toBe(false);
+ });
+ });
+ });
+});
diff --git a/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb b/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb
index a10c3725ba2..8ec99070c91 100644
--- a/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb
+++ b/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe Mutations::AlertManagement::Alerts::Todo::Create do
let_it_be(:alert) { create(:alert_management_alert) }
let_it_be(:project) { alert.project }
+
let(:current_user) { project.owner }
let(:args) { { project_path: project.full_path, iid: alert.iid } }
diff --git a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
index 47ee338ad34..4758ac526a5 100644
--- a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
+++ b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Mutations::AlertManagement::CreateAlertIssue do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered') }
+
let(:args) { { project_path: project.full_path, iid: alert.iid } }
specify { expect(described_class).to require_graphql_authorizations(:update_alert_management_alert) }
diff --git a/spec/graphql/mutations/alert_management/http_integration/create_spec.rb b/spec/graphql/mutations/alert_management/http_integration/create_spec.rb
index 9aa89761aaf..be6c627e376 100644
--- a/spec/graphql/mutations/alert_management/http_integration/create_spec.rb
+++ b/spec/graphql/mutations/alert_management/http_integration/create_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Mutations::AlertManagement::HttpIntegration::Create do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
+
let(:args) { { project_path: project.full_path, active: true, name: 'HTTP Integration' } }
specify { expect(described_class).to require_graphql_authorizations(:admin_operations) }
diff --git a/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb b/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb
index acd7070d0d3..1aeeba1009e 100644
--- a/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb
+++ b/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Mutations::AlertManagement::HttpIntegration::Destroy do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
+
let(:integration) { create(:alert_management_http_integration, project: project) }
let(:args) { { id: GitlabSchema.id_from_object(integration) } }
diff --git a/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb b/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
index 96974c2aa6f..5a2af9e0be8 100644
--- a/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
+++ b/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Mutations::AlertManagement::HttpIntegration::ResetToken do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:integration) { create(:alert_management_http_integration, project: project) }
+
let(:args) { { id: GitlabSchema.id_from_object(integration) } }
specify { expect(described_class).to require_graphql_authorizations(:admin_operations) }
diff --git a/spec/graphql/mutations/alert_management/http_integration/update_spec.rb b/spec/graphql/mutations/alert_management/http_integration/update_spec.rb
index d6318e3161d..805996bf9e9 100644
--- a/spec/graphql/mutations/alert_management/http_integration/update_spec.rb
+++ b/spec/graphql/mutations/alert_management/http_integration/update_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Mutations::AlertManagement::HttpIntegration::Update do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:integration) { create(:alert_management_http_integration, project: project) }
+
let(:args) { { id: GitlabSchema.id_from_object(integration), active: false, name: 'New Name' } }
specify { expect(described_class).to require_graphql_authorizations(:admin_operations) }
diff --git a/spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb b/spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb
index 02a5e2e74e2..7ab0f43d674 100644
--- a/spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb
+++ b/spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Mutations::AlertManagement::PrometheusIntegration::Create do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
+
let(:args) { { project_path: project.full_path, active: true, api_url: 'http://prometheus.com/' } }
specify { expect(described_class).to require_graphql_authorizations(:admin_project) }
diff --git a/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb b/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
index ddf23909035..c9e1bf4162c 100644
--- a/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
+++ b/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Mutations::AlertManagement::PrometheusIntegration::ResetToken do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:integration) { create(:prometheus_service, project: project) }
+
let(:args) { { id: GitlabSchema.id_from_object(integration) } }
specify { expect(described_class).to require_graphql_authorizations(:admin_project) }
diff --git a/spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb b/spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb
index eab4474d827..19e0d53b75f 100644
--- a/spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb
+++ b/spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Mutations::AlertManagement::PrometheusIntegration::Update do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:integration) { create(:prometheus_service, project: project) }
+
let(:args) { { id: GitlabSchema.id_from_object(integration), active: false, api_url: 'http://new-url.com' } }
specify { expect(described_class).to require_graphql_authorizations(:admin_project) }
diff --git a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
index 8465393f299..2c2518e046a 100644
--- a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
+++ b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Mutations::AlertManagement::UpdateAlertStatus do
let_it_be(:current_user) { create(:user) }
let_it_be(:alert) { create(:alert_management_alert, :triggered) }
let_it_be(:project) { alert.project }
+
let(:new_status) { Types::AlertManagement::StatusEnum.values['ACKNOWLEDGED'].value }
let(:args) { { status: new_status, project_path: project.full_path, iid: alert.iid } }
diff --git a/spec/graphql/types/blob_viewer_type_spec.rb b/spec/graphql/types/blob_viewer_type_spec.rb
new file mode 100644
index 00000000000..1c020c63535
--- /dev/null
+++ b/spec/graphql/types/blob_viewer_type_spec.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['BlobViewer'] do
+ it 'has the correct fields' do
+ expected_fields = [:type, :load_async, :too_large, :collapsed,
+ :render_error, :file_type, :loading_partial_name]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/repository/blob_type_spec.rb b/spec/graphql/types/repository/blob_type_spec.rb
index f8647e4e964..8accee90fa3 100644
--- a/spec/graphql/types/repository/blob_type_spec.rb
+++ b/spec/graphql/types/repository/blob_type_spec.rb
@@ -5,5 +5,26 @@ require 'spec_helper'
RSpec.describe Types::Repository::BlobType do
specify { expect(described_class.graphql_name).to eq('RepositoryBlob') }
- specify { expect(described_class).to have_graphql_fields(:id, :oid, :name, :path, :web_path, :lfs_oid, :mode) }
+ specify do
+ expect(described_class).to have_graphql_fields(
+ :id,
+ :oid,
+ :name,
+ :path,
+ :web_path,
+ :lfs_oid,
+ :mode,
+ :size,
+ :raw_size,
+ :raw_blob,
+ :raw_text_blob,
+ :file_type,
+ :edit_blob_path,
+ :stored_externally,
+ :raw_path,
+ :replace_path,
+ :simple_viewer,
+ :rich_viewer
+ )
+ 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
new file mode 100644
index 00000000000..4f7c44e5d4e
--- /dev/null
+++ b/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'generator_helper'
+
+RSpec.describe Gitlab::SnowplowEventDefinitionGenerator do
+ let(:ce_temp_dir) { Dir.mktmpdir }
+ let(:ee_temp_dir) { Dir.mktmpdir }
+ let(:generator_options) { { 'category' => 'Groups::EmailCampaignsController', 'action' => 'click' } }
+
+ before do
+ stub_const("#{described_class}::CE_DIR", ce_temp_dir)
+ stub_const("#{described_class}::EE_DIR", ee_temp_dir)
+ end
+
+ after do
+ FileUtils.rm_rf([ce_temp_dir, ee_temp_dir])
+ end
+
+ describe 'Creating event definition file' do
+ before do
+ stub_const('Gitlab::VERSION', '13.11.0-pre')
+ end
+
+ let(:sample_event_dir) { 'lib/generators/gitlab/snowplow_event_definition_generator' }
+
+ it 'creates CE event definition file using the template' do
+ sample_event = ::Gitlab::Config::Loader::Yaml.new(fixture_file(File.join(sample_event_dir, 'sample_event.yml'))).load_raw!
+
+ described_class.new([], generator_options).invoke_all
+
+ event_definition_path = File.join(ce_temp_dir, 'groups__email_campaigns_controller_click.yml')
+ expect(::Gitlab::Config::Loader::Yaml.new(File.read(event_definition_path)).load_raw!).to eq(sample_event)
+ end
+
+ it 'creates EE event definition file using the template' do
+ sample_event = ::Gitlab::Config::Loader::Yaml.new(fixture_file(File.join(sample_event_dir, 'sample_event_ee.yml'))).load_raw!
+
+ described_class.new([], generator_options.merge('ee' => true)).invoke_all
+
+ event_definition_path = File.join(ee_temp_dir, 'groups__email_campaigns_controller_click.yml')
+ expect(::Gitlab::Config::Loader::Yaml.new(File.read(event_definition_path)).load_raw!).to eq(sample_event)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
index 264076859cb..889c9d239a5 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
@@ -259,15 +259,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
def expected_extra_queries
extra_jobs = 2
- non_handled_sql_queries = 3
-
- # 1. Ci::Build Load () SELECT "ci_builds".* FROM "ci_builds"
- # WHERE "ci_builds"."type" = 'Ci::Build'
- # AND "ci_builds"."commit_id" IS NULL
- # AND ("ci_builds"."retried" = FALSE OR "ci_builds"."retried" IS NULL)
- # AND (stage_idx < 1)
- # 2. Ci::InstanceVariable Load => `Ci::InstanceVariable#cached_data` => already cached with `fetch_memory_cache`
- # 3. Ci::Variable Load => `Project#ci_variables_for` => already cached with `Gitlab::SafeRequestStore`
+ non_handled_sql_queries = 2
+
+ # 1. Ci::InstanceVariable Load => `Ci::InstanceVariable#cached_data` => already cached with `fetch_memory_cache`
+ # 2. Ci::Variable Load => `Project#ci_variables_for` => already cached with `Gitlab::SafeRequestStore`
extra_jobs * non_handled_sql_queries
end
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
index 39a37234822..9243adbaa06 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
@@ -68,6 +68,12 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
subject
end
+ it 'reports interval' do
+ expect(described_class.metrics[:gauge_interval]).to receive(:set).with(labels, job_record.batched_migration.interval)
+
+ subject
+ end
+
it 'reports updated tuples (currently based on batch_size)' do
expect(described_class.metrics[:counter_updated_tuples]).to receive(:increment).with(labels, job_record.batch_size)
@@ -89,18 +95,22 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
subject
end
- it 'reports time efficiency' do
+ it 'reports job duration' do
freeze_time do
expect(Time).to receive(:current).and_return(Time.zone.now - 5.seconds).ordered
expect(Time).to receive(:current).and_return(Time.zone.now).ordered
- ratio = 5 / job_record.batched_migration.interval.to_f
-
- expect(described_class.metrics[:histogram_time_efficiency]).to receive(:observe).with(labels, ratio)
+ expect(described_class.metrics[:gauge_job_duration]).to receive(:set).with(labels, 5.seconds)
subject
end
end
+
+ it 'reports the total tuple count for the migration' do
+ expect(described_class.metrics[:gauge_total_tuple_count]).to receive(:set).with(labels, job_record.batched_migration.total_tuple_count)
+
+ subject
+ end
end
context 'when the migration job does not raise an error' do
diff --git a/spec/lib/gitlab/hook_data/key_builder_spec.rb b/spec/lib/gitlab/hook_data/key_builder_spec.rb
new file mode 100644
index 00000000000..86f33df115f
--- /dev/null
+++ b/spec/lib/gitlab/hook_data/key_builder_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::HookData::KeyBuilder do
+ let_it_be(:personal_key) { create(:personal_key) }
+ let_it_be(:other_key) { create(:key) }
+
+ describe '#build' do
+ let(:data) { described_class.new(key).build(event) }
+ let(:event_name) { data[:event_name] }
+ let(:common_attributes) do
+ [
+ :event_name, :created_at, :updated_at, :key, :id
+ ]
+ end
+
+ shared_examples_for 'includes the required attributes' do
+ it 'includes the required attributes' do
+ expect(data.keys).to contain_exactly(*attributes)
+
+ expect(data[:key]).to eq(key.key)
+ expect(data[:id]).to eq(key.id)
+ expect(data[:created_at]).to eq(key.created_at.xmlschema)
+ expect(data[:updated_at]).to eq(key.updated_at.xmlschema)
+ end
+ end
+
+ context 'for keys that belong to a user' do
+ let(:key) { personal_key }
+ let(:attributes) { common_attributes.append(:username) }
+
+ context 'data' do
+ context 'on create' do
+ let(:event) { :create }
+
+ it { expect(event_name).to eq('key_create') }
+ it { expect(data[:username]).to eq(key.user.username) }
+ it_behaves_like 'includes the required attributes'
+ end
+
+ context 'on destroy' do
+ let(:event) { :destroy }
+
+ it { expect(event_name).to eq('key_destroy') }
+ it { expect(data[:username]).to eq(key.user.username) }
+ it_behaves_like 'includes the required attributes'
+ end
+ end
+ end
+
+ context 'for keys that do not belong to a user' do
+ let(:key) { other_key }
+ let(:attributes) { common_attributes }
+
+ context 'data' do
+ context 'on create' do
+ let(:event) { :create }
+
+ it { expect(event_name).to eq('key_create') }
+ it_behaves_like 'includes the required attributes'
+ end
+
+ context 'on destroy' do
+ let(:event) { :destroy }
+
+ it { expect(event_name).to eq('key_destroy') }
+ it_behaves_like 'includes the required attributes'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/sidebars/concerns/positionable_list_spec.rb b/spec/lib/sidebars/concerns/positionable_list_spec.rb
deleted file mode 100644
index ac933faba13..00000000000
--- a/spec/lib/sidebars/concerns/positionable_list_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Sidebars::Concerns::PositionableList do
- subject do
- Class.new do
- include Sidebars::Concerns::PositionableList
- end.new
- end
-
- describe '#add_element' do
- it 'adds the element to the last position of the list' do
- list = [1, 2]
-
- subject.add_element(list, 3)
-
- expect(list).to eq([1, 2, 3])
- end
- end
-
- describe '#insert_element_before' do
- let(:user) { build(:user) }
- let(:list) { [1, user] }
-
- it 'adds element before the specific element class' do
- subject.insert_element_before(list, User, 2)
-
- expect(list).to eq [1, 2, user]
- end
-
- context 'when reference element does not exist' do
- it 'adds the element to the top of the list' do
- subject.insert_element_before(list, Project, 2)
-
- expect(list).to eq [2, 1, user]
- end
- end
- end
-
- describe '#insert_element_after' do
- let(:user) { build(:user) }
- let(:list) { [1, user] }
-
- it 'adds element after the specific element class' do
- subject.insert_element_after(list, Integer, 2)
-
- expect(list).to eq [1, 2, user]
- end
-
- context 'when reference element does not exist' do
- it 'adds the element to the end of the list' do
- subject.insert_element_after(list, Project, 2)
-
- expect(list).to eq [1, user, 2]
- end
- end
- end
-end
diff --git a/spec/lib/sidebars/menu_spec.rb b/spec/lib/sidebars/menu_spec.rb
index 320f5f1ad1e..53483f0c924 100644
--- a/spec/lib/sidebars/menu_spec.rb
+++ b/spec/lib/sidebars/menu_spec.rb
@@ -8,59 +8,82 @@ RSpec.describe Sidebars::Menu do
describe '#all_active_routes' do
it 'gathers all active routes of items and the current menu' do
- menu_item1 = Sidebars::MenuItem.new(context)
- menu_item2 = Sidebars::MenuItem.new(context)
- menu_item3 = Sidebars::MenuItem.new(context)
- menu.add_item(menu_item1)
- menu.add_item(menu_item2)
- menu.add_item(menu_item3)
+ menu.add_item(Sidebars::MenuItem.new(title: 'foo1', link: 'foo1', active_routes: { path: %w(bar test) }))
+ menu.add_item(Sidebars::MenuItem.new(title: 'foo2', link: 'foo2', active_routes: { controller: 'fooc' }))
+ menu.add_item(Sidebars::MenuItem.new(title: 'foo3', link: 'foo3', active_routes: { controller: 'barc' }))
allow(menu).to receive(:active_routes).and_return({ path: 'foo' })
- allow(menu_item1).to receive(:active_routes).and_return({ path: %w(bar test) })
- allow(menu_item2).to receive(:active_routes).and_return({ controller: 'fooc' })
- allow(menu_item3).to receive(:active_routes).and_return({ controller: 'barc' })
expect(menu.all_active_routes).to eq({ path: %w(foo bar test), controller: %w(fooc barc) })
end
-
- it 'does not include routes for non renderable items' do
- menu_item = Sidebars::MenuItem.new(context)
- menu.add_item(menu_item)
-
- allow(menu).to receive(:active_routes).and_return({ path: 'foo' })
- allow(menu_item).to receive(:render?).and_return(false)
- allow(menu_item).to receive(:active_routes).and_return({ controller: 'bar' })
-
- expect(menu.all_active_routes).to eq({ path: ['foo'] })
- end
end
describe '#render?' do
context 'when the menus has no items' do
- it 'returns true' do
- expect(menu.render?).to be true
+ it 'returns false' do
+ expect(menu.render?).to be false
end
end
context 'when the menu has items' do
- let(:menu_item) { Sidebars::MenuItem.new(context) }
+ it 'returns true' do
+ menu.add_item(Sidebars::MenuItem.new(title: 'foo1', link: 'foo1', active_routes: {}))
- before do
- menu.add_item(menu_item)
+ expect(menu.render?).to be true
end
+ end
+ end
+
+ describe '#insert_element_before' do
+ let(:item1) { Sidebars::MenuItem.new(title: 'foo1', link: 'foo1', active_routes: {}, item_id: :foo1) }
+ let(:item2) { Sidebars::MenuItem.new(title: 'foo2', link: 'foo2', active_routes: {}, item_id: :foo2) }
+ let(:item3) { Sidebars::MenuItem.new(title: 'foo3', link: 'foo3', active_routes: {}, item_id: :foo3) }
+ let(:list) { [item1, item2] }
+
+ it 'adds element before the specific element class' do
+ menu.insert_element_before(list, :foo2, item3)
+
+ expect(list).to eq [item1, item3, item2]
+ end
+
+ it 'does not add nil elements' do
+ menu.insert_element_before(list, :foo2, nil)
+
+ expect(list).to eq [item1, item2]
+ end
- context 'when items are not renderable' do
- it 'returns false' do
- allow(menu_item).to receive(:render?).and_return(false)
+ context 'when reference element does not exist' do
+ it 'adds the element to the top of the list' do
+ menu.insert_element_before(list, :non_existent, item3)
- expect(menu.render?).to be false
- end
+ expect(list).to eq [item3, item1, item2]
end
+ end
+ end
+
+ describe '#insert_element_after' do
+ let(:item1) { Sidebars::MenuItem.new(title: 'foo1', link: 'foo1', active_routes: {}, item_id: :foo1) }
+ let(:item2) { Sidebars::MenuItem.new(title: 'foo2', link: 'foo2', active_routes: {}, item_id: :foo2) }
+ let(:item3) { Sidebars::MenuItem.new(title: 'foo3', link: 'foo3', active_routes: {}, item_id: :foo3) }
+ let(:list) { [item1, item2] }
+
+ it 'adds element after the specific element class' do
+ menu.insert_element_after(list, :foo1, item3)
+
+ expect(list).to eq [item1, item3, item2]
+ end
+
+ it 'does not add nil elements' do
+ menu.insert_element_after(list, :foo1, nil)
+
+ expect(list).to eq [item1, item2]
+ end
+
+ context 'when reference element does not exist' do
+ it 'adds the element to the end of the list' do
+ menu.insert_element_after(list, :non_existent, item3)
- context 'when there are renderable items' do
- it 'returns true' do
- expect(menu.render?).to be true
- end
+ expect(list).to eq [item1, item2, item3]
end
end
end
diff --git a/spec/lib/sidebars/panel_spec.rb b/spec/lib/sidebars/panel_spec.rb
index 0e539460810..5142b0bf07e 100644
--- a/spec/lib/sidebars/panel_spec.rb
+++ b/spec/lib/sidebars/panel_spec.rb
@@ -26,9 +26,79 @@ RSpec.describe Sidebars::Panel do
end
it 'returns true when no renderable menus' do
+ allow(menu1).to receive(:render?).and_return(true)
+
panel.add_menu(menu1)
expect(panel.has_renderable_menus?).to be true
end
end
+
+ describe '#add_element' do
+ it 'adds the element to the last position of the list' do
+ list = [1, 2]
+
+ panel.add_element(list, 3)
+
+ expect(list).to eq([1, 2, 3])
+ end
+
+ it 'does not add nil elements' do
+ list = []
+
+ panel.add_element(list, nil)
+
+ expect(list).to be_empty
+ end
+ end
+
+ describe '#insert_element_before' do
+ let(:user) { build(:user) }
+ let(:list) { [1, user] }
+
+ it 'adds element before the specific element class' do
+ panel.insert_element_before(list, User, 2)
+
+ expect(list).to eq [1, 2, user]
+ end
+
+ it 'does not add nil elements' do
+ panel.insert_element_before(list, User, nil)
+
+ expect(list).to eq [1, user]
+ end
+
+ context 'when reference element does not exist' do
+ it 'adds the element to the top of the list' do
+ panel.insert_element_before(list, Project, 2)
+
+ expect(list).to eq [2, 1, user]
+ end
+ end
+ end
+
+ describe '#insert_element_after' do
+ let(:user) { build(:user) }
+ let(:list) { [1, user] }
+
+ it 'adds element after the specific element class' do
+ panel.insert_element_after(list, Integer, 2)
+
+ expect(list).to eq [1, 2, user]
+ end
+
+ it 'does not add nil elements' do
+ panel.insert_element_after(list, Integer, nil)
+
+ expect(list).to eq [1, user]
+ end
+
+ context 'when reference element does not exist' do
+ it 'adds the element to the end of the list' do
+ panel.insert_element_after(list, Project, 2)
+
+ expect(list).to eq [1, user, 2]
+ end
+ end
+ end
end
diff --git a/spec/lib/sidebars/projects/menus/learn_gitlab/menu_spec.rb b/spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb
index bc1815558d3..3dfb4c6e9e8 100644
--- a/spec/lib/sidebars/projects/menus/learn_gitlab/menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Sidebars::Projects::Menus::LearnGitlab::Menu do
+RSpec.describe Sidebars::Projects::Menus::LearnGitlabMenu do
let(:project) { build(:project) }
let(:experiment_enabled) { true }
let(:context) { Sidebars::Projects::Context.new(current_user: nil, container: project, learn_gitlab_experiment_enabled: experiment_enabled) }
diff --git a/spec/lib/sidebars/projects/menus/project_overview/menu_items/releases_spec.rb b/spec/lib/sidebars/projects/menus/project_overview/menu_items/releases_spec.rb
deleted file mode 100644
index db124c2252e..00000000000
--- a/spec/lib/sidebars/projects/menus/project_overview/menu_items/releases_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Sidebars::Projects::Menus::ProjectOverview::MenuItems::Releases do
- let_it_be(:project) { create(:project, :repository) }
-
- let(:user) { project.owner }
- let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
-
- subject { described_class.new(context) }
-
- describe '#render?' do
- context 'when project repository is empty' do
- it 'returns false' do
- allow(project).to receive(:empty_repo?).and_return(true)
-
- expect(subject.render?).to eq false
- end
- end
-
- context 'when project repository is not empty' do
- context 'when user can read releases' do
- it 'returns true' do
- expect(subject.render?).to eq true
- end
- end
-
- context 'when user cannot read releases' do
- let(:user) { nil }
-
- it 'returns false' do
- expect(subject.render?).to eq false
- end
- end
- end
- end
-end
diff --git a/spec/lib/sidebars/projects/menus/project_overview/menu_spec.rb b/spec/lib/sidebars/projects/menus/project_overview/menu_spec.rb
deleted file mode 100644
index 105a28ce953..00000000000
--- a/spec/lib/sidebars/projects/menus/project_overview/menu_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Sidebars::Projects::Menus::ProjectOverview::Menu do
- let(:project) { build(:project) }
- let(:context) { Sidebars::Projects::Context.new(current_user: nil, container: project) }
-
- subject { described_class.new(context) }
-
- it 'has the required items' do
- items = subject.instance_variable_get(:@items)
-
- expect(items[0]).to be_a(Sidebars::Projects::Menus::ProjectOverview::MenuItems::Details)
- expect(items[1]).to be_a(Sidebars::Projects::Menus::ProjectOverview::MenuItems::Activity)
- expect(items[2]).to be_a(Sidebars::Projects::Menus::ProjectOverview::MenuItems::Releases)
- end
-end
diff --git a/spec/lib/sidebars/projects/menus/project_overview_menu_spec.rb b/spec/lib/sidebars/projects/menus/project_overview_menu_spec.rb
new file mode 100644
index 00000000000..91682a9e415
--- /dev/null
+++ b/spec/lib/sidebars/projects/menus/project_overview_menu_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::Projects::Menus::ProjectOverviewMenu do
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:user) { project.owner }
+ let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
+
+ describe 'Releases' do
+ subject { described_class.new(context).items.index { |e| e.item_id == :releases } }
+
+ context 'when project repository is empty' do
+ it 'does not include releases menu item' do
+ allow(project).to receive(:empty_repo?).and_return(true)
+
+ is_expected.to be_nil
+ end
+ end
+
+ context 'when project repository is not empty' do
+ context 'when user can download code' do
+ it 'includes releases menu item' do
+ is_expected.to be_present
+ end
+ end
+
+ context 'when user cannot download code' do
+ let(:user) { nil }
+
+ it 'does not include releases menu item' do
+ is_expected.to be_nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/sidebars/projects/menus/repository/menu_spec.rb b/spec/lib/sidebars/projects/menus/repository_menu_spec.rb
index 04eb3357a6f..554a4e3f532 100644
--- a/spec/lib/sidebars/projects/menus/repository/menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/repository_menu_spec.rb
@@ -2,11 +2,11 @@
require 'spec_helper'
-RSpec.describe Sidebars::Projects::Menus::Repository::Menu do
+RSpec.describe Sidebars::Projects::Menus::RepositoryMenu do
let_it_be(:project) { create(:project, :repository) }
let(:user) { project.owner }
- let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
+ let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, current_ref: 'master') }
subject { described_class.new(context) }
diff --git a/spec/lib/sidebars/projects/panel_spec.rb b/spec/lib/sidebars/projects/panel_spec.rb
index bad9b17bc83..f4609682e6d 100644
--- a/spec/lib/sidebars/projects/panel_spec.rb
+++ b/spec/lib/sidebars/projects/panel_spec.rb
@@ -9,6 +9,6 @@ RSpec.describe Sidebars::Projects::Panel do
subject { described_class.new(context) }
it 'has a scope menu' do
- expect(subject.scope_menu).to be_a(Sidebars::Projects::Menus::Scope::Menu)
+ expect(subject.scope_menu).to be_a(Sidebars::Projects::Menus::ScopeMenu)
end
end
diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb
index f2659771a49..e520f82c118 100644
--- a/spec/models/pages/lookup_path_spec.rb
+++ b/spec/models/pages/lookup_path_spec.rb
@@ -47,15 +47,11 @@ RSpec.describe Pages::LookupPath do
describe '#source' do
let(:source) { lookup_path.source }
- shared_examples 'uses disk storage' do
- it 'uses disk storage', :aggregate_failures do
- expect(source[:type]).to eq('file')
- expect(source[:path]).to eq(project.full_path + "/public/")
- end
+ it 'uses disk storage', :aggregate_failures do
+ expect(source[:type]).to eq('file')
+ expect(source[:path]).to eq(project.full_path + "/public/")
end
- include_examples 'uses disk storage'
-
it 'return nil when legacy storage is disabled and there is no deployment' do
stub_feature_flags(pages_serve_from_legacy_storage: false)
expect(Gitlab::ErrorTracking).to receive(:track_exception)
@@ -107,14 +103,6 @@ RSpec.describe Pages::LookupPath do
)
end
end
-
- context 'when pages_serve_with_zip_file_protocol feature flag is disabled' do
- before do
- stub_feature_flags(pages_serve_with_zip_file_protocol: false)
- end
-
- include_examples 'uses disk storage'
- end
end
context 'when deployment were created during migration' do
diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb
index c4146b347d7..caa0a886abf 100644
--- a/spec/models/todo_spec.rb
+++ b/spec/models/todo_spec.rb
@@ -452,11 +452,15 @@ RSpec.describe Todo do
end
end
- describe '.pluck_user_id' do
- subject { described_class.pluck_user_id }
+ describe '.distinct_user_ids' do
+ subject { described_class.distinct_user_ids }
- let_it_be(:todo) { create(:todo) }
+ let_it_be(:user1) { create(:user) }
+ let_it_be(:user2) { create(:user) }
+ let_it_be(:todo) { create(:todo, user: user1) }
+ let_it_be(:todo) { create(:todo, user: user1) }
+ let_it_be(:todo) { create(:todo, user: user2) }
- it { is_expected.to eq([todo.user_id]) }
+ it { is_expected.to contain_exactly(user1.id, user2.id) }
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index ff2ee4adc1c..1f7d5c5f91f 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -20,6 +20,10 @@ RSpec.describe User do
it { is_expected.to include_module(AsyncDeviseEmail) }
end
+ describe 'constants' do
+ it { expect(described_class::COUNT_CACHE_VALIDITY_PERIOD).to be_a(Integer) }
+ end
+
describe 'delegations' do
it { is_expected.to delegate_method(:path).to(:namespace).with_prefix }
diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb
index 47402fea2b5..d6acc20396f 100644
--- a/spec/presenters/blob_presenter_spec.rb
+++ b/spec/presenters/blob_presenter_spec.rb
@@ -2,52 +2,59 @@
require 'spec_helper'
-RSpec.describe BlobPresenter, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
-
- let(:git_blob) do
- Gitlab::Git::Blob.find(
- repository,
- 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
- 'files/ruby/regex.rb'
- )
+RSpec.describe BlobPresenter do
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:repository) { project.repository }
+ let(:blob) { repository.blob_at('HEAD', 'files/ruby/regex.rb') }
+
+ subject(:presenter) { described_class.new(blob) }
+
+ describe '#web_url' do
+ it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
end
- let(:blob) { Blob.new(git_blob) }
+ describe '#web_path' do
+ it { expect(presenter.web_path).to eq("/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
+ end
- describe '.web_url' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
- let(:blob) { Gitlab::Graphql::Representation::TreeEntry.new(repository.tree.blobs.first, repository) }
+ describe '#edit_blob_path' do
+ it { expect(presenter.edit_blob_path).to eq("/#{project.full_path}/-/edit/#{blob.commit_id}/#{blob.path}") }
+ end
- subject { described_class.new(blob) }
+ describe '#raw_path' do
+ it { expect(presenter.raw_path).to eq("/#{project.full_path}/-/raw/#{blob.commit_id}/#{blob.path}") }
+ end
- it { expect(subject.web_url).to eq("http://localhost/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
+ describe '#replace_path' do
+ it { expect(presenter.replace_path).to eq("/#{project.full_path}/-/create/#{blob.commit_id}/#{blob.path}") }
end
- describe '#web_path' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
- let(:blob) { Gitlab::Graphql::Representation::TreeEntry.new(repository.tree.blobs.first, repository) }
+ context 'given a Gitlab::Graphql::Representation::TreeEntry' do
+ let(:blob) { Gitlab::Graphql::Representation::TreeEntry.new(super(), repository) }
- subject { described_class.new(blob) }
+ describe '#web_url' do
+ it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
+ end
- it { expect(subject.web_path).to eq("/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
+ describe '#web_path' do
+ it { expect(presenter.web_path).to eq("/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
+ end
end
describe '#highlight' do
- subject { described_class.new(blob) }
+ let(:git_blob) { blob.__getobj__ }
it 'returns highlighted content' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: nil)
- subject.highlight
+ presenter.highlight
end
it 'returns plain content when :plain is true' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: true, language: nil)
- subject.highlight(plain: true)
+ presenter.highlight(plain: true)
end
context '"to" param is present' do
@@ -60,7 +67,7 @@ RSpec.describe BlobPresenter, :seed_helper do
it 'returns limited highlighted content' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', "line one\n", plain: nil, language: nil)
- subject.highlight(to: 1)
+ presenter.highlight(to: 1)
end
end
@@ -72,7 +79,7 @@ RSpec.describe BlobPresenter, :seed_helper do
it 'passes language to inner call' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: 'ruby')
- subject.highlight
+ presenter.highlight
end
end
end
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index aa1a4643593..3da372c86a1 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
RSpec.describe API::Environments do
- let(:user) { create(:user) }
- let(:non_member) { create(:user) }
- let(:project) { create(:project, :private, :repository, namespace: user.namespace) }
- let!(:environment) { create(:environment, project: project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:non_member) { create(:user) }
+ let_it_be(:project) { create(:project, :private, :repository, namespace: user.namespace) }
+ let_it_be_with_reload(:environment) { create(:environment, project: project) }
before do
project.add_maintainer(user)
@@ -34,11 +34,51 @@ RSpec.describe API::Environments do
expect(json_response.first['name']).to eq(environment.name)
expect(json_response.first['external_url']).to eq(environment.external_url)
expect(json_response.first['project'].keys).to contain_exactly(*project_data_keys)
- expect(json_response.first).not_to have_key("last_deployment")
+ expect(json_response.first['enable_advanced_logs_querying']).to eq(false)
+ expect(json_response.first).not_to have_key('last_deployment')
+ end
+
+ context 'when elastic stack is available' do
+ before do
+ allow_next_found_instance_of(Environment) do |env|
+ allow(env).to receive(:elastic_stack_available?).and_return(true)
+ end
+ end
+
+ context 'when the user can read pod logs' do
+ it 'returns environment with enable_advanced_logs_querying' do
+ get api("/projects/#{project.id}/environments", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['enable_advanced_logs_querying']).to eq(true)
+ end
+ end
+
+ context 'when the user cannot read pod logs' do
+ before do
+ allow_next_found_instance_of(User) do |user|
+ allow(user).to receive(:can?).and_call_original
+ allow(user).to receive(:can?).with(:read_pod_logs, project).and_return(false)
+ end
+ end
+
+ it 'does not contain enable_advanced_logs_querying' do
+ get api("/projects/#{project.id}/environments", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(1)
+ expect(json_response.first).not_to have_key('enable_advanced_logs_querying')
+ end
+ end
end
context 'when filtering' do
- let!(:environment2) { create(:environment, project: project) }
+ let_it_be(:environment2) { create(:environment, project: project) }
it 'returns environment by name' do
get api("/projects/#{project.id}/environments?name=#{environment.name}", user)
diff --git a/spec/serializers/ci/downloadable_artifact_entity_spec.rb b/spec/serializers/ci/downloadable_artifact_entity_spec.rb
new file mode 100644
index 00000000000..34a271e7422
--- /dev/null
+++ b/spec/serializers/ci/downloadable_artifact_entity_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::DownloadableArtifactEntity do
+ let(:pipeline) { create(:ci_pipeline, :with_codequality_reports) }
+ let(:user) { create(:user) }
+ let(:request) { EntityRequest.new({ current_user: user }) }
+ let(:entity) { described_class.new(pipeline, request: request) }
+
+ describe '#as_json' do
+ subject { entity.as_json }
+
+ it 'contains required fields', :aggregate_failures do
+ expect(subject).to include(:artifacts)
+ expect(subject[:artifacts].size).to eq(1)
+ end
+
+ context 'when user cannot read job artifact' do
+ let!(:build) { create(:ci_build, :success, :artifacts, :non_public_artifacts, pipeline: pipeline) }
+
+ it 'returns only artifacts readable by user', :aggregate_failures do
+ expect(subject[:artifacts].size).to eq(1)
+ expect(subject[:artifacts].first[:name]).to eq("test:codequality")
+ end
+ end
+ end
+end
diff --git a/spec/serializers/ci/downloadable_artifact_serializer_spec.rb b/spec/serializers/ci/downloadable_artifact_serializer_spec.rb
new file mode 100644
index 00000000000..90f159a06f9
--- /dev/null
+++ b/spec/serializers/ci/downloadable_artifact_serializer_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::DownloadableArtifactSerializer do
+ let(:pipeline) { create(:ci_pipeline, :with_codequality_reports) }
+ let(:user) { create(:user) }
+ let(:serializer) { described_class.new(current_user: user).represent(pipeline) }
+
+ describe '#as_json' do
+ subject { serializer.as_json }
+
+ it 'matches schema' do
+ expect(subject).to match_schema('entities/downloadable_artifact')
+ end
+ end
+end
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index d8435c72896..5d60b6e0487 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -3,133 +3,68 @@
require 'spec_helper'
RSpec.describe SystemHooksService do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
- let(:project_member) { create(:project_member) }
- let(:key) { create(:key, user: user) }
- let(:deploy_key) { create(:key) }
- let(:group) { create(:group) }
- let(:group_member) { create(:group_member) }
-
- context 'event data' do
- it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) }
- it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) }
- it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
- it { expect(event_data(project, :update)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
- it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
- it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :user_id, :access_level, :project_visibility) }
- it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :user_id, :access_level, :project_visibility) }
- it { expect(event_data(project_member, :update)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :user_id, :access_level, :project_visibility) }
- it { expect(event_data(key, :create)).to include(:username, :key, :id) }
- it { expect(event_data(key, :destroy)).to include(:username, :key, :id) }
- it { expect(event_data(deploy_key, :create)).to include(:key, :id) }
- it { expect(event_data(deploy_key, :destroy)).to include(:key, :id) }
-
- it do
- project.old_path_with_namespace = 'renamed_from_path'
- expect(event_data(project, :rename)).to include(
- :event_name, :name, :created_at, :updated_at, :path, :project_id,
- :owner_name, :owner_email, :project_visibility,
- :old_path_with_namespace
- )
+ describe '#execute_hooks_for' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:group_member) { create(:group_member, source: group, user: user) }
+ let_it_be(:project_member) { create(:project_member, source: project, user: user) }
+ let_it_be(:key) { create(:key, user: user) }
+ let_it_be(:deploy_key) { create(:key) }
+
+ let(:event) { :create }
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:model_name, :builder_class) do
+ :group_member | Gitlab::HookData::GroupMemberBuilder
+ :group | Gitlab::HookData::GroupBuilder
+ :project_member | Gitlab::HookData::ProjectMemberBuilder
+ :user | Gitlab::HookData::UserBuilder
+ :project | Gitlab::HookData::ProjectBuilder
+ :key | Gitlab::HookData::KeyBuilder
+ :deploy_key | Gitlab::HookData::KeyBuilder
end
- it do
- project.old_path_with_namespace = 'transferred_from_path'
- expect(event_data(project, :transfer)).to include(
- :event_name, :name, :created_at, :updated_at, :path, :project_id,
- :owner_name, :owner_email, :project_visibility,
- :old_path_with_namespace
- )
- end
-
- it do
- expect(event_data(group, :create)).to include(
- :event_name, :name, :created_at, :updated_at, :path, :group_id
- )
- end
+ with_them do
+ it 'builds the data with the relevant builder class and then calls #execute_hooks with the obtained data' do
+ data = double
+ model = public_send(model_name)
- it do
- expect(event_data(group, :destroy)).to include(
- :event_name, :name, :created_at, :updated_at, :path, :group_id
- )
- end
+ expect_next_instance_of(builder_class, model) do |builder|
+ expect(builder).to receive(:build).with(event).and_return(data)
+ end
- it do
- expect(event_data(group_member, :create)).to include(
- :event_name, :created_at, :updated_at, :group_name, :group_path,
- :group_id, :user_id, :user_username, :user_name, :user_email, :group_access
- )
- end
+ service = described_class.new
- it do
- expect(event_data(group_member, :destroy)).to include(
- :event_name, :created_at, :updated_at, :group_name, :group_path,
- :group_id, :user_id, :user_username, :user_name, :user_email, :group_access
- )
- end
-
- it do
- expect(event_data(group_member, :update)).to include(
- :event_name, :created_at, :updated_at, :group_name, :group_path,
- :group_id, :user_id, :user_username, :user_name, :user_email, :group_access
- )
- end
-
- it 'includes the correct project visibility level' do
- data = event_data(project, :create)
-
- expect(data[:project_visibility]).to eq('private')
- end
+ expect_next_instance_of(SystemHooksService) do |system_hook_service|
+ expect(system_hook_service).to receive(:execute_hooks).with(data)
+ end
- it 'handles nil datetime columns' do
- user.update!(created_at: nil, updated_at: nil)
- data = event_data(user, :destroy)
-
- expect(data[:created_at]).to be(nil)
- expect(data[:updated_at]).to be(nil)
+ service.execute_hooks_for(model, event)
+ end
end
+ end
- context 'group_rename' do
- it 'contains old and new path' do
- allow(group).to receive(:path_before_last_save).and_return('old-path')
+ describe '#execute_hooks' do
+ let(:data) { { key: :value } }
- data = event_data(group, :rename)
+ subject { described_class.new.execute_hooks(data) }
- expect(data).to include(:event_name, :name, :created_at, :updated_at, :full_path, :path, :group_id, :old_path, :old_full_path)
- expect(data[:path]).to eq(group.path)
- expect(data[:full_path]).to eq(group.path)
- expect(data[:old_path]).to eq(group.path_before_last_save)
- expect(data[:old_full_path]).to eq(group.path_before_last_save)
- end
+ it 'executes system hooks with the given data' do
+ hook = create(:system_hook)
- it 'contains old and new full_path for subgroup' do
- subgroup = create(:group, parent: group)
- allow(subgroup).to receive(:path_before_last_save).and_return('old-path')
+ allow(SystemHook).to receive_message_chain(:hooks_for, :find_each).and_yield(hook)
- data = event_data(subgroup, :rename)
+ expect(hook).to receive(:async_execute).with(data, 'system_hooks')
- expect(data[:full_path]).to eq(subgroup.full_path)
- expect(data[:old_path]).to eq('old-path')
- end
+ subject
end
- end
- context 'event names' do
- it { expect(event_name(project, :create)).to eq "project_create" }
- it { expect(event_name(project, :destroy)).to eq "project_destroy" }
- it { expect(event_name(project, :rename)).to eq "project_rename" }
- it { expect(event_name(project, :transfer)).to eq "project_transfer" }
- it { expect(event_name(project, :update)).to eq "project_update" }
- it { expect(event_name(key, :create)).to eq 'key_create' }
- it { expect(event_name(key, :destroy)).to eq 'key_destroy' }
- end
+ it 'executes FileHook with the given data' do
+ expect(Gitlab::FileHook).to receive(:execute_all_async).with(data)
- def event_data(*args)
- SystemHooksService.new.send :build_event_data, *args
- end
-
- def event_name(*args)
- SystemHooksService.new.send :build_event_name, *args
+ subject
+ end
end
end
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index 35503010b53..6a8e6dc8970 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -348,7 +348,7 @@ RSpec.describe TodoService do
create(:todo, state: :pending, target: issue, user: author, author: author, project: issue.project)
create(:todo, state: :done, target: issue, user: assignee, author: assignee, project: issue.project)
- expect_next(Users::UpdateTodoCountCacheService, [author, assignee]).to receive(:execute)
+ expect_next(Users::UpdateTodoCountCacheService, [author.id, assignee.id]).to receive(:execute)
service.destroy_target(issue) { issue.destroy! }
end
@@ -1094,7 +1094,7 @@ RSpec.describe TodoService do
it 'updates cached counts when a todo is created' do
issue = create(:issue, project: project, assignees: [john_doe], author: author)
- expect_next(Users::UpdateTodoCountCacheService, [john_doe]).to receive(:execute)
+ expect_next(Users::UpdateTodoCountCacheService, [john_doe.id]).to receive(:execute)
service.new_issue(issue, author)
end
diff --git a/spec/services/users/update_todo_count_cache_service_spec.rb b/spec/services/users/update_todo_count_cache_service_spec.rb
index 3e3618b1291..3d96af928df 100644
--- a/spec/services/users/update_todo_count_cache_service_spec.rb
+++ b/spec/services/users/update_todo_count_cache_service_spec.rb
@@ -14,13 +14,21 @@ RSpec.describe Users::UpdateTodoCountCacheService do
let_it_be(:todo5) { create(:todo, user: user2, state: :pending) }
let_it_be(:todo6) { create(:todo, user: user2, state: :pending) }
+ def execute_all
+ described_class.new([user1.id, user2.id]).execute
+ end
+
+ def execute_single
+ described_class.new([user1.id]).execute
+ end
+
it 'updates the todos_counts for users', :use_clean_rails_memory_store_caching do
Rails.cache.write(['users', user1.id, 'todos_done_count'], 0)
Rails.cache.write(['users', user1.id, 'todos_pending_count'], 0)
Rails.cache.write(['users', user2.id, 'todos_done_count'], 0)
Rails.cache.write(['users', user2.id, 'todos_pending_count'], 0)
- expect { described_class.new([user1, user2]).execute }
+ expect { execute_all }
.to change(user1, :todos_done_count).from(0).to(2)
.and change(user1, :todos_pending_count).from(0).to(1)
.and change(user2, :todos_done_count).from(0).to(1)
@@ -28,7 +36,7 @@ RSpec.describe Users::UpdateTodoCountCacheService do
Todo.delete_all
- expect { described_class.new([user1, user2]).execute }
+ expect { execute_all }
.to change(user1, :todos_done_count).from(2).to(0)
.and change(user1, :todos_pending_count).from(1).to(0)
.and change(user2, :todos_done_count).from(1).to(0)
@@ -36,26 +44,24 @@ RSpec.describe Users::UpdateTodoCountCacheService do
end
it 'avoids N+1 queries' do
- control_count = ActiveRecord::QueryRecorder.new { described_class.new([user1]).execute }.count
+ control_count = ActiveRecord::QueryRecorder.new { execute_single }.count
- expect { described_class.new([user1, user2]).execute }.not_to exceed_query_limit(control_count)
+ expect { execute_all }.not_to exceed_query_limit(control_count)
end
it 'executes one query per batch of users' do
stub_const("#{described_class}::QUERY_BATCH_SIZE", 1)
- expect(ActiveRecord::QueryRecorder.new { described_class.new([user1]).execute }.count).to eq(1)
- expect(ActiveRecord::QueryRecorder.new { described_class.new([user1, user2]).execute }.count).to eq(2)
+ expect(ActiveRecord::QueryRecorder.new { execute_single }.count).to eq(1)
+ expect(ActiveRecord::QueryRecorder.new { execute_all }.count).to eq(2)
end
- it 'sets the cache expire time to the users count_cache_validity_period' do
- allow(user1).to receive(:count_cache_validity_period).and_return(1.minute)
- allow(user2).to receive(:count_cache_validity_period).and_return(1.hour)
-
- expect(Rails.cache).to receive(:write).with(['users', user1.id, anything], anything, expires_in: 1.minute).twice
- expect(Rails.cache).to receive(:write).with(['users', user2.id, anything], anything, expires_in: 1.hour).twice
+ it 'sets the correct cache expire time' do
+ expect(Rails.cache).to receive(:write)
+ .with(['users', user1.id, anything], anything, expires_in: User::COUNT_CACHE_VALIDITY_PERIOD)
+ .twice
- described_class.new([user1, user2]).execute
+ execute_single
end
end
end
diff --git a/spec/views/shared/nav/_sidebar.html.haml_spec.rb b/spec/views/shared/nav/_sidebar.html.haml_spec.rb
index 268d2952683..7c7c7176e0f 100644
--- a/spec/views/shared/nav/_sidebar.html.haml_spec.rb
+++ b/spec/views/shared/nav/_sidebar.html.haml_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'shared/nav/_sidebar.html.haml' do
let(:project) { build(:project, id: non_existing_record_id) }
- let(:context) { Sidebars::Projects::Context.new(current_user: nil, container: project)}
+ let(:context) { Sidebars::Projects::Context.new(current_user: nil, container: project) }
let(:sidebar) { Sidebars::Projects::Panel.new(context) }
before do