diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-05 18:08:48 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-05 18:08:48 +0300 |
commit | 205b6baf2677879c35968d2b659225b58e8a1227 (patch) | |
tree | 10ed06185aae2f6ed6e7c61349a92acab605daca /spec | |
parent | f34077e88198da754b4efecd1ce1d996ce982286 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
31 files changed, 306 insertions, 734 deletions
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index 8070e17b7af..bd13f86034a 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -114,11 +114,17 @@ RSpec.describe 'Database schema' do context 'all foreign keys' do # for index to be effective, the FK constraint has to be at first place it 'are indexed' do - first_indexed_column = indexes.map(&:columns).map do |columns| + first_indexed_column = indexes.filter_map do |index| + columns = index.columns + # In cases of complex composite indexes, a string is returned eg: # "lower((extern_uid)::text), group_id" columns = columns.split(',') if columns.is_a?(String) - columns.first.chomp + column = columns.first.chomp + + # A partial index is not suitable for a foreign key column, unless + # the only condition is for the presence of the foreign key itself + column if index.where.nil? || index.where == "(#{column} IS NOT NULL)" end foreign_keys_columns = all_foreign_keys.map(&:column) required_indexed_columns = foreign_keys_columns - ignored_index_columns(table) diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb index 919b45e57e2..6a21df943f5 100644 --- a/spec/factories/clusters/applications/helm.rb +++ b/spec/factories/clusters/applications/helm.rb @@ -103,10 +103,6 @@ FactoryBot.define do cluster factory: %i(cluster with_installed_helm provided_by_gcp) end - factory :clusters_applications_elastic_stack, class: 'Clusters::Applications::ElasticStack' do - cluster factory: %i(cluster with_installed_helm provided_by_gcp) - end - factory :clusters_applications_crossplane, class: 'Clusters::Applications::Crossplane' do stack { 'gcp' } cluster factory: %i(cluster with_installed_helm provided_by_gcp) diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb index 7666533691e..72424a3c321 100644 --- a/spec/factories/clusters/clusters.rb +++ b/spec/factories/clusters/clusters.rb @@ -100,7 +100,6 @@ FactoryBot.define do application_runner factory: %i(clusters_applications_runner installed) application_jupyter factory: %i(clusters_applications_jupyter installed) application_knative factory: %i(clusters_applications_knative installed) - application_elastic_stack factory: %i(clusters_applications_elastic_stack installed) application_cilium factory: %i(clusters_applications_cilium installed) end diff --git a/spec/factories/clusters/integrations/elastic_stack.rb b/spec/factories/clusters/integrations/elastic_stack.rb deleted file mode 100644 index 1ab3256845b..00000000000 --- a/spec/factories/clusters/integrations/elastic_stack.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :clusters_integrations_elastic_stack, class: 'Clusters::Integrations::ElasticStack' do - cluster factory: %i(cluster provided_by_gcp) - enabled { true } - - trait :disabled do - enabled { false } - end - end -end diff --git a/spec/factories/import_states.rb b/spec/factories/import_states.rb index 4dca78b1059..0c73082be57 100644 --- a/spec/factories/import_states.rb +++ b/spec/factories/import_states.rb @@ -34,6 +34,10 @@ FactoryBot.define do status { :failed } end + trait :canceled do + status { :canceled } + end + after(:create) do |import_state, evaluator| columns = {} columns[:import_url] = evaluator.import_url unless evaluator.import_url.blank? diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 2ed71975d0c..76fa0c4c047 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -151,6 +151,10 @@ FactoryBot.define do import_status { :failed } end + trait :import_canceled do + import_status { :canceled } + end + trait :jira_dvcs_cloud do before(:create) do |project| create(:project_feature_usage, :dvcs_cloud, project: project) diff --git a/spec/finders/groups/user_groups_finder_spec.rb b/spec/finders/groups/user_groups_finder_spec.rb index a4a9b8d16d0..9339741da79 100644 --- a/spec/finders/groups/user_groups_finder_spec.rb +++ b/spec/finders/groups/user_groups_finder_spec.rb @@ -9,6 +9,7 @@ RSpec.describe Groups::UserGroupsFinder do let_it_be(:private_maintainer_group) { create(:group, :private, name: 'b private maintainer', path: 'b-private-maintainer') } let_it_be(:public_developer_group) { create(:group, project_creation_level: nil, name: 'c public developer', path: 'c-public-developer') } let_it_be(:public_maintainer_group) { create(:group, name: 'a public maintainer', path: 'a-public-maintainer') } + let_it_be(:public_owner_group) { create(:group, name: 'a public owner', path: 'a-public-owner') } subject { described_class.new(current_user, target_user, arguments).execute } @@ -21,12 +22,14 @@ RSpec.describe Groups::UserGroupsFinder do private_maintainer_group.add_maintainer(user) public_developer_group.add_developer(user) public_maintainer_group.add_maintainer(user) + public_owner_group.add_owner(user) end it 'returns all groups where the user is a direct member' do is_expected.to match( [ public_maintainer_group, + public_owner_group, private_maintainer_group, public_developer_group, guest_group @@ -53,6 +56,7 @@ RSpec.describe Groups::UserGroupsFinder do is_expected.to match( [ public_maintainer_group, + public_owner_group, private_maintainer_group, public_developer_group ] @@ -73,6 +77,32 @@ RSpec.describe Groups::UserGroupsFinder do end end + context 'when permission is :transfer_projects' do + let(:arguments) { { permission_scope: :transfer_projects } } + + specify do + is_expected.to match( + [ + public_maintainer_group, + public_owner_group, + private_maintainer_group + ] + ) + end + + context 'when search is provided' do + let(:arguments) { { permission_scope: :transfer_projects, search: 'owner' } } + + specify do + is_expected.to match( + [ + public_owner_group + ] + ) + end + end + end + context 'when search is provided' do let(:arguments) { { search: 'maintainer' } } diff --git a/spec/frontend/fixtures/jobs.rb b/spec/frontend/fixtures/jobs.rb index c76b06bd39e..2e15eefdce6 100644 --- a/spec/frontend/fixtures/jobs.rb +++ b/spec/frontend/fixtures/jobs.rb @@ -35,13 +35,21 @@ RSpec.describe 'Jobs (JavaScript fixtures)' do end describe GraphQL::Query, type: :request do + let(:artifact) { create(:ci_job_artifact, file_type: :archive, file_format: :zip) } + let!(:build) { create(:ci_build, :success, name: 'build', pipeline: pipeline) } + let!(:cancelable) { create(:ci_build, :cancelable, name: 'cancelable', pipeline: pipeline) } let!(:created_by_tag) { create(:ci_build, :success, name: 'created_by_tag', tag: true, pipeline: pipeline) } + let!(:pending) { create(:ci_build, :pending, name: 'pending', pipeline: pipeline) } + let!(:playable) { create(:ci_build, :playable, name: 'playable', pipeline: pipeline) } + let!(:retryable) { create(:ci_build, :retryable, name: 'retryable', pipeline: pipeline) } + let!(:scheduled) { create(:ci_build, :scheduled, name: 'scheduled', pipeline: pipeline) } + let!(:with_artifact) { create(:ci_build, :success, name: 'with_artifact', job_artifacts: [artifact], pipeline: pipeline) } let!(:with_coverage) { create(:ci_build, :success, name: 'with_coverage', coverage: 40.0, pipeline: pipeline) } - let!(:stuck) { create(:ci_build, :pending, name: 'stuck', pipeline: pipeline) } fixtures_path = 'graphql/jobs/' get_jobs_query = 'get_jobs.query.graphql' + full_path = 'frontend-fixtures/builds-project' let_it_be(:query) do get_graphql_query_as_string("jobs/components/table/graphql/queries/#{get_jobs_query}") @@ -49,7 +57,7 @@ RSpec.describe 'Jobs (JavaScript fixtures)' do it "#{fixtures_path}#{get_jobs_query}.json" do post_graphql(query, current_user: user, variables: { - fullPath: 'frontend-fixtures/builds-project' + fullPath: full_path }) expect_graphql_errors_to_be_empty @@ -60,7 +68,25 @@ RSpec.describe 'Jobs (JavaScript fixtures)' do project.add_guest(guest) post_graphql(query, current_user: guest, variables: { - fullPath: 'frontend-fixtures/builds-project' + fullPath: full_path + }) + + expect_graphql_errors_to_be_empty + end + + it "#{fixtures_path}#{get_jobs_query}.paginated.json" do + post_graphql(query, current_user: user, variables: { + fullPath: full_path, + first: 2 + }) + + expect_graphql_errors_to_be_empty + end + + it "#{fixtures_path}#{get_jobs_query}.empty.json" do + post_graphql(query, current_user: user, variables: { + fullPath: full_path, + first: 0 }) expect_graphql_errors_to_be_empty diff --git a/spec/frontend/jobs/components/table/cells/actions_cell_spec.js b/spec/frontend/jobs/components/table/cells/actions_cell_spec.js index 976b128532d..7cc008f332d 100644 --- a/spec/frontend/jobs/components/table/cells/actions_cell_spec.js +++ b/spec/frontend/jobs/components/table/cells/actions_cell_spec.js @@ -12,17 +12,12 @@ import JobRetryMutation from '~/jobs/components/table/graphql/mutations/job_retr import JobUnscheduleMutation from '~/jobs/components/table/graphql/mutations/job_unschedule.mutation.graphql'; import JobCancelMutation from '~/jobs/components/table/graphql/mutations/job_cancel.mutation.graphql'; import { - playableJob, - retryableJob, - cancelableJob, - scheduledJob, - cannotRetryJob, - cannotPlayJob, - cannotPlayScheduledJob, - retryMutationResponse, + mockJobsNodes, + mockJobsNodesAsGuest, playMutationResponse, - cancelMutationResponse, + retryMutationResponse, unscheduleMutationResponse, + cancelMutationResponse, } from '../../../mock_data'; jest.mock('~/lib/utils/url_utility'); @@ -32,6 +27,22 @@ Vue.use(VueApollo); describe('Job actions cell', () => { let wrapper; + const findMockJob = (jobName, nodes = mockJobsNodes) => { + const job = nodes.find(({ name }) => name === jobName); + expect(job).toBeDefined(); // ensure job is present + return job; + }; + + const mockJob = findMockJob('build'); + const cancelableJob = findMockJob('cancelable'); + const playableJob = findMockJob('playable'); + const retryableJob = findMockJob('retryable'); + const scheduledJob = findMockJob('scheduled'); + const jobWithArtifact = findMockJob('with_artifact'); + const cannotPlayJob = findMockJob('playable', mockJobsNodesAsGuest); + const cannotRetryJob = findMockJob('retryable', mockJobsNodesAsGuest); + const cannotPlayScheduledJob = findMockJob('scheduled', mockJobsNodesAsGuest); + const findRetryButton = () => wrapper.findByTestId('retry'); const findPlayButton = () => wrapper.findByTestId('play'); const findCancelButton = () => wrapper.findByTestId('cancel-button'); @@ -55,10 +66,10 @@ describe('Job actions cell', () => { return createMockApollo(requestHandlers); }; - const createComponent = (jobType, requestHandlers, props = {}) => { + const createComponent = (job, requestHandlers, props = {}) => { wrapper = shallowMountExtended(ActionsCell, { propsData: { - job: jobType, + job, ...props, }, apolloProvider: createMockApolloProvider(requestHandlers), @@ -73,15 +84,15 @@ describe('Job actions cell', () => { }); it('displays the artifacts download button with correct link', () => { - createComponent(playableJob); + createComponent(jobWithArtifact); expect(findDownloadArtifactsButton().attributes('href')).toBe( - playableJob.artifacts.nodes[0].downloadPath, + jobWithArtifact.artifacts.nodes[0].downloadPath, ); }); it('does not display an artifacts download button', () => { - createComponent(retryableJob); + createComponent(mockJob); expect(findDownloadArtifactsButton().exists()).toBe(false); }); @@ -101,7 +112,7 @@ describe('Job actions cell', () => { button | action | jobType ${findPlayButton} | ${'play'} | ${playableJob} ${findRetryButton} | ${'retry'} | ${retryableJob} - ${findDownloadArtifactsButton} | ${'download artifacts'} | ${playableJob} + ${findDownloadArtifactsButton} | ${'download artifacts'} | ${jobWithArtifact} ${findCancelButton} | ${'cancel'} | ${cancelableJob} `('displays the $action button', ({ button, jobType }) => { createComponent(jobType); diff --git a/spec/frontend/jobs/components/table/cells/job_cell_spec.js b/spec/frontend/jobs/components/table/cells/job_cell_spec.js index e3bef17b6fa..ddc196129a7 100644 --- a/spec/frontend/jobs/components/table/cells/job_cell_spec.js +++ b/spec/frontend/jobs/components/table/cells/job_cell_spec.js @@ -2,13 +2,22 @@ import { shallowMount } from '@vue/test-utils'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import JobCell from '~/jobs/components/table/cells/job_cell.vue'; -import { mockJobsInTable, mockJobsAsGuestInTable } from '../../../mock_data'; - -const getMockJob = (name) => mockJobsInTable.find((job) => job.name === name); +import { mockJobsNodes, mockJobsNodesAsGuest } from '../../../mock_data'; describe('Job Cell', () => { let wrapper; + const findMockJob = (jobName, nodes = mockJobsNodes) => { + const job = nodes.find(({ name }) => name === jobName); + expect(job).toBeDefined(); // ensure job is present + return job; + }; + + const mockJob = findMockJob('build'); + const jobCreatedByTag = findMockJob('created_by_tag'); + const pendingJob = findMockJob('pending'); + const jobAsGuest = findMockJob('build', mockJobsNodesAsGuest); + const findJobIdLink = () => wrapper.findByTestId('job-id-link'); const findJobIdNoLink = () => wrapper.findByTestId('job-id-limited-access'); const findJobRef = () => wrapper.findByTestId('job-ref'); @@ -20,13 +29,11 @@ describe('Job Cell', () => { const findBadgeById = (id) => wrapper.findByTestId(id); - const mockJob = getMockJob('build'); - - const createComponent = (jobData = mockJob) => { + const createComponent = (job = mockJob) => { wrapper = extendedWrapper( shallowMount(JobCell, { propsData: { - job: jobData, + job, }, }), ); @@ -48,11 +55,9 @@ describe('Job Cell', () => { }); it('display the job id with no link', () => { - const mockJobAsGuest = mockJobsAsGuestInTable[0]; - - createComponent(mockJobAsGuest); + createComponent(jobAsGuest); - const expectedJobId = `#${getIdFromGraphQLId(mockJobAsGuest.id)}`; + const expectedJobId = `#${getIdFromGraphQLId(jobAsGuest.id)}`; expect(findJobIdNoLink().text()).toBe(expectedJobId); expect(findJobIdNoLink().exists()).toBe(true); @@ -76,7 +81,7 @@ describe('Job Cell', () => { }); it('displays label icon when job is created by a tag', () => { - createComponent(getMockJob('created_by_tag')); + createComponent(jobCreatedByTag); expect(findLabelIcon().exists()).toBe(true); expect(findForkIcon().exists()).toBe(false); @@ -131,8 +136,8 @@ describe('Job Cell', () => { expect(findStuckIcon().exists()).toBe(false); }); - it('stuck icon is shown if job is stuck', () => { - createComponent(getMockJob('stuck')); + it('stuck icon is shown if job is pending', () => { + createComponent(pendingJob); expect(findStuckIcon().exists()).toBe(true); expect(findStuckIcon().attributes('name')).toBe('warning'); diff --git a/spec/frontend/jobs/components/table/job_table_app_spec.js b/spec/frontend/jobs/components/table/job_table_app_spec.js index 986fba21fb9..963112fbd5e 100644 --- a/spec/frontend/jobs/components/table/job_table_app_spec.js +++ b/spec/frontend/jobs/components/table/job_table_app_spec.js @@ -18,8 +18,8 @@ import JobsTableApp from '~/jobs/components/table/jobs_table_app.vue'; import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue'; import JobsFilteredSearch from '~/jobs/components/filtered_search/jobs_filtered_search.vue'; import { - mockJobsQueryResponse, - mockJobsQueryEmptyResponse, + mockJobsResponsePaginated, + mockJobsResponseEmpty, mockFailedSearchToken, } from '../../mock_data'; @@ -32,9 +32,9 @@ describe('Job table app', () => { let wrapper; let jobsTableVueSearch = true; - const successHandler = jest.fn().mockResolvedValue(mockJobsQueryResponse); + const successHandler = jest.fn().mockResolvedValue(mockJobsResponsePaginated); const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error')); - const emptyHandler = jest.fn().mockResolvedValue(mockJobsQueryEmptyResponse); + const emptyHandler = jest.fn().mockResolvedValue(mockJobsResponseEmpty); const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader); const findLoadingSpinner = () => wrapper.findComponent(GlLoadingIcon); @@ -128,15 +128,18 @@ describe('Job table app', () => { }); it('handles infinite scrolling by calling fetch more', async () => { + const pageSize = 30; + expect(findLoadingSpinner().exists()).toBe(true); await waitForPromises(); expect(findLoadingSpinner().exists()).toBe(false); - expect(successHandler).toHaveBeenCalledWith({ - after: 'eyJpZCI6IjIzMTcifQ', - fullPath: 'gitlab-org/gitlab', + expect(successHandler).toHaveBeenLastCalledWith({ + first: pageSize, + fullPath: projectPath, + after: mockJobsResponsePaginated.data.project.jobs.pageInfo.endCursor, }); }); }); diff --git a/spec/frontend/jobs/components/table/jobs_table_spec.js b/spec/frontend/jobs/components/table/jobs_table_spec.js index ac8bef675f8..803df3df37f 100644 --- a/spec/frontend/jobs/components/table/jobs_table_spec.js +++ b/spec/frontend/jobs/components/table/jobs_table_spec.js @@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import JobsTable from '~/jobs/components/table/jobs_table.vue'; import CiBadge from '~/vue_shared/components/ci_badge_link.vue'; -import { mockJobsInTable } from '../../mock_data'; +import { mockJobsNodes } from '../../mock_data'; describe('Jobs Table', () => { let wrapper; @@ -19,7 +19,7 @@ describe('Jobs Table', () => { wrapper = extendedWrapper( mount(JobsTable, { propsData: { - jobs: mockJobsInTable, + jobs: mockJobsNodes, ...props, }, }), @@ -39,7 +39,7 @@ describe('Jobs Table', () => { }); it('displays correct number of job rows', () => { - expect(findTableRows()).toHaveLength(mockJobsInTable.length); + expect(findTableRows()).toHaveLength(mockJobsNodes.length); }); it('displays job status', () => { @@ -47,14 +47,14 @@ describe('Jobs Table', () => { }); it('displays the job stage and name', () => { - const firstJob = mockJobsInTable[0]; + const firstJob = mockJobsNodes[0]; expect(findJobStage().text()).toBe(firstJob.stage.name); expect(findJobName().text()).toBe(firstJob.name); }); it('displays the coverage for only jobs that have coverage', () => { - const jobsThatHaveCoverage = mockJobsInTable.filter((job) => job.coverage !== null); + const jobsThatHaveCoverage = mockJobsNodes.filter((job) => job.coverage !== null); jobsThatHaveCoverage.forEach((job, index) => { expect(findAllCoverageJobs().at(index).text()).toBe(`${job.coverage}%`); diff --git a/spec/frontend/jobs/mock_data.js b/spec/frontend/jobs/mock_data.js index cc5fd92615a..4d7ea6a46bd 100644 --- a/spec/frontend/jobs/mock_data.js +++ b/spec/frontend/jobs/mock_data.js @@ -1,3 +1,5 @@ +import mockJobsEmpty from 'test_fixtures/graphql/jobs/get_jobs.query.graphql.empty.json'; +import mockJobsPaginated from 'test_fixtures/graphql/jobs/get_jobs.query.graphql.paginated.json'; import mockJobs from 'test_fixtures/graphql/jobs/get_jobs.query.graphql.json'; import mockJobsAsGuest from 'test_fixtures/graphql/jobs/get_jobs.query.graphql.as_guest.json'; import { TEST_HOST } from 'spec/test_constants'; @@ -6,8 +8,10 @@ const threeWeeksAgo = new Date(); threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21); // Fixtures generated at spec/frontend/fixtures/jobs.rb -export const mockJobsInTable = mockJobs.data.project.jobs.nodes; -export const mockJobsAsGuestInTable = mockJobsAsGuest.data.project.jobs.nodes; +export const mockJobsResponsePaginated = mockJobsPaginated; +export const mockJobsResponseEmpty = mockJobsEmpty; +export const mockJobsNodes = mockJobs.data.project.jobs.nodes; +export const mockJobsNodesAsGuest = mockJobsAsGuest.data.project.jobs.nodes; export const stages = [ { @@ -1289,409 +1293,6 @@ export const mockPipelineDetached = { }, }; -export const mockJobsQueryResponse = { - data: { - project: { - id: '1', - jobs: { - count: 1, - pageInfo: { - endCursor: 'eyJpZCI6IjIzMTcifQ', - hasNextPage: true, - hasPreviousPage: false, - startCursor: 'eyJpZCI6IjIzMzYifQ', - __typename: 'PageInfo', - }, - nodes: [ - { - artifacts: { - nodes: [ - { - downloadPath: '/root/ci-project/-/jobs/2336/artifacts/download?file_type=trace', - fileType: 'TRACE', - __typename: 'CiJobArtifact', - }, - { - downloadPath: - '/root/ci-project/-/jobs/2336/artifacts/download?file_type=metadata', - fileType: 'METADATA', - __typename: 'CiJobArtifact', - }, - { - downloadPath: '/root/ci-project/-/jobs/2336/artifacts/download?file_type=archive', - fileType: 'ARCHIVE', - __typename: 'CiJobArtifact', - }, - ], - __typename: 'CiJobArtifactConnection', - }, - allowFailure: false, - status: 'SUCCESS', - scheduledAt: null, - manualJob: false, - triggered: null, - createdByTag: false, - detailedStatus: { - id: 'status-1', - detailsPath: '/root/ci-project/-/jobs/2336', - group: 'success', - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - action: { - id: 'action-1', - buttonTitle: 'Retry this job', - icon: 'retry', - method: 'post', - path: '/root/ci-project/-/jobs/2336/retry', - title: 'Retry', - __typename: 'StatusAction', - }, - __typename: 'DetailedStatus', - }, - id: 'gid://gitlab/Ci::Build/2336', - refName: 'main', - refPath: '/root/ci-project/-/commits/main', - tags: [], - shortSha: '4408fa2a', - commitPath: '/root/ci-project/-/commit/4408fa2a27aaadfdf42d8dda3d6a9c01ce6cad78', - pipeline: { - id: 'gid://gitlab/Ci::Pipeline/473', - path: '/root/ci-project/-/pipelines/473', - user: { - id: 'user-1', - webPath: '/root', - avatarUrl: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - __typename: 'UserCore', - }, - __typename: 'Pipeline', - }, - stage: { - id: 'stage-1', - name: 'deploy', - __typename: 'CiStage', - }, - name: 'artifact_job', - duration: 3, - finishedAt: '2021-04-29T14:19:50Z', - coverage: null, - retryable: true, - playable: false, - cancelable: false, - active: false, - stuck: false, - userPermissions: { - readBuild: true, - readJobArtifacts: true, - updateBuild: true, - __typename: 'JobPermissions', - }, - __typename: 'CiJob', - }, - ], - __typename: 'CiJobConnection', - }, - __typename: 'Project', - }, - }, -}; - -export const mockJobsQueryEmptyResponse = { - data: { - project: { - id: '1', - jobs: [], - }, - }, -}; - -export const retryableJob = { - artifacts: { - nodes: [ - { - downloadPath: '/root/ci-project/-/jobs/847/artifacts/download?file_type=trace', - fileType: 'TRACE', - __typename: 'CiJobArtifact', - }, - ], - __typename: 'CiJobArtifactConnection', - }, - allowFailure: false, - status: 'SUCCESS', - scheduledAt: null, - manualJob: false, - triggered: null, - createdByTag: false, - detailedStatus: { - detailsPath: '/root/test-job-artifacts/-/jobs/1981', - group: 'success', - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - action: { - buttonTitle: 'Retry this job', - icon: 'retry', - method: 'post', - path: '/root/test-job-artifacts/-/jobs/1981/retry', - title: 'Retry', - __typename: 'StatusAction', - }, - __typename: 'DetailedStatus', - }, - id: 'gid://gitlab/Ci::Build/1981', - refName: 'main', - refPath: '/root/test-job-artifacts/-/commits/main', - tags: [], - shortSha: '75daf01b', - commitPath: '/root/test-job-artifacts/-/commit/75daf01b465e7eab5a04a315e44660c9a17c8055', - pipeline: { - id: 'gid://gitlab/Ci::Pipeline/288', - path: '/root/test-job-artifacts/-/pipelines/288', - user: { - webPath: '/root', - avatarUrl: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - __typename: 'UserCore', - }, - __typename: 'Pipeline', - }, - stage: { name: 'test', __typename: 'CiStage' }, - name: 'hello_world', - duration: 7, - finishedAt: '2021-08-30T20:33:56Z', - coverage: null, - retryable: true, - playable: false, - cancelable: false, - active: false, - stuck: false, - userPermissions: { readBuild: true, updateBuild: true, __typename: 'JobPermissions' }, - __typename: 'CiJob', -}; - -export const cancelableJob = { - artifacts: { - nodes: [], - __typename: 'CiJobArtifactConnection', - }, - allowFailure: false, - status: 'PENDING', - scheduledAt: null, - manualJob: false, - triggered: null, - createdByTag: false, - detailedStatus: { - id: 'pending-1305-1305', - detailsPath: '/root/lots-of-jobs-project/-/jobs/1305', - group: 'pending', - icon: 'status_pending', - label: 'pending', - text: 'pending', - tooltip: 'pending', - action: { - id: 'Ci::Build-pending-1305', - buttonTitle: 'Cancel this job', - icon: 'cancel', - method: 'post', - path: '/root/lots-of-jobs-project/-/jobs/1305/cancel', - title: 'Cancel', - __typename: 'StatusAction', - }, - __typename: 'DetailedStatus', - }, - id: 'gid://gitlab/Ci::Build/1305', - refName: 'main', - refPath: '/root/lots-of-jobs-project/-/commits/main', - tags: [], - shortSha: '750605f2', - commitPath: '/root/lots-of-jobs-project/-/commit/750605f29530778cf0912779eba6d073128962a5', - stage: { - id: 'gid://gitlab/Ci::Stage/181', - name: 'deploy', - __typename: 'CiStage', - }, - name: 'job_212', - duration: null, - finishedAt: null, - coverage: null, - retryable: false, - playable: false, - cancelable: true, - active: true, - stuck: false, - userPermissions: { - readBuild: true, - readJobArtifacts: true, - updateBuild: true, - __typename: 'JobPermissions', - }, - __typename: 'CiJob', -}; - -export const cannotRetryJob = { - ...retryableJob, - userPermissions: { readBuild: true, updateBuild: false, __typename: 'JobPermissions' }, -}; - -export const playableJob = { - artifacts: { - nodes: [ - { - downloadPath: '/root/ci-project/-/jobs/621/artifacts/download?file_type=archive', - fileType: 'ARCHIVE', - __typename: 'CiJobArtifact', - }, - { - downloadPath: '/root/ci-project/-/jobs/621/artifacts/download?file_type=metadata', - fileType: 'METADATA', - __typename: 'CiJobArtifact', - }, - { - downloadPath: '/root/ci-project/-/jobs/621/artifacts/download?file_type=trace', - fileType: 'TRACE', - __typename: 'CiJobArtifact', - }, - ], - __typename: 'CiJobArtifactConnection', - }, - allowFailure: false, - status: 'SUCCESS', - scheduledAt: null, - manualJob: true, - triggered: null, - createdByTag: false, - detailedStatus: { - detailsPath: '/root/test-job-artifacts/-/jobs/1982', - group: 'success', - icon: 'status_success', - label: 'manual play action', - text: 'passed', - tooltip: 'passed', - action: { - buttonTitle: 'Trigger this manual action', - icon: 'play', - method: 'post', - path: '/root/test-job-artifacts/-/jobs/1982/play', - title: 'Play', - __typename: 'StatusAction', - }, - __typename: 'DetailedStatus', - }, - id: 'gid://gitlab/Ci::Build/1982', - refName: 'main', - refPath: '/root/test-job-artifacts/-/commits/main', - tags: [], - shortSha: '75daf01b', - commitPath: '/root/test-job-artifacts/-/commit/75daf01b465e7eab5a04a315e44660c9a17c8055', - pipeline: { - id: 'gid://gitlab/Ci::Pipeline/288', - path: '/root/test-job-artifacts/-/pipelines/288', - user: { - webPath: '/root', - avatarUrl: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - __typename: 'UserCore', - }, - __typename: 'Pipeline', - }, - stage: { name: 'test', __typename: 'CiStage' }, - name: 'hello_world_delayed', - duration: 6, - finishedAt: '2021-08-30T20:36:12Z', - coverage: null, - retryable: true, - playable: true, - cancelable: false, - active: false, - stuck: false, - userPermissions: { - readBuild: true, - readJobArtifacts: true, - updateBuild: true, - __typename: 'JobPermissions', - }, - __typename: 'CiJob', -}; - -export const cannotPlayJob = { - ...playableJob, - userPermissions: { - readBuild: true, - readJobArtifacts: true, - updateBuild: false, - __typename: 'JobPermissions', - }, -}; - -export const scheduledJob = { - artifacts: { nodes: [], __typename: 'CiJobArtifactConnection' }, - allowFailure: false, - status: 'SCHEDULED', - scheduledAt: '2021-08-31T22:36:05Z', - manualJob: true, - triggered: null, - createdByTag: false, - detailedStatus: { - detailsPath: '/root/test-job-artifacts/-/jobs/1986', - group: 'scheduled', - icon: 'status_scheduled', - label: 'unschedule action', - text: 'delayed', - tooltip: 'delayed manual action (%{remainingTime})', - action: { - buttonTitle: 'Unschedule job', - icon: 'time-out', - method: 'post', - path: '/root/test-job-artifacts/-/jobs/1986/unschedule', - title: 'Unschedule', - __typename: 'StatusAction', - }, - __typename: 'DetailedStatus', - }, - id: 'gid://gitlab/Ci::Build/1986', - refName: 'main', - refPath: '/root/test-job-artifacts/-/commits/main', - tags: [], - shortSha: '75daf01b', - commitPath: '/root/test-job-artifacts/-/commit/75daf01b465e7eab5a04a315e44660c9a17c8055', - pipeline: { - id: 'gid://gitlab/Ci::Pipeline/290', - path: '/root/test-job-artifacts/-/pipelines/290', - user: { - webPath: '/root', - avatarUrl: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - __typename: 'UserCore', - }, - __typename: 'Pipeline', - }, - stage: { name: 'test', __typename: 'CiStage' }, - name: 'hello_world_delayed', - duration: null, - finishedAt: null, - coverage: null, - retryable: false, - playable: true, - cancelable: false, - active: false, - stuck: false, - userPermissions: { readBuild: true, updateBuild: true, __typename: 'JobPermissions' }, - __typename: 'CiJob', -}; - -export const cannotPlayScheduledJob = { - ...scheduledJob, - userPermissions: { - readBuild: true, - readJobArtifacts: true, - updateBuild: false, - __typename: 'JobPermissions', - }, -}; - export const CIJobConnectionIncomingCache = { __typename: 'CiJobConnection', pageInfo: { diff --git a/spec/graphql/resolvers/users/groups_resolver_spec.rb b/spec/graphql/resolvers/users/groups_resolver_spec.rb index bbe9b6371cf..1e0e001fbf7 100644 --- a/spec/graphql/resolvers/users/groups_resolver_spec.rb +++ b/spec/graphql/resolvers/users/groups_resolver_spec.rb @@ -12,6 +12,7 @@ RSpec.describe Resolvers::Users::GroupsResolver do let_it_be(:private_maintainer_group) { create(:group, :private, name: 'b private maintainer', path: 'b-private-maintainer') } let_it_be(:public_developer_group) { create(:group, project_creation_level: nil, name: 'c public developer', path: 'c-public-developer') } let_it_be(:public_maintainer_group) { create(:group, name: 'a public maintainer', path: 'a-public-maintainer') } + let_it_be(:public_owner_group) { create(:group, name: 'a public owner', path: 'a-public-owner') } subject(:resolved_items) { resolve_groups(args: group_arguments, current_user: current_user, obj: resolver_object) } @@ -24,6 +25,7 @@ RSpec.describe Resolvers::Users::GroupsResolver do private_maintainer_group.add_maintainer(user) public_developer_group.add_developer(user) public_maintainer_group.add_maintainer(user) + public_owner_group.add_owner(user) end context 'when resolver object is current user' do @@ -34,6 +36,7 @@ RSpec.describe Resolvers::Users::GroupsResolver do is_expected.to match( [ public_maintainer_group, + public_owner_group, private_maintainer_group, public_developer_group ] @@ -41,10 +44,25 @@ RSpec.describe Resolvers::Users::GroupsResolver do end end + context 'when permission is :transfer_projects' do + let(:group_arguments) { { permission_scope: :transfer_projects } } + + specify do + is_expected.to match( + [ + public_maintainer_group, + public_owner_group, + private_maintainer_group + ] + ) + end + end + specify do is_expected.to match( [ public_maintainer_group, + public_owner_group, private_maintainer_group, public_developer_group, guest_group @@ -82,6 +100,7 @@ RSpec.describe Resolvers::Users::GroupsResolver do is_expected.to match( [ public_maintainer_group, + public_owner_group, private_maintainer_group, public_developer_group, guest_group diff --git a/spec/helpers/environments_helper_spec.rb b/spec/helpers/environments_helper_spec.rb index e4d4f18ad68..c1eaf1b1bcd 100644 --- a/spec/helpers/environments_helper_spec.rb +++ b/spec/helpers/environments_helper_spec.rb @@ -129,7 +129,6 @@ RSpec.describe EnvironmentsHelper do "environment_name": environment.name, "environments_path": api_v4_projects_environments_path(id: project.id), "environment_id": environment.id, - "cluster_applications_documentation_path" => help_page_path('user/clusters/integrations.md', anchor: 'elastic-stack-cluster-integration'), "clusters_path": project_clusters_path(project, format: :json) } diff --git a/spec/models/clusters/applications/elastic_stack_spec.rb b/spec/models/clusters/applications/elastic_stack_spec.rb deleted file mode 100644 index af2802d5e47..00000000000 --- a/spec/models/clusters/applications/elastic_stack_spec.rb +++ /dev/null @@ -1,177 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Clusters::Applications::ElasticStack do - include KubernetesHelpers - - include_examples 'cluster application core specs', :clusters_applications_elastic_stack - include_examples 'cluster application status specs', :clusters_applications_elastic_stack - include_examples 'cluster application version specs', :clusters_applications_elastic_stack - include_examples 'cluster application helm specs', :clusters_applications_elastic_stack - - describe 'cluster.integration_elastic_stack state synchronization' do - let!(:application) { create(:clusters_applications_elastic_stack) } - let(:cluster) { application.cluster } - let(:integration) { cluster.integration_elastic_stack } - - describe 'after_destroy' do - it 'disables the corresponding integration' do - application.destroy! - - expect(integration).not_to be_enabled - end - end - - describe 'on install' do - it 'enables the corresponding integration' do - application.make_scheduled! - application.make_installing! - application.make_installed! - - expect(integration).to be_enabled - end - end - - describe 'on uninstall' do - it 'disables the corresponding integration' do - application.make_scheduled! - application.make_installing! - application.make_installed! - application.make_externally_uninstalled! - - expect(integration).not_to be_enabled - end - end - end - - describe '#install_command' do - let!(:elastic_stack) { create(:clusters_applications_elastic_stack) } - - subject { elastic_stack.install_command } - - it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::V3::InstallCommand) } - - it 'is initialized with elastic stack arguments' do - expect(subject.name).to eq('elastic-stack') - expect(subject.chart).to eq('elastic-stack/elastic-stack') - expect(subject.version).to eq('3.0.0') - expect(subject.repository).to eq('https://charts.gitlab.io') - expect(subject).to be_rbac - expect(subject.files).to eq(elastic_stack.files) - expect(subject.preinstall).to be_empty - end - - context 'within values.yaml' do - let(:values_yaml_content) {subject.files[:"values.yaml"]} - - it 'contains the disabled index lifecycle management' do - expect(values_yaml_content).to include "setup.ilm.enabled: false" - end - - it 'contains daily indices with respective template' do - expect(values_yaml_content).to include "index: \"filebeat-%{[agent.version]}-%{+yyyy.MM.dd}\"" - expect(values_yaml_content).to include "setup.template.name: 'filebeat'" - expect(values_yaml_content).to include "setup.template.pattern: 'filebeat-*'" - end - end - - context 'on a non rbac enabled cluster' do - before do - elastic_stack.cluster.platform_kubernetes.abac! - end - - it { is_expected.not_to be_rbac } - end - - context 'on versions older than 2' do - before do - elastic_stack.status = elastic_stack.status_states[:updating] - elastic_stack.version = "1.9.0" - end - - it 'includes a preinstall script' do - expect(subject.preinstall).not_to be_empty - expect(subject.preinstall.first).to include("helm uninstall") - end - end - - context 'on versions older than 3' do - before do - elastic_stack.status = elastic_stack.status_states[:updating] - elastic_stack.version = "2.9.0" - end - - it 'includes a preinstall script' do - expect(subject.preinstall).not_to be_empty - expect(subject.preinstall.first).to include("helm uninstall") - end - end - - context 'application failed to install previously' do - let(:elastic_stack) { create(:clusters_applications_elastic_stack, :errored, version: '0.0.1') } - - it 'is initialized with the locked version' do - expect(subject.version).to eq('3.0.0') - end - end - end - - describe '#chart_above_v2?' do - let(:elastic_stack) { create(:clusters_applications_elastic_stack, version: version) } - - subject { elastic_stack.chart_above_v2? } - - context 'on v1.9.0' do - let(:version) { '1.9.0' } - - it { is_expected.to be_falsy } - end - - context 'on v2.0.0' do - let(:version) { '2.0.0' } - - it { is_expected.to be_truthy } - end - end - - describe '#chart_above_v3?' do - let(:elastic_stack) { create(:clusters_applications_elastic_stack, version: version) } - - subject { elastic_stack.chart_above_v3? } - - context 'on v1.9.0' do - let(:version) { '1.9.0' } - - it { is_expected.to be_falsy } - end - - context 'on v3.0.0' do - let(:version) { '3.0.0' } - - it { is_expected.to be_truthy } - end - end - - describe '#uninstall_command' do - let!(:elastic_stack) { create(:clusters_applications_elastic_stack) } - - subject { elastic_stack.uninstall_command } - - it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::V3::DeleteCommand) } - - it 'is initialized with elastic stack arguments' do - expect(subject.name).to eq('elastic-stack') - expect(subject).to be_rbac - expect(subject.files).to eq(elastic_stack.files) - end - - it 'specifies a post delete command to remove custom resource definitions' do - expect(subject.postdelete).to eq([ - 'kubectl delete pvc --selector app\\=elastic-stack-elasticsearch-master --namespace gitlab-managed-apps' - ]) - end - end - - it_behaves_like 'cluster-based #elasticsearch_client', :clusters_applications_elastic_stack -end diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 30591a3ff5d..65ead01a2bd 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -42,7 +42,6 @@ RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching do it { is_expected.to delegate_method(:available?).to(:application_helm).with_prefix } it { is_expected.to delegate_method(:available?).to(:application_ingress).with_prefix } it { is_expected.to delegate_method(:available?).to(:application_knative).with_prefix } - it { is_expected.to delegate_method(:available?).to(:integration_elastic_stack).with_prefix } it { is_expected.to delegate_method(:available?).to(:integration_prometheus).with_prefix } it { is_expected.to delegate_method(:external_ip).to(:application_ingress).with_prefix } it { is_expected.to delegate_method(:external_hostname).to(:application_ingress).with_prefix } @@ -200,22 +199,6 @@ RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching do end end - describe '.with_available_elasticstack' do - subject { described_class.with_available_elasticstack } - - let_it_be(:cluster) { create(:cluster) } - - context 'cluster has ElasticStack application' do - let!(:application) { create(:clusters_applications_elastic_stack, :installed, cluster: cluster) } - - it { is_expected.to include(cluster) } - end - - context 'cluster does not have ElasticStack application' do - it { is_expected.not_to include(cluster) } - end - end - describe '.distinct_with_deployed_environments' do subject { described_class.distinct_with_deployed_environments } diff --git a/spec/models/clusters/integrations/elastic_stack_spec.rb b/spec/models/clusters/integrations/elastic_stack_spec.rb deleted file mode 100644 index be4d59b52a2..00000000000 --- a/spec/models/clusters/integrations/elastic_stack_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Clusters::Integrations::ElasticStack do - include KubernetesHelpers - include StubRequests - - describe 'associations' do - it { is_expected.to belong_to(:cluster).class_name('Clusters::Cluster') } - end - - describe 'validations' do - it { is_expected.to validate_presence_of(:cluster) } - it { is_expected.not_to allow_value(nil).for(:enabled) } - end - - it_behaves_like 'cluster-based #elasticsearch_client', :clusters_integrations_elastic_stack -end diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index fd89a3a2e22..92af1c3d571 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -1711,25 +1711,6 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do end end - describe '#elastic_stack_available?' do - let!(:cluster) { create(:cluster, :project, :provided_by_user, projects: [project]) } - let!(:deployment) { create(:deployment, :success, environment: environment, project: project, cluster: cluster) } - - context 'when integration does not exist' do - it 'returns false' do - expect(environment.elastic_stack_available?).to be(false) - end - end - - context 'when integration is enabled' do - let!(:integration) { create(:clusters_integrations_elastic_stack, cluster: cluster) } - - it 'returns true' do - expect(environment.elastic_stack_available?).to be(true) - end - end - end - describe '#destroy' do it 'remove the deployment refs from gitaly' do deployment = create(:deployment, :success, environment: environment, project: project) diff --git a/spec/models/project_import_state_spec.rb b/spec/models/project_import_state_spec.rb index f6e398bd23c..db79185d759 100644 --- a/spec/models/project_import_state_spec.rb +++ b/spec/models/project_import_state_spec.rb @@ -156,7 +156,7 @@ RSpec.describe ProjectImportState, type: :model do project.import_state.finish end - it 'does not qneueue housekeeping when project does not have a valid import type' do + it 'does not enqueue housekeeping when project does not have a valid import type' do project = create(:project, :import_started, import_type: nil) expect(Projects::AfterImportWorker).not_to receive(:perform_async) @@ -164,6 +164,43 @@ RSpec.describe ProjectImportState, type: :model do project.import_state.finish end end + + context 'state transition: [:none, :scheduled, :started] => [:canceled]' do + it 'updates the import status' do + import_state = create(:import_state, :none) + expect { import_state.cancel } + .to change { import_state.status } + .from('none').to('canceled') + end + + it 'unsets the JID' do + import_state = create(:import_state, :started, jid: '123') + + expect(Gitlab::SidekiqStatus) + .to receive(:unset) + .with('123') + .and_call_original + + import_state.cancel! + + expect(import_state.jid).to be_nil + end + + it 'removes import data' do + import_data = ProjectImportData.new(data: { 'test' => 'some data' }) + project = create(:project, :import_scheduled, import_data: import_data) + + expect(project) + .to receive(:remove_import_data) + .and_call_original + + expect do + project.import_state.cancel + project.reload + end.to change { project.import_data } + .from(import_data).to(nil) + end + end end describe 'clearing `jid` after finish', :clean_gitlab_redis_cache do @@ -178,7 +215,7 @@ RSpec.describe ProjectImportState, type: :model do end end - context 'with an JID' do + context 'with a JID' do it 'unsets the JID' do import_state = create(:import_state, :started, jid: '123') diff --git a/spec/requests/api/graphql/current_user/groups_query_spec.rb b/spec/requests/api/graphql/current_user/groups_query_spec.rb index 39f323b21a3..ef0f32bacf0 100644 --- a/spec/requests/api/graphql/current_user/groups_query_spec.rb +++ b/spec/requests/api/graphql/current_user/groups_query_spec.rb @@ -8,8 +8,9 @@ RSpec.describe 'Query current user groups' do let_it_be(:user) { create(:user) } let_it_be(:guest_group) { create(:group, name: 'public guest', path: 'public-guest') } let_it_be(:private_maintainer_group) { create(:group, :private, name: 'b private maintainer', path: 'b-private-maintainer') } - let_it_be(:public_developer_group) { create(:group, :private, project_creation_level: nil, name: 'c public developer', path: 'c-public-developer') } - let_it_be(:public_maintainer_group) { create(:group, :private, name: 'a public maintainer', path: 'a-public-maintainer') } + let_it_be(:public_developer_group) { create(:group, project_creation_level: nil, name: 'c public developer', path: 'c-public-developer') } + let_it_be(:public_maintainer_group) { create(:group, name: 'a public maintainer', path: 'a-public-maintainer') } + let_it_be(:public_owner_group) { create(:group, name: 'a public owner', path: 'a-public-owner') } let(:group_arguments) { {} } let(:current_user) { user } @@ -29,6 +30,7 @@ RSpec.describe 'Query current user groups' do private_maintainer_group.add_maintainer(user) public_developer_group.add_developer(user) public_maintainer_group.add_maintainer(user) + public_owner_group.add_owner(user) end subject { graphql_data.dig('currentUser', 'groups', 'nodes') } @@ -52,6 +54,7 @@ RSpec.describe 'Query current user groups' do is_expected.to match( expected_group_hash( public_maintainer_group, + public_owner_group, private_maintainer_group, public_developer_group, guest_group @@ -66,6 +69,7 @@ RSpec.describe 'Query current user groups' do is_expected.to match( expected_group_hash( public_maintainer_group, + public_owner_group, private_maintainer_group, public_developer_group ) @@ -86,6 +90,32 @@ RSpec.describe 'Query current user groups' do end end + context 'when permission_scope is TRANSFER_PROJECTS' do + let(:group_arguments) { { permission_scope: :TRANSFER_PROJECTS } } + + specify do + is_expected.to match( + expected_group_hash( + public_maintainer_group, + public_owner_group, + private_maintainer_group + ) + ) + end + + context 'when search is provided' do + let(:group_arguments) { { permission_scope: :TRANSFER_PROJECTS, search: 'owner' } } + + specify do + is_expected.to match( + expected_group_hash( + public_owner_group + ) + ) + end + end + end + context 'when search is provided' do let(:group_arguments) { { search: 'maintainer' } } diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 7e6d80c047c..8655e5b0238 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -462,6 +462,16 @@ RSpec.describe API::ProjectImport, :aggregate_failures do expect(json_response).to include('import_status' => 'failed', 'import_error' => 'error') end + + it 'returns the import status if canceled' do + project = create(:project, :import_canceled) + project.add_maintainer(user) + + get api("/projects/#{project.id}/import", user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to include('import_status' => 'canceled') + end end describe 'POST /projects/import/authorize' do diff --git a/spec/services/clusters/applications/create_service_spec.rb b/spec/services/clusters/applications/create_service_spec.rb index eb907377ca8..00a67a9b2ef 100644 --- a/spec/services/clusters/applications/create_service_spec.rb +++ b/spec/services/clusters/applications/create_service_spec.rb @@ -168,29 +168,6 @@ RSpec.describe Clusters::Applications::CreateService do subject end end - - context 'elastic stack application' do - let(:params) do - { - application: 'elastic_stack' - } - end - - before do - create(:clusters_applications_ingress, :installed, external_ip: "127.0.0.0", cluster: cluster) - expect_any_instance_of(Clusters::Applications::ElasticStack) - .to receive(:make_scheduled!) - .and_call_original - end - - it 'creates the application' do - expect do - subject - - cluster.reload - end.to change(cluster, :application_elastic_stack) - end - end end context 'invalid application' do diff --git a/spec/services/clusters/integrations/create_service_spec.rb b/spec/services/clusters/integrations/create_service_spec.rb index 6dac97ebf8f..016511a3c01 100644 --- a/spec/services/clusters/integrations/create_service_spec.rb +++ b/spec/services/clusters/integrations/create_service_spec.rb @@ -61,7 +61,6 @@ RSpec.describe Clusters::Integrations::CreateService, '#execute' do end it_behaves_like 'a cluster integration', 'prometheus' - it_behaves_like 'a cluster integration', 'elastic_stack' context 'when application_type is invalid' do let(:params) do diff --git a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb index 3cd82b8bf4d..5a32c1b40bb 100644 --- a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb +++ b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb @@ -22,6 +22,7 @@ RSpec.describe Gitlab::GithubImport::ObjectImporter, :aggregate_failures do end let_it_be(:project) { create(:project, :import_started) } + let_it_be(:project2) { create(:project, :import_canceled) } let(:importer_class) { double(:importer_class, name: 'klass_name') } let(:importer_instance) { double(:importer_instance) } @@ -110,6 +111,27 @@ RSpec.describe Gitlab::GithubImport::ObjectImporter, :aggregate_failures do }) end + it 'logs info if the import state is canceled' do + expect(project2.import_state.status).to eq('canceled') + + expect(importer_class).not_to receive(:new) + + expect(importer_instance).not_to receive(:execute) + + expect(Gitlab::GithubImport::Logger) + .to receive(:info) + .with( + { + github_identifiers: nil, + message: 'project import canceled', + project_id: project2.id, + importer: 'klass_name' + } + ) + + worker.import(project2, client, { 'number' => 11, 'github_id' => 2 } ) + end + it 'logs error when the import fails' do expect(importer_class) .to receive(:new) diff --git a/spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb b/spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb index 1e088929f66..0ac1733781a 100644 --- a/spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb +++ b/spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe Gitlab::GithubImport::StageMethods do let_it_be(:project) { create(:project, :import_started, import_url: 'https://t0ken@github.com/repo/repo.git') } + let_it_be(:project2) { create(:project, :import_canceled) } let(:worker) do Class.new do @@ -22,6 +23,37 @@ RSpec.describe Gitlab::GithubImport::StageMethods do worker.perform(-1) end + it 'returns if the import state is canceled' do + allow(worker) + .to receive(:find_project) + .with(project2.id) + .and_return(project2) + + expect(worker).not_to receive(:try_import) + + expect(Gitlab::GithubImport::Logger) + .to receive(:info) + .with( + { + message: 'starting stage', + project_id: project2.id, + import_stage: 'DummyStage' + } + ) + + expect(Gitlab::GithubImport::Logger) + .to receive(:info) + .with( + { + message: 'project import canceled', + project_id: project2.id, + import_stage: 'DummyStage' + } + ) + + worker.perform(project2.id) + end + it 'imports the data when the project exists' do allow(worker) .to receive(:find_project) diff --git a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb index af15f465107..15bc55c1526 100644 --- a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb @@ -7,7 +7,8 @@ RSpec.describe Gitlab::GithubImport::ImportDiffNoteWorker do describe '#import' do it 'imports a diff note' do - project = double(:project, full_path: 'foo/bar', id: 1, import_state: nil) + import_state = create(:import_state, :started) + project = double(:project, full_path: 'foo/bar', id: 1, import_state: import_state) client = double(:client) importer = double(:importer) hash = { diff --git a/spec/workers/gitlab/github_import/import_issue_event_worker_spec.rb b/spec/workers/gitlab/github_import/import_issue_event_worker_spec.rb index 6af450151e3..03a6503fb84 100644 --- a/spec/workers/gitlab/github_import/import_issue_event_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_issue_event_worker_spec.rb @@ -6,8 +6,10 @@ RSpec.describe Gitlab::GithubImport::ImportIssueEventWorker do subject(:worker) { described_class.new } describe '#import' do + let(:import_state) { create(:import_state, :started) } + let(:project) do - instance_double('Project', full_path: 'foo/bar', id: 1, import_state: nil) + instance_double('Project', full_path: 'foo/bar', id: 1, import_state: import_state) end let(:client) { instance_double('Gitlab::GithubImport::Client') } diff --git a/spec/workers/gitlab/github_import/import_issue_worker_spec.rb b/spec/workers/gitlab/github_import/import_issue_worker_spec.rb index 29f21c1d184..c2a7639fde4 100644 --- a/spec/workers/gitlab/github_import/import_issue_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_issue_worker_spec.rb @@ -7,7 +7,8 @@ RSpec.describe Gitlab::GithubImport::ImportIssueWorker do describe '#import' do it 'imports an issue' do - project = double(:project, full_path: 'foo/bar', id: 1, import_state: nil) + import_state = create(:import_state, :started) + project = double(:project, full_path: 'foo/bar', id: 1, import_state: import_state) client = double(:client) importer = double(:importer) hash = { diff --git a/spec/workers/gitlab/github_import/import_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_note_worker_spec.rb index f4598340938..16ca5658f77 100644 --- a/spec/workers/gitlab/github_import/import_note_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_note_worker_spec.rb @@ -7,7 +7,8 @@ RSpec.describe Gitlab::GithubImport::ImportNoteWorker do describe '#import' do it 'imports a note' do - project = double(:project, full_path: 'foo/bar', id: 1, import_state: nil) + import_state = create(:import_state, :started) + project = double(:project, full_path: 'foo/bar', id: 1, import_state: import_state) client = double(:client) importer = double(:importer) hash = { diff --git a/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb b/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb index faed2f8f340..59f45b437c4 100644 --- a/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb @@ -7,7 +7,8 @@ RSpec.describe Gitlab::GithubImport::ImportPullRequestWorker do describe '#import' do it 'imports a pull request' do - project = double(:project, full_path: 'foo/bar', id: 1, import_state: nil) + import_state = create(:import_state, :started) + project = double(:project, full_path: 'foo/bar', id: 1, import_state: import_state) client = double(:client) importer = double(:importer) hash = { |