diff options
Diffstat (limited to 'spec')
23 files changed, 698 insertions, 327 deletions
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index 325d675a68c..8b02cfa30ab 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -74,6 +74,7 @@ RSpec.describe 'Database schema' do slack_integrations: %w[team_id user_id], snippets: %w[author_id], spam_logs: %w[user_id], + status_check_responses: %w[external_approval_rule_id], subscriptions: %w[user_id subscribable_id], suggestions: %w[commit_id], taggings: %w[tag_id taggable_id tagger_id], diff --git a/spec/frontend/editor/editor_ci_schema_ext_spec.js b/spec/frontend/editor/editor_ci_schema_ext_spec.js index 17a9ae7335f..2f0ecfb151e 100644 --- a/spec/frontend/editor/editor_ci_schema_ext_spec.js +++ b/spec/frontend/editor/editor_ci_schema_ext_spec.js @@ -4,6 +4,8 @@ import { EXTENSION_CI_SCHEMA_FILE_NAME_MATCH } from '~/editor/constants'; import EditorLite from '~/editor/editor_lite'; import { CiSchemaExtension } from '~/editor/extensions/editor_ci_schema_ext'; +const mockRef = 'AABBCCDD'; + describe('~/editor/editor_ci_config_ext', () => { const defaultBlobPath = '.gitlab-ci.yml'; @@ -75,8 +77,6 @@ describe('~/editor/editor_ci_config_ext', () => { }); it('with an schema uri that contains project and ref', () => { - const mockRef = 'AABBCCDD'; - instance.registerCiSchema({ projectNamespace: mockProjectNamespace, projectPath: mockProjectPath, @@ -95,10 +95,11 @@ describe('~/editor/editor_ci_config_ext', () => { instance.registerCiSchema({ projectNamespace: mockProjectNamespace, projectPath: mockProjectPath, + ref: mockRef, }); expect(getConfiguredYmlSchema()).toEqual({ - uri: `${TEST_HOST}/${mockProjectNamespace}/${mockProjectPath}/-/schema/master/${EXTENSION_CI_SCHEMA_FILE_NAME_MATCH}`, + uri: `${TEST_HOST}/${mockProjectNamespace}/${mockProjectPath}/-/schema/${mockRef}/${EXTENSION_CI_SCHEMA_FILE_NAME_MATCH}`, fileMatch: ['another-ci-filename.yml'], }); }); diff --git a/spec/frontend/members/mock_data.js b/spec/frontend/members/mock_data.js index 678cea87dba..4275db5fa9f 100644 --- a/spec/frontend/members/mock_data.js +++ b/spec/frontend/members/mock_data.js @@ -30,6 +30,7 @@ export const member = { usingLicense: false, groupSso: false, groupManagedAccount: false, + provisionedByThisGroup: false, validRoles: { Guest: 10, Reporter: 20, diff --git a/spec/frontend/packages/details/components/app_spec.js b/spec/frontend/packages/details/components/app_spec.js index 4b890f868f4..3132ec61942 100644 --- a/spec/frontend/packages/details/components/app_spec.js +++ b/spec/frontend/packages/details/components/app_spec.js @@ -1,5 +1,6 @@ import { GlEmptyState } from '@gitlab/ui'; import { mount, createLocalVue } from '@vue/test-utils'; +import { nextTick } from 'vue'; import Vuex from 'vuex'; import stubChildren from 'helpers/stub_children'; @@ -109,9 +110,11 @@ describe('PackagesApp', () => { window.location = location; }); - it('renders the app and displays the package title', () => { + it('renders the app and displays the package title', async () => { createComponent(); + await nextTick(); + expect(packageTitle().exists()).toBe(true); }); diff --git a/spec/frontend/packages/details/components/installations_commands_spec.js b/spec/frontend/packages/details/components/installations_commands_spec.js index 065bf503585..164f9f69741 100644 --- a/spec/frontend/packages/details/components/installations_commands_spec.js +++ b/spec/frontend/packages/details/components/installations_commands_spec.js @@ -7,6 +7,7 @@ import MavenInstallation from '~/packages/details/components/maven_installation. import NpmInstallation from '~/packages/details/components/npm_installation.vue'; import NugetInstallation from '~/packages/details/components/nuget_installation.vue'; import PypiInstallation from '~/packages/details/components/pypi_installation.vue'; +import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue'; import { conanPackage, @@ -15,6 +16,7 @@ import { nugetPackage, pypiPackage, composerPackage, + terraformModule, } from '../../mock_data'; describe('InstallationCommands', () => { @@ -32,6 +34,7 @@ describe('InstallationCommands', () => { const nugetInstallation = () => wrapper.find(NugetInstallation); const pypiInstallation = () => wrapper.find(PypiInstallation); const composerInstallation = () => wrapper.find(ComposerInstallation); + const terraformInstallation = () => wrapper.findComponent(TerraformInstallation); afterEach(() => { wrapper.destroy(); @@ -46,6 +49,7 @@ describe('InstallationCommands', () => { ${nugetPackage} | ${nugetInstallation} ${pypiPackage} | ${pypiInstallation} ${composerPackage} | ${composerInstallation} + ${terraformModule} | ${terraformInstallation} `('renders', ({ packageEntity, selector }) => { it(`${packageEntity.package_type} instructions exist`, () => { createComponent({ packageEntity }); diff --git a/spec/frontend/packages/mock_data.js b/spec/frontend/packages/mock_data.js index 46ccf5994b9..33b47cca68b 100644 --- a/spec/frontend/packages/mock_data.js +++ b/spec/frontend/packages/mock_data.js @@ -178,6 +178,20 @@ export const composerPackage = { version: '1.0.0', }; +export const terraformModule = { + created_at: '2015-12-10', + id: 2, + name: 'Test/system-22', + package_type: 'terraform_module', + project_path: 'foo/bar/baz', + projectPathName: 'foo/bar/baz', + project_id: 1, + updated_at: '2015-12-10', + version: '0.1', + versions: [], + _links, +}; + export const mockTags = [ { name: 'foo-1', diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/__snapshots__/terraform_installation_spec.js.snap b/spec/frontend/packages_and_registries/infrastructure_registry/components/__snapshots__/terraform_installation_spec.js.snap new file mode 100644 index 00000000000..427160b45e3 --- /dev/null +++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/__snapshots__/terraform_installation_spec.js.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TerraformInstallation renders all the messages 1`] = ` +<div> + <h3 + class="gl-font-lg" + > + Provision instructions + </h3> + + <code-instruction-stub + copytext="Copy Terraform Command" + instruction="module \\"Test/system-22\\" { + source = \\"foo/Test/system-22\\" + version = \\"0.1\\" +}" + label="Copy and paste into your Terraform configuration, insert the variables, and run Terraform init:" + multiline="true" + trackingaction="" + trackinglabel="" + /> + + <h3 + class="gl-font-lg" + > + Registry setup + </h3> + + <code-instruction-stub + copytext="Copy Terraform Setup Command" + instruction="credentials \\"gitlab.com\\" { + token = \\"<TOKEN>\\" +}" + label="To authorize access to the Terraform registry:" + multiline="true" + trackingaction="" + trackinglabel="" + /> + + <gl-sprintf-stub + message="For more information on the Terraform registry, %{linkStart}see our documentation%{linkEnd}." + /> +</div> +`; diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/details_title_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/details_title_spec.js new file mode 100644 index 00000000000..87e0059344c --- /dev/null +++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/details_title_spec.js @@ -0,0 +1,93 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import { terraformModule, mavenFiles, npmPackage } from 'jest/packages/mock_data'; +import component from '~/packages_and_registries/infrastructure_registry/components/details_title.vue'; +import TitleArea from '~/vue_shared/components/registry/title_area.vue'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('PackageTitle', () => { + let wrapper; + let store; + + function createComponent({ packageFiles = mavenFiles, packageEntity = terraformModule } = {}) { + store = new Vuex.Store({ + state: { + packageEntity, + packageFiles, + }, + getters: { + packagePipeline: ({ packageEntity: { pipeline = null } }) => pipeline, + }, + }); + + wrapper = shallowMount(component, { + localVue, + store, + stubs: { + TitleArea, + }, + }); + return wrapper.vm.$nextTick(); + } + + const findTitleArea = () => wrapper.findComponent(TitleArea); + const packageSize = () => wrapper.find('[data-testid="package-size"]'); + const pipelineProject = () => wrapper.find('[data-testid="pipeline-project"]'); + const packageRef = () => wrapper.find('[data-testid="package-ref"]'); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('module title', () => { + it('is correctly bound', async () => { + await createComponent(); + + expect(findTitleArea().props('title')).toBe(terraformModule.name); + }); + }); + + describe('calculates the package size', () => { + it('correctly calculates the size', async () => { + await createComponent(); + + expect(packageSize().props('text')).toBe('300 bytes'); + }); + }); + + describe('package ref', () => { + it('does not display the ref if missing', async () => { + await createComponent(); + + expect(packageRef().exists()).toBe(false); + }); + + it('correctly shows the package ref if there is one', async () => { + await createComponent({ packageEntity: npmPackage }); + expect(packageRef().props()).toMatchObject({ + text: npmPackage.pipeline.ref, + icon: 'branch', + }); + }); + }); + + describe('pipeline project', () => { + it('does not display the project if missing', async () => { + await createComponent(); + + expect(pipelineProject().exists()).toBe(false); + }); + + it('correctly shows the pipeline project if there is one', async () => { + await createComponent({ packageEntity: npmPackage }); + + expect(pipelineProject().props()).toMatchObject({ + text: npmPackage.pipeline.project.name, + icon: 'review-list', + link: npmPackage.pipeline.project.web_url, + }); + }); + }); +}); diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/terraform_installation_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/terraform_installation_spec.js new file mode 100644 index 00000000000..7a129794d54 --- /dev/null +++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/terraform_installation_spec.js @@ -0,0 +1,61 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import { terraformModule as packageEntity } from 'jest/packages/mock_data'; +import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue'; +import CodeInstructions from '~/vue_shared/components/registry/code_instruction.vue'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('TerraformInstallation', () => { + let wrapper; + + const store = new Vuex.Store({ + state: { + packageEntity, + projectPath: 'foo', + }, + }); + + const findCodeInstructions = () => wrapper.findAllComponents(CodeInstructions); + + function createComponent() { + wrapper = shallowMount(TerraformInstallation, { + localVue, + store, + }); + } + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders all the messages', () => { + expect(wrapper.element).toMatchSnapshot(); + }); + + describe('installation commands', () => { + it('renders the correct command', () => { + expect(findCodeInstructions().at(0).props('instruction')).toMatchInlineSnapshot(` + "module \\"Test/system-22\\" { + source = \\"foo/Test/system-22\\" + version = \\"0.1\\" + }" + `); + }); + }); + + describe('setup commands', () => { + it('renders the correct command', () => { + expect(findCodeInstructions().at(1).props('instruction')).toMatchInlineSnapshot(` + "credentials \\"gitlab.com\\" { + token = \\"<TOKEN>\\" + }" + `); + }); + }); +}); diff --git a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js index 756b8dd980c..6f9245e39aa 100644 --- a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js +++ b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js @@ -9,6 +9,7 @@ import { mockCommitSha, mockProjectPath, mockProjectNamespace, + mockDefaultBranch, } from '../../mock_data'; describe('Pipeline Editor | Text editor component', () => { @@ -38,6 +39,7 @@ describe('Pipeline Editor | Text editor component', () => { projectPath: mockProjectPath, projectNamespace: mockProjectNamespace, ciConfigPath: mockCiConfigPath, + defaultBranch: mockDefaultBranch, glFeatures, }, attrs: { diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index 5c9af1206c0..847f7ec2d74 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -125,10 +125,10 @@ RSpec.describe Gitlab::Database do expect(described_class.postgresql_minimum_supported_version?).to eq(false) end - it 'returns true when using PostgreSQL 11' do + it 'returns false when using PostgreSQL 11' do allow(described_class).to receive(:version).and_return('11') - expect(described_class.postgresql_minimum_supported_version?).to eq(true) + expect(described_class.postgresql_minimum_supported_version?).to eq(false) end it 'returns true when using PostgreSQL 12' do diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 8d27e61450b..085b0b4e259 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -342,7 +342,7 @@ container_repositories: - project - name project: -- external_approval_rules +- external_status_checks - taggings - base_tags - topic_taggings diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb index 4075eb96fc2..3741e01e99a 100644 --- a/spec/models/merge_request_diff_spec.rb +++ b/spec/models/merge_request_diff_spec.rb @@ -418,8 +418,8 @@ RSpec.describe MergeRequestDiff do shared_examples_for 'fetching full diffs' do it 'returns diffs from repository comparison' do expect_next_instance_of(Compare) do |comparison| - expect(comparison).to receive(:diffs_in_batch) - .with(1, 10, diff_options: diff_options) + expect(comparison).to receive(:diffs) + .with(diff_options) .and_call_original end @@ -448,13 +448,13 @@ RSpec.describe MergeRequestDiff do end it_behaves_like 'fetching full diffs' - end - context 'when diff_options include ignore_whitespace_change' do - it_behaves_like 'fetching full diffs' do + context 'when diff_options include ignore_whitespace_change' do let(:diff_options) do { ignore_whitespace_change: true } end + + it_behaves_like 'fetching full diffs' end end @@ -485,6 +485,51 @@ RSpec.describe MergeRequestDiff do 'files/whitespace' ]) end + + context 'when diff_options include ignore_whitespace_change' do + let(:diff_options) do + { ignore_whitespace_change: true } + end + + it 'returns a Gitlab::Diff::FileCollection::Compare with paginated diffs' do + diffs = diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options) + + expect(diffs).to be_a(Gitlab::Diff::FileCollection::Compare) + expect(diffs.diff_files.size).to eq 10 + expect(diffs.pagination_data).to eq(current_page: 1, next_page: 2, total_pages: 2) + end + + it 'returns an empty MergeRequestBatch with empty pagination data when the batch is empty' do + diffs = diff_with_commits.diffs_in_batch(3, 10, diff_options: diff_options) + + expect(diffs).to be_a(Gitlab::Diff::FileCollection::MergeRequestDiffBatch) + expect(diffs.diff_files.size).to eq 0 + expect(diffs.pagination_data).to eq(current_page: nil, next_page: nil, total_pages: nil) + end + + context 'with gradual load enabled' do + before do + stub_feature_flags(diffs_gradual_load: true) + end + + it 'returns pagination data from MergeRequestDiffBatch' do + diffs = diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options) + file_count = diff_with_commits.merge_request_diff_files.count + + expect(diffs).to be_a(Gitlab::Diff::FileCollection::Compare) + expect(diffs.diff_files.size).to eq 10 + expect(diffs.pagination_data).to eq(current_page: nil, next_page: nil, total_pages: file_count) + end + + it 'returns an empty MergeRequestBatch with empty pagination data when the batch is empty' do + diffs = diff_with_commits.diffs_in_batch(30, 10, diff_options: diff_options) + + expect(diffs).to be_a(Gitlab::Diff::FileCollection::MergeRequestDiffBatch) + expect(diffs.diff_files.size).to eq 0 + expect(diffs.pagination_data).to eq(current_page: nil, next_page: nil, total_pages: nil) + end + end + end end end diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb index 42c6c987872..c3abb06c5c1 100644 --- a/spec/requests/api/debian_group_packages_spec.rb +++ b/spec/requests/api/debian_group_packages_spec.rb @@ -7,33 +7,33 @@ RSpec.describe API::DebianGroupPackages do include_context 'Debian repository shared context', :group, false do describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release.gpg' do - let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/Release.gpg" } + let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/Release.gpg" } it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found end describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release' do - let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/Release" } + let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/Release" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO Release' + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Release$/ end describe 'GET groups/:id/-/packages/debian/dists/*distribution/InRelease' do - let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/InRelease" } + let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/InRelease" } it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found end describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do - let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/#{component}/binary-#{architecture}/Packages" } + let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component}/binary-#{architecture}/Packages" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO Packages' + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Packages$/ end describe 'GET groups/:id/-/packages/debian/pool/:component/:letter/:source_package/:file_name' do let(:url) { "/groups/#{container.id}/-/packages/debian/pool/#{component}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture}.deb" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO File' + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO File$/ end end end diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb index 023904cb2b8..c11c4ecc12a 100644 --- a/spec/requests/api/debian_project_packages_spec.rb +++ b/spec/requests/api/debian_project_packages_spec.rb @@ -7,33 +7,33 @@ RSpec.describe API::DebianProjectPackages do include_context 'Debian repository shared context', :project, true do describe 'GET projects/:id/packages/debian/dists/*distribution/Release.gpg' do - let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/Release.gpg" } + let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release.gpg" } it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found end describe 'GET projects/:id/packages/debian/dists/*distribution/Release' do - let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/Release" } + let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO Release' + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Release$/ end describe 'GET projects/:id/packages/debian/dists/*distribution/InRelease' do - let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/InRelease" } + let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/InRelease" } it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found end describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do - let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/#{component}/binary-#{architecture}/Packages" } + let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component}/binary-#{architecture}/Packages" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO Packages' + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Packages$/ end describe 'GET projects/:id/packages/debian/pool/:component/:letter/:source_package/:file_name' do let(:url) { "/projects/#{container.id}/packages/debian/pool/#{component}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture}.deb" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO File' + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO File$/ end describe 'PUT projects/:id/packages/debian/:file_name' do diff --git a/spec/requests/api/project_debian_distributions_spec.rb b/spec/requests/api/project_debian_distributions_spec.rb new file mode 100644 index 00000000000..de7362758f7 --- /dev/null +++ b/spec/requests/api/project_debian_distributions_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe API::ProjectDebianDistributions do + include HttpBasicAuthHelpers + include WorkhorseHelpers + + include_context 'Debian repository shared context', :project, true do + describe 'POST projects/:id/debian_distributions' do + let(:method) { :post } + let(:url) { "/projects/#{container.id}/debian_distributions" } + let(:api_params) { { 'codename': 'my-codename' } } + + it_behaves_like 'Debian repository write endpoint', 'POST distribution request', :created, /^{.*"codename":"my-codename",.*"components":\["main"\],.*"architectures":\["all","amd64"\]/, authenticate_non_public: false + + context 'with invalid parameters' do + let(:api_params) { { codename: distribution.codename } } + + it_behaves_like 'Debian repository write endpoint', 'GET request', :bad_request, /^{"message":{"codename":\["has already been taken"\]}}$/, authenticate_non_public: false + end + end + + describe 'GET projects/:id/debian_distributions' do + let(:url) { "/projects/#{container.id}/debian_distributions" } + + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^\[{.*"codename":"existing-codename\",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/, authenticate_non_public: false + end + + describe 'GET projects/:id/debian_distributions/:codename' do + let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" } + + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^{.*"codename":"existing-codename\",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/, authenticate_non_public: false + end + + describe 'PUT projects/:id/debian_distributions/:codename' do + let(:method) { :put } + let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" } + let(:api_params) { { suite: 'my-suite' } } + + it_behaves_like 'Debian repository write endpoint', 'PUT distribution request', :success, /^{.*"codename":"existing-codename",.*"suite":"my-suite",/, authenticate_non_public: false + + context 'with invalid parameters' do + let(:api_params) { { suite: distribution.codename } } + + it_behaves_like 'Debian repository write endpoint', 'GET request', :bad_request, /^{"message":{"suite":\["has already been taken as Codename"\]}}$/, authenticate_non_public: false + end + end + + describe 'DELETE projects/:id/debian_distributions/:codename' do + let(:method) { :delete } + let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" } + + it_behaves_like 'Debian repository maintainer write endpoint', 'DELETE distribution request', :success, /^{\"message\":\"202 Accepted\"}$/, authenticate_non_public: false + + context 'when destroy fails' do + before do + allow_next_found_instance_of(::Packages::Debian::ProjectDistribution) do |distribution| + expect(distribution).to receive(:destroy).and_return(false) + end + end + + it_behaves_like 'Debian repository maintainer write endpoint', 'GET request', :bad_request, /^{"message":"Failed to delete distribution"}$/, authenticate_non_public: false + end + end + end +end diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb index f092cbf26a4..5a38f92221f 100644 --- a/spec/requests/users_controller_spec.rb +++ b/spec/requests/users_controller_spec.rb @@ -675,48 +675,6 @@ RSpec.describe UsersController do end end - describe 'GET #suggests' do - context 'when user exists' do - it 'returns JSON indicating the user exists and a suggestion' do - get user_suggests_url user.username - - expected_json = { exists: true, suggests: ["#{user.username}1"] }.to_json - expect(response.body).to eq(expected_json) - end - - context 'when the casing is different' do - let(:user) { create(:user, username: 'CamelCaseUser') } - - it 'returns JSON indicating the user exists and a suggestion' do - get user_suggests_url user.username.downcase - - expected_json = { exists: true, suggests: ["#{user.username.downcase}1"] }.to_json - expect(response.body).to eq(expected_json) - end - end - end - - context 'when the user does not exist' do - it 'returns JSON indicating the user does not exist' do - get user_suggests_url 'foo' - - expected_json = { exists: false, suggests: [] }.to_json - expect(response.body).to eq(expected_json) - end - - context 'when a user changed their username' do - let(:redirect_route) { user.namespace.redirect_routes.create!(path: 'old-username') } - - it 'returns JSON indicating a user by that username does not exist' do - get user_suggests_url 'old-username' - - expected_json = { exists: false, suggests: [] }.to_json - expect(response.body).to eq(expected_json) - end - end - end - end - describe '#ensure_canonical_path' do before do sign_in(user) diff --git a/spec/services/packages/debian/create_distribution_service_spec.rb b/spec/services/packages/debian/create_distribution_service_spec.rb index 87cf1070075..ecf82c6a1db 100644 --- a/spec/services/packages/debian/create_distribution_service_spec.rb +++ b/spec/services/packages/debian/create_distribution_service_spec.rb @@ -4,8 +4,12 @@ require 'spec_helper' RSpec.describe Packages::Debian::CreateDistributionService do RSpec.shared_examples 'Create Debian Distribution' do |expected_message, expected_components, expected_architectures| + let_it_be(:container) { create(container_type) } # rubocop:disable Rails/SaveBang + it 'returns ServiceResponse', :aggregate_failures do if expected_message.nil? + expect(::Packages::Debian::GenerateDistributionWorker).to receive(:perform_async).with(container_type, an_instance_of(Integer)) + expect { response } .to change { container.debian_distributions.klass.all.count } .from(0).to(1) @@ -18,6 +22,7 @@ RSpec.describe Packages::Debian::CreateDistributionService do .and not_change { Packages::Debian::ProjectComponentFile.count } .and not_change { Packages::Debian::GroupComponentFile.count } else + expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async) expect { response } .to not_change { container.debian_distributions.klass.all.count } .and not_change { container.debian_distributions.count } @@ -109,13 +114,13 @@ RSpec.describe Packages::Debian::CreateDistributionService do let(:response) { subject.execute } context 'within a projet' do - let_it_be(:container) { create(:project) } + let_it_be(:container_type) { :project } it_behaves_like 'Debian Create Distribution Service' end context 'within a group' do - let_it_be(:container) { create(:group) } + let_it_be(:container_type) { :group } it_behaves_like 'Debian Create Distribution Service' end diff --git a/spec/services/packages/debian/destroy_distribution_service_spec.rb b/spec/services/packages/debian/destroy_distribution_service_spec.rb deleted file mode 100644 index e4c43884bb4..00000000000 --- a/spec/services/packages/debian/destroy_distribution_service_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Packages::Debian::DestroyDistributionService do - RSpec.shared_examples 'Destroy Debian Distribution' do |expected_message| - it 'returns ServiceResponse', :aggregate_failures do - if expected_message.nil? - expect { response } - .to change { container.debian_distributions.klass.all.count } - .from(1).to(0) - .and change { container.debian_distributions.count } - .from(1).to(0) - .and change { component1.class.all.count } - .from(2).to(0) - .and change { architecture1.class.all.count } - .from(3).to(0) - .and change { component_file1.class.all.count } - .from(4).to(0) - else - expect { response } - .to not_change { container.debian_distributions.klass.all.count } - .and not_change { container.debian_distributions.count } - .and not_change { component1.class.all.count } - .and not_change { architecture1.class.all.count } - .and not_change { component_file1.class.all.count } - end - - expect(response).to be_a(ServiceResponse) - expect(response.success?).to eq(expected_message.nil?) - expect(response.error?).to eq(!expected_message.nil?) - expect(response.message).to eq(expected_message) - - if expected_message.nil? - expect(response.payload).to eq({}) - else - expect(response.payload).to eq(distribution: distribution) - end - end - end - - RSpec.shared_examples 'Debian Destroy Distribution Service' do |container_type, can_freeze| - context "with a Debian #{container_type} distribution" do - let_it_be(:container, freeze: can_freeze) { create(container_type) } # rubocop:disable Rails/SaveBang - let_it_be(:distribution, freeze: can_freeze) { create("debian_#{container_type}_distribution", container: container) } - let_it_be(:component1, freeze: can_freeze) { create("debian_#{container_type}_component", distribution: distribution, name: 'component1') } - let_it_be(:component2, freeze: can_freeze) { create("debian_#{container_type}_component", distribution: distribution, name: 'component2') } - let_it_be(:architecture0, freeze: true) { create("debian_#{container_type}_architecture", distribution: distribution, name: 'all') } - let_it_be(:architecture1, freeze: can_freeze) { create("debian_#{container_type}_architecture", distribution: distribution, name: 'architecture1') } - let_it_be(:architecture2, freeze: can_freeze) { create("debian_#{container_type}_architecture", distribution: distribution, name: 'architecture2') } - let_it_be(:component_file1, freeze: can_freeze) { create("debian_#{container_type}_component_file", :source, component: component1) } - let_it_be(:component_file2, freeze: can_freeze) { create("debian_#{container_type}_component_file", component: component1, architecture: architecture1) } - let_it_be(:component_file3, freeze: can_freeze) { create("debian_#{container_type}_component_file", :source, component: component2) } - let_it_be(:component_file4, freeze: can_freeze) { create("debian_#{container_type}_component_file", component: component2, architecture: architecture2) } - - subject { described_class.new(distribution) } - - let(:response) { subject.execute } - - context 'with a distribution' do - it_behaves_like 'Destroy Debian Distribution' - end - - context 'when destroy fails' do - let(:distribution) { create("debian_#{container_type}_distribution", container: container) } - - before do - expect(distribution).to receive(:destroy).and_return(false) - end - - it_behaves_like 'Destroy Debian Distribution', "Unable to destroy Debian #{container_type} distribution" - end - end - end - - it_behaves_like 'Debian Destroy Distribution Service', :project, true - it_behaves_like 'Debian Destroy Distribution Service', :group, false -end diff --git a/spec/services/packages/debian/update_distribution_service_spec.rb b/spec/services/packages/debian/update_distribution_service_spec.rb index 852fc713e34..2aa34a62111 100644 --- a/spec/services/packages/debian/update_distribution_service_spec.rb +++ b/spec/services/packages/debian/update_distribution_service_spec.rb @@ -6,6 +6,8 @@ RSpec.describe Packages::Debian::UpdateDistributionService do RSpec.shared_examples 'Update Debian Distribution' do |expected_message, expected_components, expected_architectures, component_file_delta = 0| it 'returns ServiceResponse', :aggregate_failures do expect(distribution).to receive(:update).with(simple_params).and_call_original if expected_message.nil? + expect(::Packages::Debian::GenerateDistributionWorker).to receive(:perform_async).with(distribution.class.container_type, distribution.id).and_call_original if expected_message.nil? + expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async) unless expected_message.nil? if component_file_delta.zero? expect { response } diff --git a/spec/services/packages/nuget/metadata_extraction_service_spec.rb b/spec/services/packages/nuget/metadata_extraction_service_spec.rb index b3980d3b7fa..79428b58bd9 100644 --- a/spec/services/packages/nuget/metadata_extraction_service_spec.rb +++ b/spec/services/packages/nuget/metadata_extraction_service_spec.rb @@ -24,22 +24,7 @@ RSpec.describe Packages::Nuget::MetadataExtractionService do package_tags: [] } - context 'with packages_nuget_archive_new_file_reader enabled' do - before do - expect(service).to receive(:with_new_file_reader).and_call_original - end - - it { is_expected.to eq(expected_metadata) } - end - - context 'with packages_nuget_archive_new_file_reader disabled' do - before do - stub_feature_flags(packages_nuget_archive_new_file_reader: false) - expect(service).to receive(:with_legacy_file_reader).and_call_original - end - - it { is_expected.to eq(expected_metadata) } - end + it { is_expected.to eq(expected_metadata) } end context 'with nuspec file' do diff --git a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb index e41475f786d..ffe1a5b7646 100644 --- a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb +++ b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb @@ -63,191 +63,178 @@ RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_ end end - shared_examples 'handling all conditions' do - context 'with no existing package' do - let(:package_id) { package.id } - - it 'updates package and package file' do - expect { subject } - .to change { ::Packages::Package.count }.by(1) - .and change { Packages::Dependency.count }.by(1) - .and change { Packages::DependencyLink.count }.by(1) - .and change { ::Packages::Nuget::Metadatum.count }.by(0) - - expect(package.reload.name).to eq(package_name) - expect(package.version).to eq(package_version) - expect(package).to be_default - expect(package_file.reload.file_name).to eq(package_file_name) - # hard reset needed to properly reload package_file.file - expect(Packages::PackageFile.find(package_file.id).file.size).not_to eq 0 - end - - it_behaves_like 'taking the lease' - - it_behaves_like 'not updating the package if the lease is taken' + context 'with no existing package' do + let(:package_id) { package.id } + + it 'updates package and package file' do + expect { subject } + .to change { ::Packages::Package.count }.by(1) + .and change { Packages::Dependency.count }.by(1) + .and change { Packages::DependencyLink.count }.by(1) + .and change { ::Packages::Nuget::Metadatum.count }.by(0) + + expect(package.reload.name).to eq(package_name) + expect(package.version).to eq(package_version) + expect(package).to be_default + expect(package_file.reload.file_name).to eq(package_file_name) + # hard reset needed to properly reload package_file.file + expect(Packages::PackageFile.find(package_file.id).file.size).not_to eq 0 end - context 'with existing package' do - let!(:existing_package) { create(:nuget_package, project: package.project, name: package_name, version: package_version) } - let(:package_id) { existing_package.id } + it_behaves_like 'taking the lease' - it 'link existing package and updates package file' do - expect(service).to receive(:try_obtain_lease).and_call_original + it_behaves_like 'not updating the package if the lease is taken' + end - expect { subject } - .to change { ::Packages::Package.count }.by(-1) - .and change { Packages::Dependency.count }.by(0) - .and change { Packages::DependencyLink.count }.by(0) - .and change { Packages::Nuget::DependencyLinkMetadatum.count }.by(0) - .and change { ::Packages::Nuget::Metadatum.count }.by(0) - expect(package_file.reload.file_name).to eq(package_file_name) - expect(package_file.package).to eq(existing_package) - end + context 'with existing package' do + let!(:existing_package) { create(:nuget_package, project: package.project, name: package_name, version: package_version) } + let(:package_id) { existing_package.id } - it_behaves_like 'taking the lease' + it 'link existing package and updates package file' do + expect(service).to receive(:try_obtain_lease).and_call_original - it_behaves_like 'not updating the package if the lease is taken' + expect { subject } + .to change { ::Packages::Package.count }.by(-1) + .and change { Packages::Dependency.count }.by(0) + .and change { Packages::DependencyLink.count }.by(0) + .and change { Packages::Nuget::DependencyLinkMetadatum.count }.by(0) + .and change { ::Packages::Nuget::Metadatum.count }.by(0) + expect(package_file.reload.file_name).to eq(package_file_name) + expect(package_file.package).to eq(existing_package) end - context 'with a nuspec file with metadata' do - let(:nuspec_filepath) { 'packages/nuget/with_metadata.nuspec' } - let(:expected_tags) { %w(foo bar test tag1 tag2 tag3 tag4 tag5) } + it_behaves_like 'taking the lease' - before do - allow_next_instance_of(Packages::Nuget::MetadataExtractionService) do |service| - allow(service) - .to receive(:nuspec_file_content).and_return(fixture_file(nuspec_filepath)) - end - end + it_behaves_like 'not updating the package if the lease is taken' + end - it 'creates tags' do - expect(service).to receive(:try_obtain_lease).and_call_original - expect { subject }.to change { ::Packages::Tag.count }.by(8) - expect(package.reload.tags.map(&:name)).to contain_exactly(*expected_tags) - end + context 'with a nuspec file with metadata' do + let(:nuspec_filepath) { 'packages/nuget/with_metadata.nuspec' } + let(:expected_tags) { %w(foo bar test tag1 tag2 tag3 tag4 tag5) } - context 'with existing package and tags' do - let!(:existing_package) { create(:nuget_package, project: package.project, name: 'DummyProject.WithMetadata', version: '1.2.3') } - let!(:tag1) { create(:packages_tag, package: existing_package, name: 'tag1') } - let!(:tag2) { create(:packages_tag, package: existing_package, name: 'tag2') } - let!(:tag3) { create(:packages_tag, package: existing_package, name: 'tag_not_in_metadata') } - - it 'creates tags and deletes those not in metadata' do - expect(service).to receive(:try_obtain_lease).and_call_original - expect { subject }.to change { ::Packages::Tag.count }.by(5) - expect(existing_package.tags.map(&:name)).to contain_exactly(*expected_tags) - end + before do + allow_next_instance_of(Packages::Nuget::MetadataExtractionService) do |service| + allow(service) + .to receive(:nuspec_file_content).and_return(fixture_file(nuspec_filepath)) end + end - it 'creates nuget metadatum' do - expect { subject } - .to change { ::Packages::Package.count }.by(1) - .and change { ::Packages::Nuget::Metadatum.count }.by(1) + it 'creates tags' do + expect(service).to receive(:try_obtain_lease).and_call_original + expect { subject }.to change { ::Packages::Tag.count }.by(8) + expect(package.reload.tags.map(&:name)).to contain_exactly(*expected_tags) + end - metadatum = package_file.reload.package.nuget_metadatum - expect(metadatum.license_url).to eq('https://opensource.org/licenses/MIT') - expect(metadatum.project_url).to eq('https://gitlab.com/gitlab-org/gitlab') - expect(metadatum.icon_url).to eq('https://opensource.org/files/osi_keyhole_300X300_90ppi_0.png') + context 'with existing package and tags' do + let!(:existing_package) { create(:nuget_package, project: package.project, name: 'DummyProject.WithMetadata', version: '1.2.3') } + let!(:tag1) { create(:packages_tag, package: existing_package, name: 'tag1') } + let!(:tag2) { create(:packages_tag, package: existing_package, name: 'tag2') } + let!(:tag3) { create(:packages_tag, package: existing_package, name: 'tag_not_in_metadata') } + + it 'creates tags and deletes those not in metadata' do + expect(service).to receive(:try_obtain_lease).and_call_original + expect { subject }.to change { ::Packages::Tag.count }.by(5) + expect(existing_package.tags.map(&:name)).to contain_exactly(*expected_tags) end + end - context 'with too long url' do - let_it_be(:too_long_url) { "http://localhost/#{'bananas' * 50}" } + it 'creates nuget metadatum' do + expect { subject } + .to change { ::Packages::Package.count }.by(1) + .and change { ::Packages::Nuget::Metadatum.count }.by(1) - let(:metadata) { { package_name: package_name, package_version: package_version, license_url: too_long_url } } + metadatum = package_file.reload.package.nuget_metadatum + expect(metadatum.license_url).to eq('https://opensource.org/licenses/MIT') + expect(metadatum.project_url).to eq('https://gitlab.com/gitlab-org/gitlab') + expect(metadatum.icon_url).to eq('https://opensource.org/files/osi_keyhole_300X300_90ppi_0.png') + end + + context 'with too long url' do + let_it_be(:too_long_url) { "http://localhost/#{'bananas' * 50}" } - before do - allow(service).to receive(:metadata).and_return(metadata) - end + let(:metadata) { { package_name: package_name, package_version: package_version, license_url: too_long_url } } - it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError + before do + allow(service).to receive(:metadata).and_return(metadata) end + + it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError end + end - context 'with nuspec file with dependencies' do - let(:nuspec_filepath) { 'packages/nuget/with_dependencies.nuspec' } - let(:package_name) { 'Test.Package' } - let(:package_version) { '3.5.2' } - let(:package_file_name) { 'test.package.3.5.2.nupkg' } + context 'with nuspec file with dependencies' do + let(:nuspec_filepath) { 'packages/nuget/with_dependencies.nuspec' } + let(:package_name) { 'Test.Package' } + let(:package_version) { '3.5.2' } + let(:package_file_name) { 'test.package.3.5.2.nupkg' } - before do - allow_next_instance_of(Packages::Nuget::MetadataExtractionService) do |service| - allow(service) - .to receive(:nuspec_file_content).and_return(fixture_file(nuspec_filepath)) - end + before do + allow_next_instance_of(Packages::Nuget::MetadataExtractionService) do |service| + allow(service) + .to receive(:nuspec_file_content).and_return(fixture_file(nuspec_filepath)) end + end - it 'updates package and package file' do - expect { subject } - .to change { ::Packages::Package.count }.by(1) - .and change { Packages::Dependency.count }.by(4) - .and change { Packages::DependencyLink.count }.by(4) - .and change { Packages::Nuget::DependencyLinkMetadatum.count }.by(2) - - expect(package.reload.name).to eq(package_name) - expect(package.version).to eq(package_version) - expect(package).to be_default - expect(package_file.reload.file_name).to eq(package_file_name) - # hard reset needed to properly reload package_file.file - expect(Packages::PackageFile.find(package_file.id).file.size).not_to eq 0 - end + it 'updates package and package file' do + expect { subject } + .to change { ::Packages::Package.count }.by(1) + .and change { Packages::Dependency.count }.by(4) + .and change { Packages::DependencyLink.count }.by(4) + .and change { Packages::Nuget::DependencyLinkMetadatum.count }.by(2) + + expect(package.reload.name).to eq(package_name) + expect(package.version).to eq(package_version) + expect(package).to be_default + expect(package_file.reload.file_name).to eq(package_file_name) + # hard reset needed to properly reload package_file.file + expect(Packages::PackageFile.find(package_file.id).file.size).not_to eq 0 end + end - context 'with package file not containing a nuspec file' do - before do - allow_next_instance_of(Zip::File) do |file| - allow(file).to receive(:glob).and_return([]) - end + context 'with package file not containing a nuspec file' do + before do + allow_next_instance_of(Zip::File) do |file| + allow(file).to receive(:glob).and_return([]) end - - it_behaves_like 'raising an', ::Packages::Nuget::MetadataExtractionService::ExtractionError end - context 'with an invalid package name' do - invalid_names = [ - '', - 'My/package', - '../../../my_package', - '%2e%2e%2fmy_package' - ] + it_behaves_like 'raising an', ::Packages::Nuget::MetadataExtractionService::ExtractionError + end - invalid_names.each do |invalid_name| - before do - allow(service).to receive(:package_name).and_return(invalid_name) - end + context 'with an invalid package name' do + invalid_names = [ + '', + 'My/package', + '../../../my_package', + '%2e%2e%2fmy_package' + ] - it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError + invalid_names.each do |invalid_name| + before do + allow(service).to receive(:package_name).and_return(invalid_name) end - end - context 'with an invalid package version' do - invalid_versions = [ - '', - '555', - '1.2', - '1./2.3', - '../../../../../1.2.3', - '%2e%2e%2f1.2.3' - ] - - invalid_versions.each do |invalid_version| - before do - allow(service).to receive(:package_version).and_return(invalid_version) - end - - it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError - end + it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError end end - it_behaves_like 'handling all conditions' + context 'with an invalid package version' do + invalid_versions = [ + '', + '555', + '1.2', + '1./2.3', + '../../../../../1.2.3', + '%2e%2e%2f1.2.3' + ] + + invalid_versions.each do |invalid_version| + before do + allow(service).to receive(:package_version).and_return(invalid_version) + end - context 'with packages_nuget_archive_new_file_reader disabled' do - before do - stub_feature_flags(packages_nuget_archive_new_file_reader: false) - stub_package_file_object_storage(enabled: true, direct_upload: true) + it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError end - - it_behaves_like 'handling all conditions' end end end diff --git a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb index 7c47b900dc0..0530aa8c760 100644 --- a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb @@ -12,7 +12,35 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_ let_it_be(:user, freeze: true) { create(:user) } let_it_be(:personal_access_token, freeze: true) { create(:personal_access_token, user: user) } - let(:distribution) { 'bullseye' } + let_it_be(:private_distribution, freeze: true) { create("debian_#{container_type}_distribution", container: private_container, codename: 'existing-codename') } + let_it_be(:private_component, freeze: true) { create("debian_#{container_type}_component", distribution: private_distribution, name: 'existing-component') } + let_it_be(:private_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'all') } + let_it_be(:private_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'existing-arch') } + + let_it_be(:public_distribution, freeze: true) { create("debian_#{container_type}_distribution", container: public_container, codename: 'existing-codename') } + let_it_be(:public_component, freeze: true) { create("debian_#{container_type}_component", distribution: public_distribution, name: 'existing-component') } + let_it_be(:public_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'all') } + let_it_be(:public_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'existing-arch') } + + if container_type == :group + let_it_be(:private_project) { create(:project, :private, group: private_container) } + let_it_be(:public_project) { create(:project, :public, group: public_container) } + let_it_be(:private_project_distribution) { create(:debian_project_distribution, container: private_project, codename: 'existing-codename') } + let_it_be(:public_project_distribution) { create(:debian_project_distribution, container: public_project, codename: 'existing-codename') } + else + let_it_be(:private_project) { private_container } + let_it_be(:public_project) { public_container } + let_it_be(:private_project_distribution) { private_distribution } + let_it_be(:public_project_distribution) { public_distribution } + end + + let_it_be(:private_package) { create(:debian_package, project: private_project, published_in: private_project_distribution) } + let_it_be(:public_package) { create(:debian_package, project: public_project, published_in: public_project_distribution) } + + let(:visibility_level) { :public } + + let(:distribution) { { private: private_distribution, public: public_distribution }[visibility_level] } + let(:component) { 'main' } let(:architecture) { 'amd64' } let(:source_package) { 'sample' } @@ -97,7 +125,7 @@ RSpec.shared_examples 'Debian repository GET request' do |status, body = nil| expect(response).to have_gitlab_http_status(status) unless body.nil? - expect(response.body).to eq(body) + expect(response.body).to match(body) end end end @@ -125,7 +153,7 @@ RSpec.shared_examples 'Debian repository upload request' do |status, body = nil| expect(response.media_type).to eq('text/plain') unless body.nil? - expect(response.body).to eq(body) + expect(response.body).to match(body) end end it_behaves_like 'a package tracking event', described_class.name, 'push_package' @@ -136,7 +164,7 @@ RSpec.shared_examples 'Debian repository upload request' do |status, body = nil| expect(response).to have_gitlab_http_status(status) unless body.nil? - expect(response.body).to eq(body) + expect(response.body).to match(body) end end end @@ -182,18 +210,112 @@ RSpec.shared_examples 'Debian repository upload authorize request' do |status, b expect(response).to have_gitlab_http_status(status) unless body.nil? - expect(response.body).to eq(body) + expect(response.body).to match(body) + end + end + end +end + +RSpec.shared_examples 'Debian repository POST distribution request' do |status, body| + and_body = body.nil? ? '' : ' and expected body' + + if status == :created + it 'creates distribution', :aggregate_failures do + expect(::Packages::Debian::CreateDistributionService).to receive(:new).with(container, user, api_params).and_call_original + + expect { subject } + .to change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }.by(1) + .and change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }.by(1) + .and change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }.by(2) + + expect(response).to have_gitlab_http_status(status) + expect(response.media_type).to eq('application/json') + + unless body.nil? + expect(response.body).to match(body) + end + end + else + it "returns #{status}#{and_body}", :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(status) + + unless body.nil? + expect(response.body).to match(body) + end + end + end +end + +RSpec.shared_examples 'Debian repository PUT distribution request' do |status, body| + and_body = body.nil? ? '' : ' and expected body' + + if status == :success + it 'updates distribution', :aggregate_failures do + expect(::Packages::Debian::UpdateDistributionService).to receive(:new).with(distribution, api_params.except(:codename)).and_call_original + + expect { subject } + .to not_change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count } + .and not_change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count } + .and not_change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count } + + expect(response).to have_gitlab_http_status(status) + expect(response.media_type).to eq('application/json') + + unless body.nil? + expect(response.body).to match(body) + end + end + else + it "returns #{status}#{and_body}", :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(status) + + unless body.nil? + expect(response.body).to match(body) end end end end -RSpec.shared_examples 'rejects Debian access with unknown container id' do +RSpec.shared_examples 'Debian repository DELETE distribution request' do |status, body| + and_body = body.nil? ? '' : ' and expected body' + + if status == :success + it 'updates distribution', :aggregate_failures do + expect { subject } + .to change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }.by(-1) + .and change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }.by(-1) + .and change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }.by(-2) + + expect(response).to have_gitlab_http_status(status) + expect(response.media_type).to eq('application/json') + + unless body.nil? + expect(response.body).to match(body) + end + end + else + it "returns #{status}#{and_body}", :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(status) + + unless body.nil? + expect(response.body).to match(body) + end + end + end +end + +RSpec.shared_examples 'rejects Debian access with unknown container id' do |hidden_status| context 'with an unknown container' do let(:container) { double(id: non_existing_record_id) } context 'as anonymous' do - it_behaves_like 'Debian repository GET request', :unauthorized, nil + it_behaves_like 'Debian repository GET request', hidden_status, nil end context 'as authenticated user' do @@ -204,19 +326,25 @@ RSpec.shared_examples 'rejects Debian access with unknown container id' do end end -RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, success_status, success_body| +RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true| + hidden_status = if authenticate_non_public + :unauthorized + else + :not_found + end + context 'with valid container' do using RSpec::Parameterized::TableSyntax where(:visibility_level, :user_role, :member, :user_token, :expected_status, :expected_body) do :public | :developer | true | true | success_status | success_body :public | :guest | true | true | success_status | success_body - :public | :developer | true | false | success_status | success_body - :public | :guest | true | false | success_status | success_body + :public | :developer | true | false | :unauthorized | nil + :public | :guest | true | false | :unauthorized | nil :public | :developer | false | true | success_status | success_body :public | :guest | false | true | success_status | success_body - :public | :developer | false | false | success_status | success_body - :public | :guest | false | false | success_status | success_body + :public | :developer | false | false | :unauthorized | nil + :public | :guest | false | false | :unauthorized | nil :public | :anonymous | false | true | success_status | success_body :private | :developer | true | true | success_status | success_body :private | :guest | true | true | :forbidden | nil @@ -226,7 +354,7 @@ RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, su :private | :guest | false | true | :not_found | nil :private | :developer | false | false | :unauthorized | nil :private | :guest | false | false | :unauthorized | nil - :private | :anonymous | false | true | :unauthorized | nil + :private | :anonymous | false | true | hidden_status | nil end with_them do @@ -236,10 +364,16 @@ RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, su end end - it_behaves_like 'rejects Debian access with unknown container id' + it_behaves_like 'rejects Debian access with unknown container id', hidden_status end -RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, success_status, success_body| +RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true| + hidden_status = if authenticate_non_public + :unauthorized + else + :not_found + end + context 'with valid container' do using RSpec::Parameterized::TableSyntax @@ -261,7 +395,50 @@ RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, s :private | :guest | false | true | :not_found | nil :private | :developer | false | false | :unauthorized | nil :private | :guest | false | false | :unauthorized | nil - :private | :anonymous | false | true | :unauthorized | nil + :private | :anonymous | false | true | hidden_status | nil + end + + with_them do + include_context 'Debian repository access', params[:visibility_level], params[:user_role], params[:member], params[:user_token], :basic do + it_behaves_like "Debian repository #{desired_behavior}", params[:expected_status], params[:expected_body] + end + end + end + + it_behaves_like 'rejects Debian access with unknown container id', hidden_status +end + +RSpec.shared_examples 'Debian repository maintainer write endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true| + hidden_status = if authenticate_non_public + :unauthorized + else + :not_found + end + + context 'with valid container' do + using RSpec::Parameterized::TableSyntax + + where(:visibility_level, :user_role, :member, :user_token, :expected_status, :expected_body) do + :public | :maintainer | true | true | success_status | success_body + :public | :developer | true | true | :forbidden | nil + :public | :guest | true | true | :forbidden | nil + :public | :maintainer | true | false | :unauthorized | nil + :public | :guest | true | false | :unauthorized | nil + :public | :maintainer | false | true | :forbidden | nil + :public | :guest | false | true | :forbidden | nil + :public | :maintainer | false | false | :unauthorized | nil + :public | :guest | false | false | :unauthorized | nil + :public | :anonymous | false | true | :unauthorized | nil + :private | :maintainer | true | true | success_status | success_body + :private | :developer | true | true | :forbidden | nil + :private | :guest | true | true | :forbidden | nil + :private | :maintainer | true | false | :unauthorized | nil + :private | :guest | true | false | :unauthorized | nil + :private | :maintainer | false | true | :not_found | nil + :private | :guest | false | true | :not_found | nil + :private | :maintainer | false | false | :unauthorized | nil + :private | :guest | false | false | :unauthorized | nil + :private | :anonymous | false | true | hidden_status | nil end with_them do @@ -271,5 +448,5 @@ RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, s end end - it_behaves_like 'rejects Debian access with unknown container id' + it_behaves_like 'rejects Debian access with unknown container id', hidden_status end |