summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-05-16 12:09:06 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-16 12:09:06 +0000
commit0045970352e8729b2797591beb88a7df884d84f4 (patch)
treeb9cd4c5aaaa26ce4a3c944ec5cfdbd7ad44b796d /spec
parent613868af23d7c0e09210857518895edd6678f5e9 (diff)
downloadgitlab-ce-0045970352e8729b2797591beb88a7df884d84f4.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/alert_management/http_integrations.rb6
-rw-r--r--spec/finders/alert_management/http_integrations_finder_spec.rb38
-rw-r--r--spec/frontend/fixtures/pipeline_details.rb38
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js38
-rw-r--r--spec/frontend/pipelines/__snapshots__/utils_spec.js.snap471
-rw-r--r--spec/frontend/pipelines/graph/graph_component_spec.js8
-rw-r--r--spec/frontend/pipelines/graph/graph_component_wrapper_spec.js23
-rw-r--r--spec/frontend/pipelines/graph/linked_pipelines_column_spec.js8
-rw-r--r--spec/frontend/pipelines/graph/mock_data.js704
-rw-r--r--spec/frontend/pipelines/graph_shared/links_layer_spec.js4
-rw-r--r--spec/frontend/pipelines/utils_spec.js11
-rw-r--r--spec/lib/api/helpers/packages/npm_spec.rb42
-rw-r--r--spec/migrations/add_type_to_http_integrations_spec.rb21
-rw-r--r--spec/models/alert_management/http_integration_spec.rb91
-rw-r--r--spec/requests/api/npm_group_packages_spec.rb198
-rw-r--r--spec/services/alert_management/http_integrations/create_service_spec.rb38
-rw-r--r--spec/services/alert_management/http_integrations/destroy_service_spec.rb7
-rw-r--r--spec/services/projects/prometheus/alerts/notify_service_spec.rb20
-rw-r--r--spec/support/helpers/javascript_fixtures_helpers.rb5
-rw-r--r--spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb10
20 files changed, 534 insertions, 1247 deletions
diff --git a/spec/factories/alert_management/http_integrations.rb b/spec/factories/alert_management/http_integrations.rb
index 405ec09251f..43cf8b3c6db 100644
--- a/spec/factories/alert_management/http_integrations.rb
+++ b/spec/factories/alert_management/http_integrations.rb
@@ -19,6 +19,12 @@ FactoryBot.define do
endpoint_identifier { 'legacy' }
end
+ trait :prometheus do
+ type_identifier { :prometheus }
+ end
+
initialize_with { new(**attributes) }
+
+ factory :alert_management_prometheus_integration, traits: [:prometheus]
end
end
diff --git a/spec/finders/alert_management/http_integrations_finder_spec.rb b/spec/finders/alert_management/http_integrations_finder_spec.rb
index d65de2cdbbd..eb3d24f8653 100644
--- a/spec/finders/alert_management/http_integrations_finder_spec.rb
+++ b/spec/finders/alert_management/http_integrations_finder_spec.rb
@@ -2,10 +2,12 @@
require 'spec_helper'
-RSpec.describe AlertManagement::HttpIntegrationsFinder do
+RSpec.describe AlertManagement::HttpIntegrationsFinder, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be_with_reload(:integration) { create(:alert_management_http_integration, project: project ) }
let_it_be(:extra_integration) { create(:alert_management_http_integration, project: project ) }
+ let_it_be(:prometheus_integration) { create(:alert_management_prometheus_integration, :inactive, project: project ) }
+ let_it_be(:extra_prometheus_integration) { create(:alert_management_prometheus_integration, project: project ) }
let_it_be(:alt_project_integration) { create(:alert_management_http_integration) }
let(:params) { {} }
@@ -14,7 +16,7 @@ RSpec.describe AlertManagement::HttpIntegrationsFinder do
subject(:execute) { described_class.new(project, params).execute }
context 'empty params' do
- it { is_expected.to contain_exactly(integration) }
+ it { is_expected.to contain_exactly(integration, prometheus_integration) }
end
context 'endpoint_identifier param given' do
@@ -37,7 +39,7 @@ RSpec.describe AlertManagement::HttpIntegrationsFinder do
context 'but blank' do
let(:params) { { endpoint_identifier: nil } }
- it { is_expected.to contain_exactly(integration) }
+ it { is_expected.to contain_exactly(integration, prometheus_integration) }
end
end
@@ -46,18 +48,34 @@ RSpec.describe AlertManagement::HttpIntegrationsFinder do
it { is_expected.to contain_exactly(integration) }
- context 'when integration is disabled' do
- before do
- integration.update!(active: false)
- end
+ context 'but blank' do
+ let(:params) { { active: nil } }
- it { is_expected.to be_empty }
+ it { is_expected.to contain_exactly(integration, prometheus_integration) }
+ end
+ end
+
+ context 'type_identifier param given' do
+ let(:params) { { type_identifier: extra_integration.type_identifier } }
+
+ it { is_expected.to contain_exactly(integration) }
+
+ context 'matches an unavailable integration' do
+ let(:params) { { type_identifier: extra_prometheus_integration.type_identifier } }
+
+ it { is_expected.to contain_exactly(prometheus_integration) }
+ end
+
+ context 'but unknown' do
+ let(:params) { { type_identifier: :unknown } }
+
+ it { is_expected.to contain_exactly(integration, prometheus_integration) }
end
context 'but blank' do
- let(:params) { { active: nil } }
+ let(:params) { { type_identifier: nil } }
- it { is_expected.to contain_exactly(integration) }
+ it { is_expected.to contain_exactly(integration, prometheus_integration) }
end
end
diff --git a/spec/frontend/fixtures/pipeline_details.rb b/spec/frontend/fixtures/pipeline_details.rb
new file mode 100644
index 00000000000..af9b11b0841
--- /dev/null
+++ b/spec/frontend/fixtures/pipeline_details.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe "GraphQL Pipeline details", '(JavaScript fixtures)', type: :request, feature_category: :pipeline_composition do
+ include ApiHelpers
+ include GraphqlHelpers
+ include JavaScriptFixturesHelpers
+
+ let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures') }
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:admin) { project.first_owner }
+ let_it_be(:commit) { create(:commit, project: project) }
+ let_it_be(:pipeline) do
+ create(:ci_pipeline, project: project, sha: commit.id, ref: 'master', user: admin, status: :success)
+ end
+
+ let_it_be(:build_success) do
+ create(:ci_build, :dependent, name: 'build_my_app', pipeline: pipeline, stage: 'build', status: :success)
+ end
+
+ let_it_be(:build_test) { create(:ci_build, :dependent, name: 'test_my_app', pipeline: pipeline, stage: 'test') }
+ let_it_be(:build_deploy_failed) do
+ create(:ci_build, :dependent, name: 'deploy_my_app', status: :failed, pipeline: pipeline, stage: 'deploy')
+ end
+
+ let_it_be(:bridge) { create(:ci_bridge, pipeline: pipeline) }
+
+ let(:pipeline_details_query_path) { 'app/graphql/queries/pipelines/get_pipeline_details.query.graphql' }
+
+ it "pipelines/pipeline_details.json" do
+ query = get_graphql_query_as_string(pipeline_details_query_path, with_base_path: false)
+
+ post_graphql(query, current_user: admin, variables: { projectPath: project.full_path, iid: pipeline.iid })
+
+ expect_graphql_errors_to_be_empty
+ end
+end
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index 0799bc87c8c..4bf3a779f00 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -397,6 +397,44 @@ describe('URL utility', () => {
});
});
+ describe('visitUrl', () => {
+ let originalLocation;
+ const mockUrl = 'http://example.com/page';
+
+ beforeAll(() => {
+ originalLocation = window.location;
+
+ Object.defineProperty(window, 'location', {
+ writable: true,
+ value: new URL(TEST_HOST),
+ });
+ });
+
+ afterAll(() => {
+ window.location = originalLocation;
+ });
+
+ it('navigates to a page', () => {
+ urlUtils.visitUrl(mockUrl);
+
+ expect(window.location.href).toBe(mockUrl);
+ });
+
+ it('navigates to a new page', () => {
+ const otherWindow = {};
+
+ Object.defineProperty(window, 'open', {
+ writable: true,
+ value: jest.fn().mockReturnValue(otherWindow),
+ });
+
+ urlUtils.visitUrl(mockUrl, true);
+
+ expect(otherWindow.opener).toBe(null);
+ expect(otherWindow.location).toBe(mockUrl);
+ });
+ });
+
describe('updateHistory', () => {
const state = { key: 'prop' };
const title = 'TITLE';
diff --git a/spec/frontend/pipelines/__snapshots__/utils_spec.js.snap b/spec/frontend/pipelines/__snapshots__/utils_spec.js.snap
deleted file mode 100644
index 724ec7366d3..00000000000
--- a/spec/frontend/pipelines/__snapshots__/utils_spec.js.snap
+++ /dev/null
@@ -1,471 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`DAG visualization parsing utilities generateColumnsFromLayersList matches the snapshot 1`] = `
-Array [
- Object {
- "groups": Array [
- Object {
- "__typename": "CiGroup",
- "id": "4",
- "jobs": Array [
- Object {
- "__typename": "CiJob",
- "id": "6",
- "kind": "BUILD",
- "name": "build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
- "needs": Array [],
- "previousStageJobsOrNeeds": Array [],
- "scheduledAt": null,
- "status": Object {
- "__typename": "DetailedStatus",
- "action": Object {
- "__typename": "StatusAction",
- "buttonTitle": "Retry this job",
- "icon": "retry",
- "id": "8",
- "path": "/root/abcd-dag/-/jobs/1482/retry",
- "title": "Retry",
- },
- "detailsPath": "/root/abcd-dag/-/jobs/1482",
- "group": "success",
- "hasDetails": true,
- "icon": "status_success",
- "id": "7",
- "label": "passed",
- "tooltip": "passed",
- },
- },
- ],
- "name": "build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
- "size": 1,
- "stageName": "build",
- "status": Object {
- "__typename": "DetailedStatus",
- "group": "success",
- "icon": "status_success",
- "id": "5",
- "label": "passed",
- },
- },
- Object {
- "__typename": "CiGroup",
- "id": "9",
- "jobs": Array [
- Object {
- "__typename": "CiJob",
- "id": "11",
- "kind": "BUILD",
- "name": "build_b",
- "needs": Array [],
- "previousStageJobsOrNeeds": Array [],
- "scheduledAt": null,
- "status": Object {
- "__typename": "DetailedStatus",
- "action": Object {
- "__typename": "StatusAction",
- "buttonTitle": "Retry this job",
- "icon": "retry",
- "id": "13",
- "path": "/root/abcd-dag/-/jobs/1515/retry",
- "title": "Retry",
- },
- "detailsPath": "/root/abcd-dag/-/jobs/1515",
- "group": "success",
- "hasDetails": true,
- "icon": "status_success",
- "id": "12",
- "label": "passed",
- "tooltip": "passed",
- },
- },
- ],
- "name": "build_b",
- "size": 1,
- "stageName": "build",
- "status": Object {
- "__typename": "DetailedStatus",
- "group": "success",
- "icon": "status_success",
- "id": "10",
- "label": "passed",
- },
- },
- Object {
- "__typename": "CiGroup",
- "id": "14",
- "jobs": Array [
- Object {
- "__typename": "CiJob",
- "id": "16",
- "kind": "BUILD",
- "name": "build_c",
- "needs": Array [],
- "previousStageJobsOrNeeds": Array [],
- "scheduledAt": null,
- "status": Object {
- "__typename": "DetailedStatus",
- "action": Object {
- "__typename": "StatusAction",
- "buttonTitle": "Retry this job",
- "icon": "retry",
- "id": "18",
- "path": "/root/abcd-dag/-/jobs/1484/retry",
- "title": "Retry",
- },
- "detailsPath": "/root/abcd-dag/-/jobs/1484",
- "group": "success",
- "hasDetails": true,
- "icon": "status_success",
- "id": "17",
- "label": "passed",
- "tooltip": "passed",
- },
- },
- ],
- "name": "build_c",
- "size": 1,
- "stageName": "build",
- "status": Object {
- "__typename": "DetailedStatus",
- "group": "success",
- "icon": "status_success",
- "id": "15",
- "label": "passed",
- },
- },
- Object {
- "__typename": "CiGroup",
- "id": "19",
- "jobs": Array [
- Object {
- "__typename": "CiJob",
- "id": "21",
- "kind": "BUILD",
- "name": "build_d 1/3",
- "needs": Array [],
- "previousStageJobsOrNeeds": Array [],
- "scheduledAt": null,
- "status": Object {
- "__typename": "DetailedStatus",
- "action": Object {
- "__typename": "StatusAction",
- "buttonTitle": "Retry this job",
- "icon": "retry",
- "id": "23",
- "path": "/root/abcd-dag/-/jobs/1485/retry",
- "title": "Retry",
- },
- "detailsPath": "/root/abcd-dag/-/jobs/1485",
- "group": "success",
- "hasDetails": true,
- "icon": "status_success",
- "id": "22",
- "label": "passed",
- "tooltip": "passed",
- },
- },
- Object {
- "__typename": "CiJob",
- "id": "24",
- "kind": "BUILD",
- "name": "build_d 2/3",
- "needs": Array [],
- "previousStageJobsOrNeeds": Array [],
- "scheduledAt": null,
- "status": Object {
- "__typename": "DetailedStatus",
- "action": Object {
- "__typename": "StatusAction",
- "buttonTitle": "Retry this job",
- "icon": "retry",
- "id": "26",
- "path": "/root/abcd-dag/-/jobs/1486/retry",
- "title": "Retry",
- },
- "detailsPath": "/root/abcd-dag/-/jobs/1486",
- "group": "success",
- "hasDetails": true,
- "icon": "status_success",
- "id": "25",
- "label": "passed",
- "tooltip": "passed",
- },
- },
- Object {
- "__typename": "CiJob",
- "id": "27",
- "kind": "BUILD",
- "name": "build_d 3/3",
- "needs": Array [],
- "previousStageJobsOrNeeds": Array [],
- "scheduledAt": null,
- "status": Object {
- "__typename": "DetailedStatus",
- "action": Object {
- "__typename": "StatusAction",
- "buttonTitle": "Retry this job",
- "icon": "retry",
- "id": "29",
- "path": "/root/abcd-dag/-/jobs/1487/retry",
- "title": "Retry",
- },
- "detailsPath": "/root/abcd-dag/-/jobs/1487",
- "group": "success",
- "hasDetails": true,
- "icon": "status_success",
- "id": "28",
- "label": "passed",
- "tooltip": "passed",
- },
- },
- ],
- "name": "build_d",
- "size": 3,
- "stageName": "build",
- "status": Object {
- "__typename": "DetailedStatus",
- "group": "success",
- "icon": "status_success",
- "id": "20",
- "label": "passed",
- },
- },
- Object {
- "__typename": "CiGroup",
- "id": "57",
- "jobs": Array [
- Object {
- "__typename": "CiJob",
- "id": "59",
- "kind": "BUILD",
- "name": "test_c",
- "needs": Array [],
- "previousStageJobsOrNeeds": Array [],
- "scheduledAt": null,
- "status": Object {
- "__typename": "DetailedStatus",
- "action": null,
- "detailsPath": "/root/kinder-pipe/-/pipelines/154",
- "group": "success",
- "hasDetails": true,
- "icon": "status_success",
- "id": "60",
- "label": null,
- "tooltip": null,
- },
- },
- ],
- "name": "test_c",
- "size": 1,
- "stageName": "test",
- "status": Object {
- "__typename": "DetailedStatus",
- "group": "success",
- "icon": "status_success",
- "id": "58",
- "label": null,
- },
- },
- ],
- "id": "layer-0",
- "name": "",
- "status": Object {
- "action": null,
- },
- },
- Object {
- "groups": Array [
- Object {
- "__typename": "CiGroup",
- "id": "32",
- "jobs": Array [
- Object {
- "__typename": "CiJob",
- "id": "34",
- "kind": "BUILD",
- "name": "test_a",
- "needs": Array [
- "build_c",
- "build_b",
- "build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
- ],
- "previousStageJobsOrNeeds": Array [
- "build_c",
- "build_b",
- "build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
- ],
- "scheduledAt": null,
- "status": Object {
- "__typename": "DetailedStatus",
- "action": Object {
- "__typename": "StatusAction",
- "buttonTitle": "Retry this job",
- "icon": "retry",
- "id": "36",
- "path": "/root/abcd-dag/-/jobs/1514/retry",
- "title": "Retry",
- },
- "detailsPath": "/root/abcd-dag/-/jobs/1514",
- "group": "success",
- "hasDetails": true,
- "icon": "status_success",
- "id": "35",
- "label": "passed",
- "tooltip": "passed",
- },
- },
- ],
- "name": "test_a",
- "size": 1,
- "stageName": "test",
- "status": Object {
- "__typename": "DetailedStatus",
- "group": "success",
- "icon": "status_success",
- "id": "33",
- "label": "passed",
- },
- },
- Object {
- "__typename": "CiGroup",
- "id": "40",
- "jobs": Array [
- Object {
- "__typename": "CiJob",
- "id": "42",
- "kind": "BUILD",
- "name": "test_b 1/2",
- "needs": Array [
- "build_d 3/3",
- "build_d 2/3",
- "build_d 1/3",
- "build_b",
- "build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
- ],
- "previousStageJobsOrNeeds": Array [
- "build_d 3/3",
- "build_d 2/3",
- "build_d 1/3",
- "build_b",
- "build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
- ],
- "scheduledAt": null,
- "status": Object {
- "__typename": "DetailedStatus",
- "action": Object {
- "__typename": "StatusAction",
- "buttonTitle": "Retry this job",
- "icon": "retry",
- "id": "44",
- "path": "/root/abcd-dag/-/jobs/1489/retry",
- "title": "Retry",
- },
- "detailsPath": "/root/abcd-dag/-/jobs/1489",
- "group": "success",
- "hasDetails": true,
- "icon": "status_success",
- "id": "43",
- "label": "passed",
- "tooltip": "passed",
- },
- },
- Object {
- "__typename": "CiJob",
- "id": "67",
- "kind": "BUILD",
- "name": "test_b 2/2",
- "needs": Array [
- "build_d 3/3",
- "build_d 2/3",
- "build_d 1/3",
- "build_b",
- "build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
- ],
- "previousStageJobsOrNeeds": Array [
- "build_d 3/3",
- "build_d 2/3",
- "build_d 1/3",
- "build_b",
- "build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
- ],
- "scheduledAt": null,
- "status": Object {
- "__typename": "DetailedStatus",
- "action": Object {
- "__typename": "StatusAction",
- "buttonTitle": "Retry this job",
- "icon": "retry",
- "id": "51",
- "path": "/root/abcd-dag/-/jobs/1490/retry",
- "title": "Retry",
- },
- "detailsPath": "/root/abcd-dag/-/jobs/1490",
- "group": "success",
- "hasDetails": true,
- "icon": "status_success",
- "id": "50",
- "label": "passed",
- "tooltip": "passed",
- },
- },
- ],
- "name": "test_b",
- "size": 2,
- "stageName": "test",
- "status": Object {
- "__typename": "DetailedStatus",
- "group": "success",
- "icon": "status_success",
- "id": "41",
- "label": "passed",
- },
- },
- Object {
- "__typename": "CiGroup",
- "id": "61",
- "jobs": Array [
- Object {
- "__typename": "CiJob",
- "id": "53",
- "kind": "BUILD",
- "name": "test_d",
- "needs": Array [
- "build_b",
- ],
- "previousStageJobsOrNeeds": Array [
- "build_b",
- ],
- "scheduledAt": null,
- "status": Object {
- "__typename": "DetailedStatus",
- "action": null,
- "detailsPath": "/root/abcd-dag/-/pipelines/153",
- "group": "success",
- "hasDetails": true,
- "icon": "status_success",
- "id": "64",
- "label": null,
- "tooltip": null,
- },
- },
- ],
- "name": "test_d",
- "size": 1,
- "stageName": "test",
- "status": Object {
- "__typename": "DetailedStatus",
- "group": "success",
- "icon": "status_success",
- "id": "62",
- "label": null,
- },
- },
- ],
- "id": "layer-1",
- "name": "",
- "status": Object {
- "action": null,
- },
- },
-]
-`;
diff --git a/spec/frontend/pipelines/graph/graph_component_spec.js b/spec/frontend/pipelines/graph/graph_component_spec.js
index 95207fd59ff..e9bce037800 100644
--- a/spec/frontend/pipelines/graph/graph_component_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_spec.js
@@ -1,4 +1,5 @@
import { shallowMount } from '@vue/test-utils';
+import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { LAYER_VIEW, STAGE_VIEW } from '~/pipelines/components/graph/constants';
import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
@@ -7,11 +8,8 @@ import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
import { calculatePipelineLayersInfo } from '~/pipelines/components/graph/utils';
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
-import {
- generateResponse,
- mockPipelineResponse,
- pipelineWithUpstreamDownstream,
-} from './mock_data';
+
+import { generateResponse, pipelineWithUpstreamDownstream } from './mock_data';
describe('graph component', () => {
let wrapper;
diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
index cc952eac1d7..9599b5e6b7b 100644
--- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
@@ -2,6 +2,7 @@ import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon, GlToggle } from '@gitl
import MockAdapter from 'axios-mock-adapter';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
+import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
@@ -26,7 +27,6 @@ import {
import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue';
import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.vue';
-import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
import * as Api from '~/pipelines/components/graph_shared/api';
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
import * as parsingUtils from '~/pipelines/components/parsing_utils';
@@ -34,7 +34,7 @@ import getPipelineHeaderData from '~/pipelines/graphql/queries/get_pipeline_head
import * as sentryUtils from '~/pipelines/utils';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { mockRunningPipelineHeaderData } from '../mock_data';
-import { mapCallouts, mockCalloutsResponse, mockPipelineResponse } from './mock_data';
+import { mapCallouts, mockCalloutsResponse } from './mock_data';
const defaultProvide = {
graphqlResourceEtag: 'frog/amphibirama/etag/',
@@ -55,8 +55,6 @@ describe('Pipeline graph wrapper', () => {
const findLinksLayer = () => wrapper.findComponent(LinksLayer);
const findGraph = () => wrapper.findComponent(PipelineGraph);
const findStageColumnTitle = () => wrapper.findByTestId('stage-column-title');
- const findAllStageColumnGroupsInColumn = () =>
- wrapper.findComponent(StageColumnComponent).findAll('[data-testid="stage-column-group"]');
const findViewSelector = () => wrapper.findComponent(GraphViewSelector);
const findViewSelectorToggle = () => findViewSelector().findComponent(GlToggle);
const findViewSelectorTrip = () => findViewSelector().findComponent(GlAlert);
@@ -316,12 +314,10 @@ describe('Pipeline graph wrapper', () => {
});
it('switches between views', async () => {
- const groupsInFirstColumn =
- mockPipelineResponse.data.project.pipeline.stages.nodes[0].groups.nodes.length;
- expect(findAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn);
- expect(findStageColumnTitle().text()).toBe('build');
+ expect(findStageColumnTitle().text()).toBe('deploy');
+
await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW);
- expect(findAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn + 1);
+
expect(findStageColumnTitle().text()).toBe('');
});
@@ -507,9 +503,9 @@ describe('Pipeline graph wrapper', () => {
});
describe('with metrics path', () => {
- const duration = 875;
- const numLinks = 7;
- const totalGroups = 8;
+ const duration = 500;
+ const numLinks = 3;
+ const totalGroups = 7;
const metricsData = {
histograms: [
{ name: PIPELINES_DETAIL_LINK_DURATION, value: duration / 1000 },
@@ -559,9 +555,6 @@ describe('Pipeline graph wrapper', () => {
createComponentWithApollo({
provide: {
metricsPath,
- glFeatures: {
- pipelineGraphLayersView: true,
- },
},
data: {
currentViewType: LAYER_VIEW,
diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
index 6e4b9498918..bcea140f2dd 100644
--- a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
+++ b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
@@ -1,6 +1,7 @@
import { mount, shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
+import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
@@ -15,11 +16,8 @@ import LinkedPipeline from '~/pipelines/components/graph/linked_pipeline.vue';
import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
import * as parsingUtils from '~/pipelines/components/parsing_utils';
import { LOAD_FAILURE } from '~/pipelines/constants';
-import {
- mockPipelineResponse,
- pipelineWithUpstreamDownstream,
- wrappedPipelineReturn,
-} from './mock_data';
+
+import { pipelineWithUpstreamDownstream, wrappedPipelineReturn } from './mock_data';
const processedPipeline = pipelineWithUpstreamDownstream(mockPipelineResponse);
diff --git a/spec/frontend/pipelines/graph/mock_data.js b/spec/frontend/pipelines/graph/mock_data.js
index 08624cc511d..b012e7f66e1 100644
--- a/spec/frontend/pipelines/graph/mock_data.js
+++ b/spec/frontend/pipelines/graph/mock_data.js
@@ -5,710 +5,6 @@ import {
RETRY_ACTION_TITLE,
} from '~/pipelines/components/graph/constants';
-export const mockPipelineResponse = {
- data: {
- project: {
- __typename: 'Project',
- id: '1',
- pipeline: {
- __typename: 'Pipeline',
- id: 163,
- iid: '22',
- complete: true,
- usesNeeds: true,
- downstream: null,
- upstream: null,
- userPermissions: {
- __typename: 'PipelinePermissions',
- updatePipeline: true,
- },
- stages: {
- __typename: 'CiStageConnection',
- nodes: [
- {
- __typename: 'CiStage',
- id: '2',
- name: 'build',
- status: {
- __typename: 'DetailedStatus',
- id: '3',
- action: null,
- },
- groups: {
- __typename: 'CiGroupConnection',
- nodes: [
- {
- __typename: 'CiGroup',
- id: '4',
- name: 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
- size: 1,
- status: {
- __typename: 'DetailedStatus',
- id: '5',
- label: 'passed',
- group: 'success',
- icon: 'status_success',
- },
- jobs: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiJob',
- id: '6',
- kind: BUILD_KIND,
- name: 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
- scheduledAt: null,
- status: {
- __typename: 'DetailedStatus',
- id: '7',
- icon: 'status_success',
- tooltip: 'passed',
- label: 'passed',
- hasDetails: true,
- detailsPath: '/root/abcd-dag/-/jobs/1482',
- group: 'success',
- action: {
- __typename: 'StatusAction',
- id: '8',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- path: '/root/abcd-dag/-/jobs/1482/retry',
- title: 'Retry',
- },
- },
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [],
- },
- },
- ],
- },
- },
- {
- __typename: 'CiGroup',
- name: 'build_b',
- id: '9',
- size: 1,
- status: {
- __typename: 'DetailedStatus',
- id: '10',
- label: 'passed',
- group: 'success',
- icon: 'status_success',
- },
- jobs: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiJob',
- id: '11',
- name: 'build_b',
- kind: BUILD_KIND,
- scheduledAt: null,
- status: {
- __typename: 'DetailedStatus',
- id: '12',
- icon: 'status_success',
- tooltip: 'passed',
- label: 'passed',
- hasDetails: true,
- detailsPath: '/root/abcd-dag/-/jobs/1515',
- group: 'success',
- action: {
- __typename: 'StatusAction',
- id: '13',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- path: '/root/abcd-dag/-/jobs/1515/retry',
- title: 'Retry',
- },
- },
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [],
- },
- },
- ],
- },
- },
- {
- __typename: 'CiGroup',
- id: '14',
- name: 'build_c',
- size: 1,
- status: {
- __typename: 'DetailedStatus',
- id: '15',
- label: 'passed',
- group: 'success',
- icon: 'status_success',
- },
- jobs: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiJob',
- id: '16',
- name: 'build_c',
- kind: BUILD_KIND,
- scheduledAt: null,
- status: {
- __typename: 'DetailedStatus',
- id: '17',
- icon: 'status_success',
- tooltip: 'passed',
- label: 'passed',
- hasDetails: true,
- detailsPath: '/root/abcd-dag/-/jobs/1484',
- group: 'success',
- action: {
- __typename: 'StatusAction',
- id: '18',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- path: '/root/abcd-dag/-/jobs/1484/retry',
- title: 'Retry',
- },
- },
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [],
- },
- },
- ],
- },
- },
- {
- __typename: 'CiGroup',
- id: '19',
- name: 'build_d',
- size: 3,
- status: {
- __typename: 'DetailedStatus',
- id: '20',
- label: 'passed',
- group: 'success',
- icon: 'status_success',
- },
- jobs: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiJob',
- id: '21',
- kind: BUILD_KIND,
- name: 'build_d 1/3',
- scheduledAt: null,
- status: {
- __typename: 'DetailedStatus',
- id: '22',
- icon: 'status_success',
- tooltip: 'passed',
- label: 'passed',
- hasDetails: true,
- detailsPath: '/root/abcd-dag/-/jobs/1485',
- group: 'success',
- action: {
- __typename: 'StatusAction',
- id: '23',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- path: '/root/abcd-dag/-/jobs/1485/retry',
- title: 'Retry',
- },
- },
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [],
- },
- },
- {
- __typename: 'CiJob',
- id: '24',
- kind: BUILD_KIND,
- name: 'build_d 2/3',
- scheduledAt: null,
- status: {
- __typename: 'DetailedStatus',
- id: '25',
- icon: 'status_success',
- tooltip: 'passed',
- label: 'passed',
- hasDetails: true,
- detailsPath: '/root/abcd-dag/-/jobs/1486',
- group: 'success',
- action: {
- __typename: 'StatusAction',
- id: '26',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- path: '/root/abcd-dag/-/jobs/1486/retry',
- title: 'Retry',
- },
- },
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [],
- },
- },
- {
- __typename: 'CiJob',
- id: '27',
- kind: BUILD_KIND,
- name: 'build_d 3/3',
- scheduledAt: null,
- status: {
- __typename: 'DetailedStatus',
- id: '28',
- icon: 'status_success',
- tooltip: 'passed',
- label: 'passed',
- hasDetails: true,
- detailsPath: '/root/abcd-dag/-/jobs/1487',
- group: 'success',
- action: {
- __typename: 'StatusAction',
- id: '29',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- path: '/root/abcd-dag/-/jobs/1487/retry',
- title: 'Retry',
- },
- },
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [],
- },
- },
- ],
- },
- },
- ],
- },
- },
- {
- __typename: 'CiStage',
- id: '30',
- name: 'test',
- status: {
- __typename: 'DetailedStatus',
- id: '31',
- action: null,
- },
- groups: {
- __typename: 'CiGroupConnection',
- nodes: [
- {
- __typename: 'CiGroup',
- id: '32',
- name: 'test_a',
- size: 1,
- status: {
- __typename: 'DetailedStatus',
- id: '33',
- label: 'passed',
- group: 'success',
- icon: 'status_success',
- },
- jobs: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiJob',
- id: '34',
- kind: BUILD_KIND,
- name: 'test_a',
- scheduledAt: null,
- status: {
- __typename: 'DetailedStatus',
- id: '35',
- icon: 'status_success',
- tooltip: 'passed',
- label: 'passed',
- hasDetails: true,
- detailsPath: '/root/abcd-dag/-/jobs/1514',
- group: 'success',
- action: {
- __typename: 'StatusAction',
- id: '36',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- path: '/root/abcd-dag/-/jobs/1514/retry',
- title: 'Retry',
- },
- },
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [
- {
- __typename: 'CiBuildNeed',
- id: '37',
- name: 'build_c',
- },
- {
- __typename: 'CiBuildNeed',
- id: '38',
- name: 'build_b',
- },
- {
- __typename: 'CiBuildNeed',
- id: '39',
- name:
- 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
- },
- ],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiBuildNeed',
- id: '37',
- name: 'build_c',
- },
- {
- __typename: 'CiBuildNeed',
- id: '38',
- name: 'build_b',
- },
- {
- __typename: 'CiBuildNeed',
- id: '39',
- name:
- 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
- },
- ],
- },
- },
- ],
- },
- },
- {
- __typename: 'CiGroup',
- id: '40',
- name: 'test_b',
- size: 2,
- status: {
- __typename: 'DetailedStatus',
- id: '41',
- label: 'passed',
- group: 'success',
- icon: 'status_success',
- },
- jobs: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiJob',
- id: '42',
- kind: BUILD_KIND,
- name: 'test_b 1/2',
- scheduledAt: null,
- status: {
- __typename: 'DetailedStatus',
- id: '43',
- icon: 'status_success',
- tooltip: 'passed',
- label: 'passed',
- hasDetails: true,
- detailsPath: '/root/abcd-dag/-/jobs/1489',
- group: 'success',
- action: {
- __typename: 'StatusAction',
- id: '44',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- path: '/root/abcd-dag/-/jobs/1489/retry',
- title: 'Retry',
- },
- },
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [
- {
- __typename: 'CiBuildNeed',
- id: '45',
- name: 'build_d 3/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '46',
- name: 'build_d 2/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '47',
- name: 'build_d 1/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '48',
- name: 'build_b',
- },
- {
- __typename: 'CiBuildNeed',
- id: '49',
- name:
- 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
- },
- ],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiBuildNeed',
- id: '45',
- name: 'build_d 3/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '46',
- name: 'build_d 2/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '47',
- name: 'build_d 1/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '48',
- name: 'build_b',
- },
- {
- __typename: 'CiBuildNeed',
- id: '49',
- name:
- 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
- },
- ],
- },
- },
- {
- __typename: 'CiJob',
- id: '67',
- kind: BUILD_KIND,
- name: 'test_b 2/2',
- scheduledAt: null,
- status: {
- __typename: 'DetailedStatus',
- id: '50',
- icon: 'status_success',
- tooltip: 'passed',
- label: 'passed',
- hasDetails: true,
- detailsPath: '/root/abcd-dag/-/jobs/1490',
- group: 'success',
- action: {
- __typename: 'StatusAction',
- id: '51',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- path: '/root/abcd-dag/-/jobs/1490/retry',
- title: 'Retry',
- },
- },
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [
- {
- __typename: 'CiBuildNeed',
- id: '52',
- name: 'build_d 3/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '53',
- name: 'build_d 2/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '54',
- name: 'build_d 1/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '55',
- name: 'build_b',
- },
- {
- __typename: 'CiBuildNeed',
- id: '56',
- name:
- 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
- },
- ],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiBuildNeed',
- id: '52',
- name: 'build_d 3/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '53',
- name: 'build_d 2/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '54',
- name: 'build_d 1/3',
- },
- {
- __typename: 'CiBuildNeed',
- id: '55',
- name: 'build_b',
- },
- {
- __typename: 'CiBuildNeed',
- id: '56',
- name:
- 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
- },
- ],
- },
- },
- ],
- },
- },
- {
- __typename: 'CiGroup',
- name: 'test_c',
- id: '57',
- size: 1,
- status: {
- __typename: 'DetailedStatus',
- id: '58',
- label: null,
- group: 'success',
- icon: 'status_success',
- },
- jobs: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiJob',
- id: '59',
- kind: BUILD_KIND,
- name: 'test_c',
- scheduledAt: null,
- status: {
- __typename: 'DetailedStatus',
- id: '60',
- icon: 'status_success',
- tooltip: null,
- label: null,
- hasDetails: true,
- detailsPath: '/root/kinder-pipe/-/pipelines/154',
- group: 'success',
- action: null,
- },
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [],
- },
- },
- ],
- },
- },
- {
- __typename: 'CiGroup',
- id: '61',
- name: 'test_d',
- size: 1,
- status: {
- id: '62',
- __typename: 'DetailedStatus',
- label: null,
- group: 'success',
- icon: 'status_success',
- },
- jobs: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiJob',
- id: '53',
- kind: BUILD_KIND,
- name: 'test_d',
- scheduledAt: null,
- status: {
- __typename: 'DetailedStatus',
- id: '64',
- icon: 'status_success',
- tooltip: null,
- label: null,
- hasDetails: true,
- detailsPath: '/root/abcd-dag/-/pipelines/153',
- group: 'success',
- action: null,
- },
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [
- {
- __typename: 'CiBuildNeed',
- id: '65',
- name: 'build_b',
- },
- ],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiBuildNeed',
- id: '65',
- name: 'build_b',
- },
- ],
- },
- },
- ],
- },
- },
- ],
- },
- },
- ],
- },
- },
- },
- },
-};
-
export const downstream = {
nodes: [
{
diff --git a/spec/frontend/pipelines/graph_shared/links_layer_spec.js b/spec/frontend/pipelines/graph_shared/links_layer_spec.js
index 9d39c86ed5e..88ba84c395a 100644
--- a/spec/frontend/pipelines/graph_shared/links_layer_spec.js
+++ b/spec/frontend/pipelines/graph_shared/links_layer_spec.js
@@ -1,7 +1,9 @@
import { shallowMount } from '@vue/test-utils';
+import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
import LinksInner from '~/pipelines/components/graph_shared/links_inner.vue';
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
-import { generateResponse, mockPipelineResponse } from '../graph/mock_data';
+
+import { generateResponse } from '../graph/mock_data';
describe('links layer component', () => {
let wrapper;
diff --git a/spec/frontend/pipelines/utils_spec.js b/spec/frontend/pipelines/utils_spec.js
index 51e0e0705ff..286d79edc6c 100644
--- a/spec/frontend/pipelines/utils_spec.js
+++ b/spec/frontend/pipelines/utils_spec.js
@@ -1,3 +1,4 @@
+import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
import { createSankey } from '~/pipelines/components/dag/drawing_utils';
import {
makeLinksFromNodes,
@@ -14,7 +15,7 @@ import { createNodeDict } from '~/pipelines/utils';
import { mockDownstreamPipelinesRest } from '../vue_merge_request_widget/mock_data';
import { mockDownstreamPipelinesGraphql } from '../commit/mock_data';
import { mockParsedGraphQLNodes, missingJob } from './components/dag/mock_data';
-import { generateResponse, mockPipelineResponse } from './graph/mock_data';
+import { generateResponse } from './graph/mock_data';
describe('DAG visualization parsing utilities', () => {
const nodeDict = createNodeDict(mockParsedGraphQLNodes);
@@ -152,14 +153,6 @@ describe('DAG visualization parsing utilities', () => {
});
});
});
-
- /*
- Just as a fallback in case multiple functions change, so tests pass
- but the implementation moves away from case.
- */
- it('matches the snapshot', () => {
- expect(columns).toMatchSnapshot();
- });
});
});
diff --git a/spec/lib/api/helpers/packages/npm_spec.rb b/spec/lib/api/helpers/packages/npm_spec.rb
index e1316a10fb1..cfb68d2c53e 100644
--- a/spec/lib/api/helpers/packages/npm_spec.rb
+++ b/spec/lib/api/helpers/packages/npm_spec.rb
@@ -17,20 +17,9 @@ RSpec.describe ::API::Helpers::Packages::Npm, feature_category: :package_registr
let_it_be(:project) { create(:project, :public, namespace: namespace) }
let_it_be(:package) { create(:npm_package, project: project) }
- describe '#endpoint_scope' do
- subject { object.endpoint_scope }
-
- context 'when params includes an id' do
- let(:params) { { id: 42, package_name: 'foo' } }
-
- it { is_expected.to eq(:project) }
- end
-
- context 'when params does not include an id' do
- let(:params) { { package_name: 'foo' } }
-
- it { is_expected.to eq(:instance) }
- end
+ before do
+ allow(object).to receive(:endpoint_scope).and_return(endpoint_scope)
+ allow(object).to receive(:current_user).and_return(user)
end
describe '#finder_for_endpoint_scope' do
@@ -40,6 +29,7 @@ RSpec.describe ::API::Helpers::Packages::Npm, feature_category: :package_registr
context 'when called with project scope' do
let(:params) { { id: project.id } }
+ let(:endpoint_scope) { :project }
it 'returns a PackageFinder for project scope' do
expect(::Packages::Npm::PackageFinder).to receive(:new).with(package_name, project: project)
@@ -50,6 +40,7 @@ RSpec.describe ::API::Helpers::Packages::Npm, feature_category: :package_registr
context 'when called with instance scope' do
let(:params) { { package_name: package_name } }
+ let(:endpoint_scope) { :instance }
it 'returns a PackageFinder for namespace scope' do
expect(::Packages::Npm::PackageFinder).to receive(:new).with(package_name, namespace: group)
@@ -57,6 +48,17 @@ RSpec.describe ::API::Helpers::Packages::Npm, feature_category: :package_registr
subject
end
end
+
+ context 'when called with group scope' do
+ let(:params) { { id: group.id } }
+ let(:endpoint_scope) { :group }
+
+ it 'returns a PackageFinder for group scope' do
+ expect(::Packages::Npm::PackageFinder).to receive(:new).with(package_name, namespace: group)
+
+ subject
+ end
+ end
end
describe '#project_id_or_nil' do
@@ -64,11 +66,21 @@ RSpec.describe ::API::Helpers::Packages::Npm, feature_category: :package_registr
context 'when called with project scope' do
let(:params) { { id: project.id } }
+ let(:endpoint_scope) { :project }
it { is_expected.to eq(project.id) }
end
- context 'when called with namespace scope' do
+ context 'when called with group scope' do
+ let(:params) { { id: group.id, package_name: package.name } }
+ let(:endpoint_scope) { :group }
+
+ it { is_expected.to eq(project.id) }
+ end
+
+ context 'when called with instance scope' do
+ let(:endpoint_scope) { :instance }
+
context 'when given an unscoped name' do
let(:params) { { package_name: 'foo' } }
diff --git a/spec/migrations/add_type_to_http_integrations_spec.rb b/spec/migrations/add_type_to_http_integrations_spec.rb
new file mode 100644
index 00000000000..8238c1594dc
--- /dev/null
+++ b/spec/migrations/add_type_to_http_integrations_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddTypeToHttpIntegrations, feature_category: :incident_management do
+ let(:integrations) { table(:alert_management_http_integrations) }
+
+ it 'correctly migrates up and down' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(integrations.column_names).not_to include('type_identifier')
+ }
+
+ migration.after -> {
+ integrations.reset_column_information
+ expect(integrations.column_names).to include('type_identifier')
+ }
+ end
+ end
+end
diff --git a/spec/models/alert_management/http_integration_spec.rb b/spec/models/alert_management/http_integration_spec.rb
index b453b3a82e0..606b53aeacd 100644
--- a/spec/models/alert_management/http_integration_spec.rb
+++ b/spec/models/alert_management/http_integration_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AlertManagement::HttpIntegration do
+RSpec.describe AlertManagement::HttpIntegration, feature_category: :incident_management do
include ::Gitlab::Routing.url_helpers
let_it_be(:project) { create(:project) }
@@ -21,6 +21,7 @@ RSpec.describe AlertManagement::HttpIntegration do
describe 'validations' do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:name) }
+ it { is_expected.to validate_presence_of(:type_identifier) }
it { is_expected.to validate_length_of(:name).is_at_most(255) }
context 'when active' do
@@ -86,6 +87,66 @@ RSpec.describe AlertManagement::HttpIntegration do
end
end
+ describe 'scopes' do
+ let_it_be(:integration_1) { create(:alert_management_http_integration) }
+ let_it_be(:integration_2) { create(:alert_management_http_integration, :inactive, project: project) }
+ let_it_be(:integration_3) { create(:alert_management_http_integration, :prometheus, project: project) }
+ let_it_be(:integration_4) { create(:alert_management_http_integration, :legacy, :inactive) }
+
+ describe '.for_endpoint_identifier' do
+ let(:identifier) { integration_1.endpoint_identifier }
+
+ subject { described_class.for_endpoint_identifier(identifier) }
+
+ it { is_expected.to contain_exactly(integration_1) }
+ end
+
+ describe '.for_type' do
+ let(:type) { :prometheus }
+
+ subject { described_class.for_type(type) }
+
+ it { is_expected.to contain_exactly(integration_3) }
+ end
+
+ describe '.for_project' do
+ let(:project) { integration_2.project }
+
+ subject { described_class.for_project(project) }
+
+ it { is_expected.to contain_exactly(integration_2, integration_3) }
+
+ context 'with project_ids array' do
+ let(:project) { [integration_1.project_id] }
+
+ it { is_expected.to contain_exactly(integration_1) }
+ end
+ end
+
+ describe '.active' do
+ subject { described_class.active }
+
+ it { is_expected.to contain_exactly(integration_1, integration_3) }
+ end
+
+ describe '.legacy' do
+ subject { described_class.legacy }
+
+ it { is_expected.to contain_exactly(integration_4) }
+ end
+
+ describe '.ordered_by_type_and_id' do
+ before do
+ # Rearrange cache by saving to avoid false-positives
+ integration_2.touch
+ end
+
+ subject { described_class.ordered_by_type_and_id }
+
+ it { is_expected.to eq([integration_1, integration_2, integration_4, integration_3]) }
+ end
+ end
+
describe 'before validation' do
describe '#ensure_payload_example_not_nil' do
subject(:integration) { build(:alert_management_http_integration, payload_example: payload_example) }
@@ -230,5 +291,33 @@ RSpec.describe AlertManagement::HttpIntegration do
)
end
end
+
+ context 'for a prometheus integration' do
+ let(:integration) { build(:alert_management_http_integration, :prometheus) }
+
+ it do
+ is_expected.to eq(
+ project_alert_http_integration_url(
+ integration.project,
+ 'datadog',
+ integration.endpoint_identifier,
+ format: :json
+ )
+ )
+ end
+
+ context 'for a legacy integration' do
+ let(:integration) { build(:alert_management_http_integration, :prometheus, :legacy) }
+
+ it do
+ is_expected.to eq(
+ notify_project_prometheus_alerts_url(
+ integration.project,
+ format: :json
+ )
+ )
+ end
+ end
+ end
end
end
diff --git a/spec/requests/api/npm_group_packages_spec.rb b/spec/requests/api/npm_group_packages_spec.rb
new file mode 100644
index 00000000000..888ce548e6d
--- /dev/null
+++ b/spec/requests/api/npm_group_packages_spec.rb
@@ -0,0 +1,198 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::NpmGroupPackages, feature_category: :package_registry do
+ using RSpec::Parameterized::TableSyntax
+
+ include_context 'npm api setup'
+
+ describe 'GET /api/v4/groups/:id/-/packages/npm/*package_name' do
+ let(:url) { api("/groups/#{group.id}/-/packages/npm/#{package_name}") }
+
+ it_behaves_like 'handling get metadata requests', scope: :group
+
+ context 'with a duplicate package name in another project' do
+ subject { get(url) }
+
+ before do
+ group.add_developer(user)
+ end
+
+ let_it_be(:project2) { create(:project, :public, namespace: namespace) }
+ let_it_be(:package2) do
+ create(:npm_package,
+ project: project2,
+ name: "@#{group.path}/scoped_package",
+ version: '1.2.0')
+ end
+
+ it 'includes all matching package versions in the response' do
+ subject
+
+ expect(json_response['versions'].keys).to match_array([package.version, package2.version])
+ end
+
+ context 'with the feature flag disabled' do
+ before do
+ stub_feature_flags(npm_allow_packages_in_multiple_projects: false)
+ end
+
+ it 'returns matching package versions from only one project' do
+ subject
+
+ expect(json_response['versions'].keys).to match_array([package2.version])
+ end
+ end
+ end
+
+ context 'with mixed group and project visibilities' do
+ subject { get(url, headers: headers) }
+
+ where(:auth, :group_visibility, :project_visibility, :user_role, :expected_status) do
+ nil | :public | :public | nil | :ok
+ nil | :public | :internal | nil | :not_found
+ nil | :public | :private | nil | :not_found
+ nil | :internal | :internal | nil | :not_found
+ nil | :internal | :private | nil | :not_found
+ nil | :private | :private | nil | :not_found
+
+ :oauth | :public | :public | :guest | :ok
+ :oauth | :public | :internal | :guest | :ok
+ :oauth | :public | :private | :guest | :forbidden
+ :oauth | :internal | :internal | :guest | :ok
+ :oauth | :internal | :private | :guest | :forbidden
+ :oauth | :private | :private | :guest | :forbidden
+ :oauth | :public | :public | :reporter | :ok
+ :oauth | :public | :internal | :reporter | :ok
+ :oauth | :public | :private | :reporter | :ok
+ :oauth | :internal | :internal | :reporter | :ok
+ :oauth | :internal | :private | :reporter | :ok
+ :oauth | :private | :private | :reporter | :ok
+
+ :personal_access_token | :public | :public | :guest | :ok
+ :personal_access_token | :public | :internal | :guest | :ok
+ :personal_access_token | :public | :private | :guest | :forbidden
+ :personal_access_token | :internal | :internal | :guest | :ok
+ :personal_access_token | :internal | :private | :guest | :forbidden
+ :personal_access_token | :private | :private | :guest | :forbidden
+ :personal_access_token | :public | :public | :reporter | :ok
+ :personal_access_token | :public | :internal | :reporter | :ok
+ :personal_access_token | :public | :private | :reporter | :ok
+ :personal_access_token | :internal | :internal | :reporter | :ok
+ :personal_access_token | :internal | :private | :reporter | :ok
+ :personal_access_token | :private | :private | :reporter | :ok
+
+ :job_token | :public | :public | :developer | :ok
+ :job_token | :public | :internal | :developer | :ok
+ :job_token | :public | :private | :developer | :ok
+ :job_token | :internal | :internal | :developer | :ok
+ :job_token | :internal | :private | :developer | :ok
+ :job_token | :private | :private | :developer | :ok
+
+ :deploy_token | :public | :public | nil | :ok
+ :deploy_token | :public | :internal | nil | :ok
+ :deploy_token | :public | :private | nil | :ok
+ :deploy_token | :internal | :internal | nil | :ok
+ :deploy_token | :internal | :private | nil | :ok
+ :deploy_token | :private | :private | nil | :ok
+ end
+
+ with_them do
+ let(:headers) do
+ case auth
+ when :oauth
+ build_token_auth_header(token.plaintext_token)
+ when :personal_access_token
+ build_token_auth_header(personal_access_token.token)
+ when :job_token
+ build_token_auth_header(job.token)
+ when :deploy_token
+ build_token_auth_header(deploy_token.token)
+ else
+ {}
+ end
+ end
+
+ before do
+ project.update!(visibility: project_visibility.to_s)
+ project.send("add_#{user_role}", user) if user_role
+ group.update!(visibility: group_visibility.to_s)
+ group.send("add_#{user_role}", user) if user_role
+ end
+
+ it_behaves_like 'returning response status', params[:expected_status]
+ end
+ end
+
+ context 'when user is a reporter of project but is not a direct member of group' do
+ subject { get(url, headers: headers) }
+
+ where(:group_visibility, :project_visibility, :expected_status) do
+ :public | :public | :ok
+ :public | :internal | :ok
+ :public | :private | :ok
+ :internal | :internal | :ok
+ :internal | :private | :ok
+ :private | :private | :ok
+ end
+
+ with_them do
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+
+ before do
+ project.update!(visibility: project_visibility.to_s)
+ project.add_reporter(user)
+
+ group.update!(visibility: group_visibility.to_s)
+ end
+
+ it_behaves_like 'returning response status', params[:expected_status]
+ end
+ end
+ end
+
+ describe 'GET /api/v4/packages/npm/-/package/*package_name/dist-tags' do
+ let(:url) { api("/groups/#{group.id}/-/packages/npm/-/package/#{package_name}/dist-tags") }
+
+ subject { get(url) }
+
+ it_behaves_like 'returning response status', :not_found
+ end
+
+ describe 'PUT /api/v4/packages/npm/-/package/*package_name/dist-tags/:tag' do
+ let(:tag_name) { 'test' }
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+ let(:url) { api("/groups/#{group.id}/-/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
+
+ subject { put(url, headers: headers) }
+
+ it_behaves_like 'returning response status', :not_found
+ end
+
+ describe 'DELETE /api/v4/packages/npm/-/package/*package_name/dist-tags/:tag' do
+ let(:tag_name) { 'test' }
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+ let(:url) { api("/groups/#{group.id}/-/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
+
+ subject { delete(url, headers: headers) }
+
+ it_behaves_like 'returning response status', :not_found
+ end
+
+ describe 'POST /api/v4/groups/:id/-/packages/npm/-/npm/v1/security/advisories/bulk' do
+ let(:url) { api("/groups/#{group.id}/-/packages/npm/-/npm/v1/security/advisories/bulk") }
+
+ subject { post(url) }
+
+ it_behaves_like 'returning response status', :not_found
+ end
+
+ describe 'POST /api/v4/groups/:id/-/packages/npm/-/npm/v1/security/audits/quick' do
+ let(:url) { api("/groups/#{group.id}/-/packages/npm/-/npm/v1/security/audits/quick") }
+
+ subject { post(url) }
+
+ it_behaves_like 'returning response status', :not_found
+ end
+end
diff --git a/spec/services/alert_management/http_integrations/create_service_spec.rb b/spec/services/alert_management/http_integrations/create_service_spec.rb
index 5200ec27dd1..bced09044eb 100644
--- a/spec/services/alert_management/http_integrations/create_service_spec.rb
+++ b/spec/services/alert_management/http_integrations/create_service_spec.rb
@@ -38,12 +38,6 @@ RSpec.describe AlertManagement::HttpIntegrations::CreateService, feature_categor
it_behaves_like 'error response', 'You have insufficient permissions to create an HTTP integration for this project'
end
- context 'when an integration already exists' do
- let_it_be(:existing_integration) { create(:alert_management_http_integration, project: project) }
-
- it_behaves_like 'error response', 'Multiple HTTP integrations are not supported for this project'
- end
-
context 'when an error occurs during update' do
it_behaves_like 'error response', "Name can't be blank"
end
@@ -61,6 +55,38 @@ RSpec.describe AlertManagement::HttpIntegrations::CreateService, feature_categor
expect(integration.token).to be_present
expect(integration.endpoint_identifier).to be_present
end
+
+ context 'with an existing HTTP integration' do
+ let_it_be(:http_integration) { create(:alert_management_http_integration, project: project) }
+
+ it_behaves_like 'error response', 'Multiple integrations of a single type are not supported for this project'
+
+ context 'when creating a different type of integration' do
+ let(:params) { { type_identifier: :prometheus, name: 'Prometheus' } }
+
+ it 'is successful' do
+ expect(response).to be_success
+ expect(response.payload[:integration]).to be_a(::AlertManagement::HttpIntegration)
+ end
+ end
+ end
+
+ context 'with an existing Prometheus integration' do
+ let_it_be(:http_integration) { create(:alert_management_prometheus_integration, project: project) }
+
+ context 'when creating a different type of integration' do
+ it 'is successful' do
+ expect(response).to be_success
+ expect(response.payload[:integration]).to be_a(::AlertManagement::HttpIntegration)
+ end
+ end
+
+ context 'when creating the same time of integration' do
+ let(:params) { { type_identifier: :prometheus, name: 'Prometheus' } }
+
+ it_behaves_like 'error response', 'Multiple integrations of a single type are not supported for this project'
+ end
+ end
end
end
end
diff --git a/spec/services/alert_management/http_integrations/destroy_service_spec.rb b/spec/services/alert_management/http_integrations/destroy_service_spec.rb
index a8e9746cb85..e3d9ddfbad8 100644
--- a/spec/services/alert_management/http_integrations/destroy_service_spec.rb
+++ b/spec/services/alert_management/http_integrations/destroy_service_spec.rb
@@ -47,6 +47,13 @@ RSpec.describe AlertManagement::HttpIntegrations::DestroyService, feature_catego
it_behaves_like 'error response', 'Name cannot be removed'
end
+ context 'when destroying a legacy Prometheus integration' do
+ let_it_be(:existing_integration) { create(:alert_management_prometheus_integration, :legacy, project: project) }
+ let!(:integration) { existing_integration }
+
+ it_behaves_like 'error response', 'Legacy Prometheus integrations cannot currently be removed'
+ end
+
it 'successfully returns the integration' do
expect(response).to be_success
diff --git a/spec/services/projects/prometheus/alerts/notify_service_spec.rb b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
index 0feac6c3e72..24affa45aa5 100644
--- a/spec/services/projects/prometheus/alerts/notify_service_spec.rb
+++ b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Prometheus::Alerts::NotifyService, feature_category: :metrics do
+RSpec.describe Projects::Prometheus::Alerts::NotifyService, feature_category: :incident_management do
include PrometheusHelpers
using RSpec::Parameterized::TableSyntax
@@ -163,6 +163,24 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService, feature_category: :m
raise "invalid result: #{result.inspect}"
end
end
+
+ context 'with simultaneous manual configuration' do
+ let_it_be(:integration) { create(:alert_management_prometheus_integration, :legacy, project: project) }
+ let_it_be(:old_prometheus_integration) { create(:prometheus_integration, project: project) }
+ let_it_be(:alerting_setting) { create(:project_alerting_setting, project: project, token: integration.token) }
+
+ subject { service.execute(integration.token, integration) }
+
+ it_behaves_like 'processes one firing and one resolved prometheus alerts'
+
+ context 'when HTTP integration is inactive' do
+ before do
+ integration.update!(active: false)
+ end
+
+ it_behaves_like 'alerts service responds with an error and takes no actions', :unauthorized
+ end
+ end
end
context 'incident settings' do
diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb
index 403456fa48e..417bf4366c5 100644
--- a/spec/support/helpers/javascript_fixtures_helpers.rb
+++ b/spec/support/helpers/javascript_fixtures_helpers.rb
@@ -46,9 +46,8 @@ module JavaScriptFixturesHelpers
#
# query_path - file path to the GraphQL query, relative to `app/assets/javascripts`.
# ee - boolean, when true `query_path` will be looked up in `/ee`.
- def get_graphql_query_as_string(query_path, ee: false)
- base = (ee ? 'ee/' : '') + 'app/assets/javascripts'
-
+ def get_graphql_query_as_string(query_path, ee: false, with_base_path: true)
+ base = (ee ? 'ee/' : '') + (with_base_path ? 'app/assets/javascripts' : '')
path = Rails.root / base / query_path
queries = Gitlab::Graphql::Queries.find(path)
if queries.length == 1
diff --git a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
index f53532d00d7..d1712a3c02a 100644
--- a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
@@ -259,8 +259,13 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project|
before do
project.send("add_#{user_role}", user) if user_role
project.update!(visibility: visibility.to_s)
+
+ group.send("add_#{user_role}", user) if user_role && scope == :group
+ group.update!(visibility: visibility.to_s) if scope == :group
+
package.update!(name: package_name) unless package_name == 'non-existing-package'
- if scope == :instance
+
+ if %i[instance group].include?(scope)
allow_fetch_application_setting(attribute: "npm_package_requests_forwarding", return_value: request_forward)
else
allow_fetch_cascade_application_setting(attribute: "npm_package_requests_forwarding", return_value: request_forward)
@@ -280,6 +285,8 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project|
end
end
+ status = :not_found if scope == :group && params[:package_name_type] == :non_existing && !params[:request_forward]
+
it_behaves_like example_name, status: status
end
end
@@ -300,6 +307,7 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project|
let(:headers) { build_token_auth_header(personal_access_token.token) }
before do
+ group.add_developer(user) if scope == :group
project.add_developer(user)
end