diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-27 15:11:21 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-27 15:11:21 +0300 |
commit | 5471fef2360f9bcf604a026d5807a554dae243e9 (patch) | |
tree | 8b9c82036dc5da7fdf30ba3c2c5f85997ad41eaa /spec | |
parent | b6f32e82a08a171debbb57236e8995b8d741e6a5 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
23 files changed, 1126 insertions, 852 deletions
diff --git a/spec/controllers/projects/registry/repositories_controller_spec.rb b/spec/controllers/projects/registry/repositories_controller_spec.rb index 834fdddd583..a07a3641edf 100644 --- a/spec/controllers/projects/registry/repositories_controller_spec.rb +++ b/spec/controllers/projects/registry/repositories_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::Registry::RepositoriesController do +RSpec.describe Projects::Registry::RepositoriesController, feature_category: :container_registry do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :private) } @@ -103,8 +103,6 @@ RSpec.describe Projects::Registry::RepositoriesController do end it 'marks the repository as delete_scheduled' do - expect(DeleteContainerRepositoryWorker).not_to receive(:perform_async).with(user.id, repository.id) - expect { delete_repository(repository) } .to change { repository.reload.status }.from(nil).to('delete_scheduled') @@ -113,8 +111,6 @@ RSpec.describe Projects::Registry::RepositoriesController do end it 'tracks the event', :snowplow do - allow(DeleteContainerRepositoryWorker).to receive(:perform_async).with(user.id, repository.id) - delete_repository(repository) expect_snowplow_event(category: anything, action: 'delete_repository') diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index ed2c712feb1..6e3a5ecaac7 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -18,7 +18,6 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do let_it_be(:confidential_issue) { create(:issue, project: project, assignees: [user], milestone: milestone, confidential: true) } let(:current_user) { user } - let(:visible_label_selection_on_metadata) { false } before_all do project.add_maintainer(user) @@ -28,7 +27,6 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do before do stub_licensed_features(multiple_issue_assignees: false, issue_weights: false) - stub_feature_flags(visible_label_selection_on_metadata: visible_label_selection_on_metadata) sign_in(current_user) end @@ -117,232 +115,125 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do end end - context 'with the visible_label_selection_on_metadata feature flag enabled' do - let(:visible_label_selection_on_metadata) { true } - - it 'allows user to create new issue' do - fill_in 'issue_title', with: 'title' - fill_in 'issue_description', with: 'title' - - expect(find('a', text: 'Assign to me')).to be_visible - click_button 'Unassigned' - - wait_for_requests - - page.within '.dropdown-menu-user' do - click_link user2.name - end - expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user2.id.to_s) - page.within '.js-assignee-search' do - expect(page).to have_content user2.name - end - expect(find('a', text: 'Assign to me')).to be_visible - - click_link 'Assign to me' - assignee_ids = page.all('input[name="issue[assignee_ids][]"]', visible: false) - - expect(assignee_ids[0].value).to match(user.id.to_s) - - page.within '.js-assignee-search' do - expect(page).to have_content user.name - end - expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible + it 'allows user to create new issue' do + fill_in 'issue_title', with: 'title' + fill_in 'issue_description', with: 'title' - click_button 'Select milestone' - click_button milestone.title - expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s) - expect(page).to have_button milestone.title + expect(find('a', text: 'Assign to me')).to be_visible + click_button 'Unassigned' - click_button _('Select label') - wait_for_all_requests - page.within '[data-testid="sidebar-labels"]' do - click_button label.title - click_button label2.title - click_button _('Close') - wait_for_requests - page.within('[data-testid="embedded-labels-list"]') do - expect(page).to have_content(label.title) - expect(page).to have_content(label2.title) - end - end + wait_for_requests - click_button 'Create issue' + page.within '.dropdown-menu-user' do + click_link user2.name + end + expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user2.id.to_s) + page.within '.js-assignee-search' do + expect(page).to have_content user2.name + end + expect(find('a', text: 'Assign to me')).to be_visible - page.within '.issuable-sidebar' do - page.within '.assignee' do - expect(page).to have_content "Assignee" - end + click_link 'Assign to me' + assignee_ids = page.all('input[name="issue[assignee_ids][]"]', visible: false) - page.within '.milestone' do - expect(page).to have_content milestone.title - end + expect(assignee_ids[0].value).to match(user.id.to_s) - page.within '.labels' do - expect(page).to have_content label.title - expect(page).to have_content label2.title - end - end + page.within '.js-assignee-search' do + expect(page).to have_content user.name + end + expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible - page.within '.breadcrumbs' do - issue = Issue.find_by(title: 'title') + click_button 'Select milestone' + click_button milestone.title + expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s) + expect(page).to have_button milestone.title - expect(page).to have_text("Issues #{issue.to_reference}") + click_button _('Select label') + wait_for_all_requests + page.within '[data-testid="sidebar-labels"]' do + click_button label.title + click_button label2.title + click_button _('Close') + wait_for_requests + page.within('[data-testid="embedded-labels-list"]') do + expect(page).to have_content(label.title) + expect(page).to have_content(label2.title) end end - it 'correctly updates the dropdown toggle when removing a label' do - click_button _('Select label') - - wait_for_all_requests - - page.within '[data-testid="sidebar-labels"]' do - click_button label.title - click_button _('Close') - - wait_for_requests - - page.within('[data-testid="embedded-labels-list"]') do - expect(page).to have_content(label.title) - end + click_button 'Create issue' - expect(page.find('.gl-dropdown-button-text')).to have_content(label.title) + page.within '.issuable-sidebar' do + page.within '.assignee' do + expect(page).to have_content "Assignee" end - click_button label.title, class: 'gl-dropdown-toggle' - - wait_for_all_requests - - page.within '[data-testid="sidebar-labels"]' do - click_button label.title, class: 'dropdown-item' - click_button _('Close') - - wait_for_requests + page.within '.milestone' do + expect(page).to have_content milestone.title + end - expect(page).not_to have_selector('[data-testid="embedded-labels-list"]') - expect(page.find('.gl-dropdown-button-text')).to have_content(_('Select label')) + page.within '.labels' do + expect(page).to have_content label.title + expect(page).to have_content label2.title end end - it 'clears label search input field when a label is selected', :js do - click_button _('Select label') + page.within '.breadcrumbs' do + issue = Issue.find_by(title: 'title') - wait_for_all_requests - - page.within '[data-testid="sidebar-labels"]' do - search_field = find('input[type="search"]') - - search_field.native.send_keys(label.title) - - expect(page).to have_css('.gl-search-box-by-type-clear') - - click_button label.title, class: 'dropdown-item' - - expect(page).not_to have_css('.gl-search-box-by-type-clear') - expect(search_field.value).to eq '' - end + expect(page).to have_text("Issues #{issue.to_reference}") end end - context 'with the visible_label_selection_on_metadata feature flag disabled' do - let(:visible_label_selection_on_metadata) { false } + it 'correctly updates the dropdown toggle when removing a label' do + click_button _('Select label') - it 'allows user to create new issue' do - fill_in 'issue_title', with: 'title' - fill_in 'issue_description', with: 'title' + wait_for_all_requests - expect(find('a', text: 'Assign to me')).to be_visible - click_button 'Unassigned' + page.within '[data-testid="sidebar-labels"]' do + click_button label.title + click_button _('Close') wait_for_requests - page.within '.dropdown-menu-user' do - click_link user2.name - end - expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user2.id.to_s) - page.within '.js-assignee-search' do - expect(page).to have_content user2.name - end - expect(find('a', text: 'Assign to me')).to be_visible - - click_link 'Assign to me' - assignee_ids = page.all('input[name="issue[assignee_ids][]"]', visible: false) - - expect(assignee_ids[0].value).to match(user.id.to_s) - - page.within '.js-assignee-search' do - expect(page).to have_content user.name + page.within('[data-testid="embedded-labels-list"]') do + expect(page).to have_content(label.title) end - expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible - - click_button 'Select milestone' - click_button milestone.title - expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s) - expect(page).to have_button milestone.title - - click_button 'Labels' - page.within '.dropdown-menu-labels' do - click_link label.title - click_link label2.title - end - - find('.js-issuable-form-dropdown.js-label-select').click - page.within '.js-label-select' do - expect(page).to have_content label.title - end - expect(page.all('input[name="issue[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s) - expect(page.all('input[name="issue[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s) - - click_button 'Create issue' + expect(page.find('.gl-dropdown-button-text')).to have_content(label.title) + end - page.within '.issuable-sidebar' do - page.within '.assignee' do - expect(page).to have_content "Assignee" - end + click_button label.title, class: 'gl-dropdown-toggle' - page.within '.milestone' do - expect(page).to have_content milestone.title - end + wait_for_all_requests - page.within '.labels' do - expect(page).to have_content label.title - expect(page).to have_content label2.title - end - end + page.within '[data-testid="sidebar-labels"]' do + click_button label.title, class: 'dropdown-item' + click_button _('Close') - page.within '.breadcrumbs' do - issue = Issue.find_by(title: 'title') + wait_for_requests - expect(page).to have_text("Issues #{issue.to_reference}") - end + expect(page).not_to have_selector('[data-testid="embedded-labels-list"]') + expect(page.find('.gl-dropdown-button-text')).to have_content(_('Select label')) end + end - it 'correctly updates the dropdown toggle when removing a label' do - click_button 'Labels' - - page.within '.dropdown-menu-labels' do - click_link label.title - end + it 'clears label search input field when a label is selected', :js do + click_button _('Select label') - expect(find('.js-label-select')).to have_content(label.title) + wait_for_all_requests - page.within '.dropdown-menu-labels' do - click_link label.title - end + page.within '[data-testid="sidebar-labels"]' do + search_field = find('input[type="search"]') - expect(find('.js-label-select')).to have_content('Labels') - end + search_field.native.send_keys(label.title) - it 'clears label search input field when a label is selected' do - click_button 'Labels' + expect(page).to have_css('.gl-search-box-by-type-clear') - page.within '.dropdown-menu-labels' do - search_field = find('input[type="search"]') + click_button label.title, class: 'dropdown-item' - search_field.set(label2.title) - click_link label2.title - expect(search_field.value).to eq '' - end + expect(page).not_to have_css('.gl-search-box-by-type-clear') + expect(search_field.value).to eq '' end end @@ -559,100 +450,52 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do visit edit_project_issue_path(project, issue) end - context 'with the visible_label_selection_on_metadata feature flag enabled' do - let(:visible_label_selection_on_metadata) { true } - - it 'allows user to update issue' do - expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user.id.to_s) - expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s) - expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible - - page.within '.js-user-search' do - expect(page).to have_content user.name - end - - expect(page).to have_button milestone.title - - click_button _('Select label') - - wait_for_all_requests - - page.within '[data-testid="sidebar-labels"]' do - click_button label.title - click_button label2.title - click_button _('Close') + it 'allows user to update issue' do + expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user.id.to_s) + expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s) + expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible - wait_for_requests + page.within '.js-user-search' do + expect(page).to have_content user.name + end - page.within('[data-testid="embedded-labels-list"]') do - expect(page).to have_content(label.title) - expect(page).to have_content(label2.title) - end - end + expect(page).to have_button milestone.title - expect(page.all('input[name="issue[label_ids][]"]', visible: false) - .map(&:value)) - .to contain_exactly(label.id.to_s, label2.id.to_s) + click_button _('Select label') - click_button 'Save changes' + wait_for_all_requests - page.within '.issuable-sidebar' do - page.within '.assignee' do - expect(page).to have_content user.name - end + page.within '[data-testid="sidebar-labels"]' do + click_button label.title + click_button label2.title + click_button _('Close') - page.within '.milestone' do - expect(page).to have_content milestone.title - end + wait_for_requests - page.within '.labels' do - expect(page).to have_content label.title - expect(page).to have_content label2.title - end + page.within('[data-testid="embedded-labels-list"]') do + expect(page).to have_content(label.title) + expect(page).to have_content(label2.title) end end - end - context 'with the visible_label_selection_on_metadata feature flag disabled' do - let(:visible_label_selection_on_metadata) { false } + expect(page.all('input[name="issue[label_ids][]"]', visible: false) + .map(&:value)) + .to contain_exactly(label.id.to_s, label2.id.to_s) - it 'allows user to update issue' do - expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user.id.to_s) - expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s) - expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible + click_button 'Save changes' - page.within '.js-user-search' do + page.within '.issuable-sidebar' do + page.within '.assignee' do expect(page).to have_content user.name end - expect(page).to have_button milestone.title - - click_button 'Labels' - page.within '.dropdown-menu-labels' do - click_link label.title - click_link label2.title + page.within '.milestone' do + expect(page).to have_content milestone.title end - page.within '.js-label-select' do - expect(page).to have_content label.title - end - expect(page.all('input[name="issue[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s) - expect(page.all('input[name="issue[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s) - - click_button 'Save changes' - page.within '.issuable-sidebar' do - page.within '.assignee' do - expect(page).to have_content user.name - end - - page.within '.milestone' do - expect(page).to have_content milestone.title - end - - page.within '.labels' do - expect(page).to have_content label.title - expect(page).to have_content label2.title - end + page.within '.labels' do + expect(page).to have_content label.title + expect(page).to have_content label2.title end end end @@ -733,10 +576,8 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do visit new_project_issue_path(sub_group_project) end - context 'with the visible_label_selection_on_metadata feature flag enabled', :js do - let(:visible_label_selection_on_metadata) { true } - - it 'creates project label from dropdown', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/416585' do + context 'labels', :js do + it 'creates project label from dropdown' do find('[data-testid="labels-select-dropdown-contents"] button').click wait_for_all_requests @@ -761,29 +602,6 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do end end end - - context 'with the visible_label_selection_on_metadata feature flag disabled' do - let(:visible_label_selection_on_metadata) { false } - - it 'creates project label from dropdown' do - click_button 'Labels' - - click_link 'Create project label' - - page.within '.dropdown-new-label' do - fill_in 'new_label_name', with: 'test label' - first('.suggest-colors-dropdown a').click - - click_button 'Create' - - wait_for_requests - end - - page.within '.dropdown-menu-labels' do - expect(page).to have_link 'test label' - end - end - end end def before_for_selector(selector) diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb index 857cb1f39a2..c4aff4ca04b 100644 --- a/spec/features/issues/user_creates_issue_spec.rb +++ b/spec/features/issues/user_creates_issue_spec.rb @@ -9,8 +9,6 @@ RSpec.describe "User creates issue", feature_category: :team_planning do let_it_be(:project) { create(:project_empty_repo, :public) } let_it_be(:user) { create(:user) } - let(:visible_label_selection_on_metadata) { false } - context "when unauthenticated" do before do sign_out(:user) @@ -37,7 +35,6 @@ RSpec.describe "User creates issue", feature_category: :team_planning do context "when signed in as guest", :js do before do - stub_feature_flags(visible_label_selection_on_metadata: visible_label_selection_on_metadata) project.add_guest(user) sign_in(user) @@ -97,50 +94,28 @@ RSpec.describe "User creates issue", feature_category: :team_planning do end end - context 'with the visible_label_selection_on_metadata feature flag enabled' do - let(:visible_label_selection_on_metadata) { true } - - it "creates issue" do - issue_title = "500 error on profile" - - fill_in("Title", with: issue_title) - - click_button _('Select label') + it "creates issue" do + issue_title = "500 error on profile" - wait_for_all_requests + fill_in("Title", with: issue_title) - page.within '[data-testid="sidebar-labels"]' do - click_button label_titles.first - click_button _('Close') + click_button _('Select label') - wait_for_requests - end + wait_for_all_requests - click_button("Create issue") + page.within '[data-testid="sidebar-labels"]' do + click_button label_titles.first + click_button _('Close') - expect(page).to have_content(issue_title) - .and have_content(user.name) - .and have_content(project.name) - .and have_content(label_titles.first) + wait_for_requests end - end - - context 'with the visible_label_selection_on_metadata feature flag disabled' do - let(:visible_label_selection_on_metadata) { false } - it "creates issue" do - issue_title = "500 error on profile" + click_button("Create issue") - fill_in("Title", with: issue_title) - click_button("Label") - click_link(label_titles.first) - click_button("Create issue") - - expect(page).to have_content(issue_title) - .and have_content(user.name) - .and have_content(project.name) - .and have_content(label_titles.first) - end + expect(page).to have_content(issue_title) + .and have_content(user.name) + .and have_content(project.name) + .and have_content(label_titles.first) end end diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index 0cb712622f2..edc7b570933 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -158,73 +158,35 @@ RSpec.describe 'Labels Hierarchy', :js, feature_category: :team_planning do end end - context 'with the visible_label_selection_on_metadata feature flag enabled' do + context 'when creating new issuable' do before do - stub_feature_flags(visible_label_selection_on_metadata: true) + visit new_project_issue_path(project_1) + close_rich_text_promo_popover_if_present end - context 'when creating new issuable' do - before do - visit new_project_issue_path(project_1) - close_rich_text_promo_popover_if_present - end - - it 'is able to assign ancestor group labels' do - fill_in 'issue_title', with: 'new created issue' - fill_in 'issue_description', with: 'new issue description' - - click_button _('Select label') - - wait_for_all_requests + it 'is able to assign ancestor group labels' do + fill_in 'issue_title', with: 'new created issue' + fill_in 'issue_description', with: 'new issue description' - page.within '[data-testid="sidebar-labels"]' do - click_button grandparent_group_label.title - click_button parent_group_label.title - click_button project_label_1.title - click_button _('Close') + click_button _('Select label') - wait_for_requests - end - - find('.btn-confirm').click + wait_for_all_requests - expect(page.find('.issue-details h1.title')).to have_content('new created issue') - expect(page).to have_selector('span.gl-label-text', text: grandparent_group_label.title) - expect(page).to have_selector('span.gl-label-text', text: parent_group_label.title) - expect(page).to have_selector('span.gl-label-text', text: project_label_1.title) - end - end - end + page.within '[data-testid="sidebar-labels"]' do + click_button grandparent_group_label.title + click_button parent_group_label.title + click_button project_label_1.title + click_button _('Close') - context 'with the visible_label_selection_on_metadata feature flag disabled' do - before do - stub_feature_flags(visible_label_selection_on_metadata: false) - end - - context 'when creating new issuable' do - before do - visit new_project_issue_path(project_1) - close_rich_text_promo_popover_if_present - end - - it 'is able to assign ancestor group labels' do - fill_in 'issue_title', with: 'new created issue' - fill_in 'issue_description', with: 'new issue description' - - find(".js-label-select").click wait_for_requests + end - find('a.label-item', text: grandparent_group_label.title).click - find('a.label-item', text: parent_group_label.title).click - find('a.label-item', text: project_label_1.title).click - - find('.btn-confirm').click + find('.btn-confirm').click - expect(page.find('.issue-details h1.title')).to have_content('new created issue') - expect(page).to have_selector('span.gl-label-text', text: grandparent_group_label.title) - expect(page).to have_selector('span.gl-label-text', text: parent_group_label.title) - expect(page).to have_selector('span.gl-label-text', text: project_label_1.title) - end + expect(page.find('.issue-details h1.title')).to have_content('new created issue') + expect(page).to have_selector('span.gl-label-text', text: grandparent_group_label.title) + expect(page).to have_selector('span.gl-label-text', text: parent_group_label.title) + expect(page).to have_selector('span.gl-label-text', text: project_label_1.title) end end diff --git a/spec/features/merge_request/user_creates_mr_spec.rb b/spec/features/merge_request/user_creates_mr_spec.rb index f48315a1636..950b64bb395 100644 --- a/spec/features/merge_request/user_creates_mr_spec.rb +++ b/spec/features/merge_request/user_creates_mr_spec.rb @@ -89,77 +89,34 @@ RSpec.describe 'Merge request > User creates MR', feature_category: :code_review end end - context 'with the visible_label_selection_on_metadata feature flag enabled' do - before do - stub_feature_flags(visible_label_selection_on_metadata: true) - end - - context 'non-fork merge request' do - include_context 'merge request create context' - it_behaves_like 'a creatable merge request with visible selected labels' - end - - context 'from a forked project' do - let(:canonical_project) { create(:project, :public, :repository) } - - let(:source_project) do - fork_project(canonical_project, user, - repository: true, - namespace: user.namespace) - end - - context 'to canonical project' do - include_context 'merge request create context' - it_behaves_like 'a creatable merge request with visible selected labels' - end - - context 'to another forked project' do - let(:target_project) do - fork_project(canonical_project, user, - repository: true, - namespace: user.namespace) - end - - include_context 'merge request create context' - it_behaves_like 'a creatable merge request with visible selected labels' - end - end + context 'non-fork merge request' do + include_context 'merge request create context' + it_behaves_like 'a creatable merge request with visible selected labels' end - context 'with the visible_label_selection_on_metadata feature flag disabled' do - before do - stub_feature_flags(visible_label_selection_on_metadata: false) + context 'from a forked project' do + let(:canonical_project) { create(:project, :public, :repository) } + + let(:source_project) do + fork_project(canonical_project, user, + repository: true, + namespace: user.namespace) end - context 'non-fork merge request' do + context 'to canonical project' do include_context 'merge request create context' - it_behaves_like 'a creatable merge request' + it_behaves_like 'a creatable merge request with visible selected labels' end - context 'from a forked project' do - let(:canonical_project) { create(:project, :public, :repository) } - - let(:source_project) do + context 'to another forked project' do + let(:target_project) do fork_project(canonical_project, user, repository: true, namespace: user.namespace) end - context 'to canonical project' do - include_context 'merge request create context' - it_behaves_like 'a creatable merge request' - end - - context 'to another forked project' do - let(:target_project) do - fork_project(canonical_project, user, - repository: true, - namespace: user.namespace) - end - - include_context 'merge request create context' - it_behaves_like 'a creatable merge request' - end + include_context 'merge request create context' + it_behaves_like 'a creatable merge request with visible selected labels' end end diff --git a/spec/features/merge_request/user_edits_mr_spec.rb b/spec/features/merge_request/user_edits_mr_spec.rb index bf237e07ac8..fca3a76949a 100644 --- a/spec/features/merge_request/user_edits_mr_spec.rb +++ b/spec/features/merge_request/user_edits_mr_spec.rb @@ -198,39 +198,15 @@ RSpec.describe 'Merge request > User edits MR', feature_category: :code_review_w stub_licensed_features(multiple_merge_request_assignees: false) end - context 'with the visible_label_selection_on_metadata feature flag enabled' do - before do - stub_feature_flags(visible_label_selection_on_metadata: true) - end - - context 'non-fork merge request' do - include_context 'merge request edit context' - it_behaves_like 'an editable merge request with visible selected labels' - end - - context 'for a forked project' do - let(:source_project) { fork_project(target_project, nil, repository: true) } - - include_context 'merge request edit context' - it_behaves_like 'an editable merge request with visible selected labels' - end + context 'non-fork merge request' do + include_context 'merge request edit context' + it_behaves_like 'an editable merge request with visible selected labels' end - context 'with the visible_label_selection_on_metadata feature flag disabled' do - before do - stub_feature_flags(visible_label_selection_on_metadata: false) - end - - context 'non-fork merge request' do - include_context 'merge request edit context' - it_behaves_like 'an editable merge request' - end + context 'for a forked project' do + let(:source_project) { fork_project(target_project, nil, repository: true) } - context 'for a forked project' do - let(:source_project) { fork_project(target_project, nil, repository: true) } - - include_context 'merge request edit context' - it_behaves_like 'an editable merge request' - end + include_context 'merge request edit context' + it_behaves_like 'an editable merge request with visible selected labels' end end diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb index 0a77c671fce..c285d039d08 100644 --- a/spec/features/projects/container_registry_spec.rb +++ b/spec/features/projects/container_registry_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Container Registry', :js, feature_category: :groups_and_projects do +RSpec.describe 'Container Registry', :js, feature_category: :container_registry do include_context 'container registry tags' let(:user) { create(:user) } @@ -75,7 +75,6 @@ RSpec.describe 'Container Registry', :js, feature_category: :groups_and_projects visit_container_registry expect_any_instance_of(ContainerRepository).to receive(:delete_scheduled!).and_call_original - expect(DeleteContainerRepositoryWorker).not_to receive(:perform_async) find('[title="Remove repository"]').click expect(find('.modal .modal-title')).to have_content _('Delete image repository?') diff --git a/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js index 7d65dddc3f9..be50858bc88 100644 --- a/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js +++ b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js @@ -29,6 +29,7 @@ describe('BulkImportsHistoryApp', () => { source_full_path: 'top-level-group-12', destination_full_path: 'h5bp/top-level-group-12', destination_name: 'top-level-group-12', + destination_slug: 'top-level-group-12', destination_namespace: 'h5bp', created_at: '2021-07-08T10:03:44.743Z', failures: [], @@ -40,6 +41,7 @@ describe('BulkImportsHistoryApp', () => { entity_type: 'project', source_full_path: 'autodevops-demo', destination_name: 'autodevops-demo', + destination_slug: 'autodevops-demo', destination_full_path: 'some-group/autodevops-demo', destination_namespace: 'flightjs', parent_id: null, @@ -173,7 +175,7 @@ describe('BulkImportsHistoryApp', () => { expect(findLocalStorageSync().props('value')).toBe(NEW_PAGE_SIZE); }); - it('renders correct url for destination group when relative_url is empty', async () => { + it('renders link to destination_full_path for destination group', async () => { createComponent({ shallow: false }); await axios.waitForAll(); @@ -182,14 +184,17 @@ describe('BulkImportsHistoryApp', () => { ); }); - it('renders loading icon when destination namespace is not defined', async () => { + it('renders destination as text when destination_full_path is not defined', async () => { const RESPONSE = [{ ...DUMMY_RESPONSE[0], destination_full_path: null }]; mock.onGet(API_URL).reply(HTTP_STATUS_OK, RESPONSE, DEFAULT_HEADERS); createComponent({ shallow: false }); await axios.waitForAll(); - expect(wrapper.find('tbody tr').findComponent(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.find('tbody tr a').exists()).toBe(false); + expect(wrapper.find('tbody tr span').text()).toBe( + `${DUMMY_RESPONSE[0].destination_namespace}/${DUMMY_RESPONSE[0].destination_slug}/`, + ); }); it('adds slash to group urls', async () => { diff --git a/spec/graphql/mutations/container_repositories/destroy_spec.rb b/spec/graphql/mutations/container_repositories/destroy_spec.rb index 85e0ac96e55..b49751985ec 100644 --- a/spec/graphql/mutations/container_repositories/destroy_spec.rb +++ b/spec/graphql/mutations/container_repositories/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::ContainerRepositories::Destroy do +RSpec.describe Mutations::ContainerRepositories::Destroy, feature_category: :container_registry do using RSpec::Parameterized::TableSyntax let_it_be_with_reload(:container_repository) { create(:container_repository) } @@ -23,7 +23,6 @@ RSpec.describe Mutations::ContainerRepositories::Destroy do it 'marks the repository as delete_scheduled' do expect(::Packages::CreateEventService) .to receive(:new).with(nil, user, event_name: :delete_repository, scope: :container).and_call_original - expect(DeleteContainerRepositoryWorker).not_to receive(:perform_async) subject expect(container_repository.reload.delete_scheduled?).to be true @@ -32,9 +31,6 @@ RSpec.describe Mutations::ContainerRepositories::Destroy do shared_examples 'denying access to container respository' do it 'raises an error' do - expect(DeleteContainerRepositoryWorker) - .not_to receive(:perform_async).with(user.id, container_repository.id) - expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) end end diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index 5db2fbd923e..a10ff60a249 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ExtractsPath do +RSpec.describe ExtractsPath, feature_category: :source_code_management do include described_class include RepoHelpers include Gitlab::Routing @@ -215,7 +215,7 @@ RSpec.describe ExtractsPath do end it 'raises an error if there are no matching refs' do - expect { extract_ref_without_atom('foo.atom') }.to raise_error(ExtractsRef::InvalidPathError) + expect { extract_ref_without_atom('foo.atom') }.to raise_error(ExtractsPath::InvalidPathError) end end end diff --git a/spec/lib/extracts_ref/ref_extractor_spec.rb b/spec/lib/extracts_ref/ref_extractor_spec.rb new file mode 100644 index 00000000000..23b283967ca --- /dev/null +++ b/spec/lib/extracts_ref/ref_extractor_spec.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ExtractsRef::RefExtractor, feature_category: :source_code_management do + include RepoHelpers + + let_it_be(:owner) { create(:user) } + let_it_be(:container) { create(:snippet, :repository, author: owner) } + + let(:ref) { sample_commit[:id] } + let(:path) { sample_commit[:line_code_path] } + let(:params) { { path: path, ref: ref } } + + let(:ref_extractor) { described_class.new(container, params) } + + before do + ref_names = ['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0', 'release/app', 'release/app/v1.0.0'] + + allow(container.repository).to receive(:ref_names).and_return(ref_names) + end + + describe '#extract_vars!' do + it_behaves_like 'extracts ref vars' + + context 'when ref contains trailing space' do + let(:ref) { 'master ' } + + it 'strips surrounding space' do + ref_extractor.extract! + + expect(ref_extractor.ref).to eq('master') + end + end + + context 'when ref and path are nil' do + let(:ref) { nil } + let(:path) { nil } + + it 'does not set commit' do + expect(container.repository).not_to receive(:commit).with('') + + ref_extractor.extract! + + expect(ref_extractor.commit).to be_nil + end + end + + context 'when a ref_type parameter is provided' do + let(:params) { { path: path, ref: ref, ref_type: 'tags' } } + + it 'sets a fully_qualified_ref variable' do + fully_qualified_ref = "refs/tags/#{ref}" + + expect(container.repository).to receive(:commit).with(fully_qualified_ref) + + ref_extractor.extract! + + expect(ref_extractor.fully_qualified_ref).to eq(fully_qualified_ref) + end + end + end + + describe '#ref_type' do + let(:params) { { ref_type: 'heads' } } + + it 'delegates to .ref_type' do + expect(described_class).to receive(:ref_type).with('heads') + + ref_extractor.ref_type + end + end + + describe '.ref_type' do + subject { described_class.ref_type(ref_type) } + + context 'when ref_type is nil' do + let(:ref_type) { nil } + + it { is_expected.to eq(nil) } + end + + context 'when ref_type is heads' do + let(:ref_type) { 'heads' } + + it { is_expected.to eq('heads') } + end + + context 'when ref_type is tags' do + let(:ref_type) { 'tags' } + + it { is_expected.to eq('tags') } + end + + context 'when ref_type is invalid' do + let(:ref_type) { 'invalid' } + + it { is_expected.to eq(nil) } + end + end + + describe '.qualify_ref' do + subject { described_class.qualify_ref(ref, ref_type) } + + context 'when ref_type is nil' do + let(:ref_type) { nil } + + it { is_expected.to eq(ref) } + end + + context 'when ref_type valid' do + let(:ref_type) { 'heads' } + + it { is_expected.to eq("refs/#{ref_type}/#{ref}") } + end + + context 'when ref_type is invalid' do + let(:ref_type) { 'invalid' } + + it { is_expected.to eq(ref) } + end + end + + it_behaves_like 'extracts ref method' +end diff --git a/spec/lib/extracts_ref_spec.rb b/spec/lib/extracts_ref_spec.rb index bf33c8b95f1..9ff11899e89 100644 --- a/spec/lib/extracts_ref_spec.rb +++ b/spec/lib/extracts_ref_spec.rb @@ -100,27 +100,5 @@ RSpec.describe ExtractsRef do end end - describe '.qualify_ref' do - subject { described_class.qualify_ref(ref, ref_type) } - - context 'when ref_type is nil' do - let(:ref_type) { nil } - - it { is_expected.to eq(ref) } - end - - context 'when ref_type valid' do - let(:ref_type) { 'heads' } - - it { is_expected.to eq("refs/#{ref_type}/#{ref}") } - end - - context 'when ref_type is invalid' do - let(:ref_type) { 'invalid' } - - it { is_expected.to eq(ref) } - end - end - it_behaves_like 'extracts refs' end diff --git a/spec/lib/gitlab/database/migrations/milestone_mixin_spec.rb b/spec/lib/gitlab/database/migrations/milestone_mixin_spec.rb new file mode 100644 index 00000000000..e375af494a2 --- /dev/null +++ b/spec/lib/gitlab/database/migrations/milestone_mixin_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Database::Migrations::MilestoneMixin, feature_category: :database do + let(:migration_no_mixin) do + Class.new(Gitlab::Database::Migration[2.1]) do + def change + # no-op here to make rubocop happy + end + end + end + + let(:migration_mixin) do + Class.new(Gitlab::Database::Migration[2.1]) do + include Gitlab::Database::Migrations::MilestoneMixin + end + end + + let(:migration_mixin_version) do + Class.new(Gitlab::Database::Migration[2.1]) do + include Gitlab::Database::Migrations::MilestoneMixin + milestone '16.4' + end + end + + context 'when the mixin is not included' do + it 'does not raise an error' do + expect { migration_no_mixin.new(4, 4) }.not_to raise_error + end + end + + context 'when the mixin is included' do + context 'when a milestone is not specified' do + it "raises MilestoneNotSetError" do + expect { migration_mixin.new(4, 4, :regular) }.to raise_error( + "#{described_class}::MilestoneNotSetError".constantize + ) + end + end + + context 'when a milestone is specified' do + it "does not raise an error" do + expect { migration_mixin_version.new(4, 4, :regular) }.not_to raise_error + end + end + end +end diff --git a/spec/lib/gitlab/database/migrations/version_spec.rb b/spec/lib/gitlab/database/migrations/version_spec.rb new file mode 100644 index 00000000000..821a2156539 --- /dev/null +++ b/spec/lib/gitlab/database/migrations/version_spec.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Database::Migrations::Version, feature_category: :database do + let(:test_versions) do + [ + 4, + 5, + described_class.new(6, Gitlab::VersionInfo.parse_from_milestone('10.3'), :regular), + 7, + described_class.new(8, Gitlab::VersionInfo.parse_from_milestone('10.3'), :regular), + described_class.new(9, Gitlab::VersionInfo.parse_from_milestone('10.4'), :regular), + described_class.new(10, Gitlab::VersionInfo.parse_from_milestone('10.3'), :post), + described_class.new(11, Gitlab::VersionInfo.parse_from_milestone('10.3'), :regular) + ] + end + + describe "#<=>" do + it 'sorts by existence of milestone, then by milestone, then by type, then by timestamp when sorted by version' do + expect(test_versions.sort.map(&:to_i)).to eq [4, 5, 7, 6, 8, 11, 10, 9] + end + end + + describe 'initialize' do + context 'when the type is :post or :regular' do + it 'does not raise an error' do + expect { described_class.new(4, 4, :regular) }.not_to raise_error + expect { described_class.new(4, 4, :post) }.not_to raise_error + end + end + + context 'when the type is anything else' do + it 'does not raise an error' do + expect { described_class.new(4, 4, 'foo') }.to raise_error("#{described_class}::InvalidTypeError".constantize) + end + end + end + + describe 'eql?' do + where(:version1, :version2, :expected_equality) do + [ + [ + described_class.new(4, Gitlab::VersionInfo.parse_from_milestone('10.3'), :regular), + described_class.new(4, Gitlab::VersionInfo.parse_from_milestone('10.3'), :regular), + true + ], + [ + described_class.new(4, Gitlab::VersionInfo.parse_from_milestone('10.3'), :regular), + described_class.new(4, Gitlab::VersionInfo.parse_from_milestone('10.4'), :regular), + false + ], + [ + described_class.new(4, Gitlab::VersionInfo.parse_from_milestone('10.3'), :regular), + described_class.new(4, Gitlab::VersionInfo.parse_from_milestone('10.3'), :post), + false + ], + [ + described_class.new(4, Gitlab::VersionInfo.parse_from_milestone('10.3'), :regular), + described_class.new(5, Gitlab::VersionInfo.parse_from_milestone('10.3'), :regular), + false + ] + ] + end + + with_them do + it 'correctly evaluates deep equality' do + expect(version1.eql?(version2)).to eq(expected_equality) + end + + it 'correctly evaluates deep equality using ==' do + expect(version1 == version2).to eq(expected_equality) + end + end + end + + describe 'type' do + subject { described_class.new(4, Gitlab::VersionInfo.parse_from_milestone('10.3'), migration_type) } + + context 'when the migration is regular' do + let(:migration_type) { :regular } + + it 'correctly identifies the migration type' do + expect(subject.type).to eq(:regular) + expect(subject.regular?).to eq(true) + expect(subject.post_deployment?).to eq(false) + end + end + + context 'when the migration is post_deployment' do + let(:migration_type) { :post } + + it 'correctly identifies the migration type' do + expect(subject.type).to eq(:post) + expect(subject.regular?).to eq(false) + expect(subject.post_deployment?).to eq(true) + end + end + end + + describe 'to_s' do + subject { described_class.new(4, Gitlab::VersionInfo.parse_from_milestone('10.3'), :regular) } + + it 'returns the given timestamp value as a string' do + expect(subject.to_s).to eql('4') + end + end + + describe 'hash' do + subject { described_class.new(4, Gitlab::VersionInfo.parse_from_milestone('10.3'), :regular) } + + let(:expected_hash) { subject.hash } + + it 'deterministically returns a hash of the timestamp, milestone, and type value' do + 3.times do + expect(subject.hash).to eq(expected_hash) + end + end + end +end diff --git a/spec/requests/api/composer_packages_spec.rb b/spec/requests/api/composer_packages_spec.rb index 3652bee5e44..6d1d6a7c4e0 100644 --- a/spec/requests/api/composer_packages_spec.rb +++ b/spec/requests/api/composer_packages_spec.rb @@ -13,6 +13,7 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token_for_project, project: project) } let_it_be(:deploy_token_for_group) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) } let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token_for_group, group: group) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } let(:snowplow_gitlab_standard_context) do { project: project, namespace: project.namespace, user: user, property: 'i_package_composer_user' } @@ -28,7 +29,7 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do subject { get api(url), headers: headers } context 'with valid project' do - let!(:package) { create(:composer_package, :with_metadatum, project: project) } + let_it_be(:package) { create(:composer_package, :with_metadatum, project: project) } context 'with a public group' do before do @@ -36,59 +37,62 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do end context 'with basic auth' do - where(:project_visibility_level, :user_role, :member, :user_token, :include_package) do - 'PUBLIC' | :developer | true | true | :include_package - 'PUBLIC' | :developer | false | true | :include_package - 'PUBLIC' | :guest | true | true | :include_package - 'PUBLIC' | :guest | false | true | :include_package - 'PUBLIC' | :anonymous | false | true | :include_package - 'PRIVATE' | :developer | true | true | :include_package - 'PRIVATE' | :developer | false | true | :does_not_include_package - 'PRIVATE' | :guest | true | true | :does_not_include_package - 'PRIVATE' | :guest | false | true | :does_not_include_package - 'PRIVATE' | :anonymous | false | true | :does_not_include_package - 'PRIVATE' | :guest | false | false | :does_not_include_package - 'PRIVATE' | :guest | true | false | :does_not_include_package - 'PRIVATE' | :developer | false | false | :does_not_include_package - 'PRIVATE' | :developer | true | false | :does_not_include_package - 'PUBLIC' | :developer | true | false | :include_package - 'PUBLIC' | :guest | true | false | :include_package - 'PUBLIC' | :developer | false | false | :include_package - 'PUBLIC' | :guest | false | false | :include_package + where(:project_visibility_level, :member_role, :token_type, :valid_token, :package_returned) do + 'PUBLIC' | :developer | :user | true | true + 'PUBLIC' | :developer | :user | false | true # Anonymous User - fallback + 'PUBLIC' | :developer | :job | true | true + 'PUBLIC' | :guest | :user | true | true + 'PUBLIC' | :guest | :user | false | true # Anonymous User - fallback + 'PUBLIC' | :guest | :job | true | true + 'PUBLIC' | nil | :user | true | true + 'PUBLIC' | nil | :user | false | true # Anonymous User - fallback + 'PUBLIC' | nil | :job | true | true + 'PUBLIC' | nil | nil | nil | true # Anonymous User + 'PRIVATE' | :developer | :user | true | true + 'PRIVATE' | :developer | :user | false | false # Anonymous User - fallback + 'PRIVATE' | :developer | :job | true | true + 'PRIVATE' | :guest | :user | true | false + 'PRIVATE' | :guest | :user | false | false # Anonymous User - fallback + 'PRIVATE' | :guest | :job | true | false + 'PRIVATE' | nil | :user | true | false + 'PRIVATE' | nil | :user | false | false # Anonymous User - fallback + 'PRIVATE' | nil | :job | true | false + 'PRIVATE' | nil | nil | nil | false # Anonymous User end with_them do - include_context 'Composer api project access', params[:project_visibility_level], params[:user_role], params[:user_token], :basic do - it_behaves_like 'Composer package index', params[:user_role], :success, params[:member], params[:include_package] + include_context 'Composer api project access', auth_method: :basic, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like 'Composer package index', member_role: params[:member_role], expected_status: :success, package_returned: params[:package_returned] end end end - context 'with private token header auth' do - where(:project_visibility_level, :user_role, :member, :user_token, :expected_status, :include_package) do - 'PUBLIC' | :developer | true | true | :success | :include_package - 'PUBLIC' | :developer | false | true | :success | :include_package - 'PUBLIC' | :guest | true | true | :success | :include_package - 'PUBLIC' | :guest | false | true | :success | :include_package - 'PUBLIC' | :anonymous | false | true | :success | :include_package - 'PRIVATE' | :developer | true | true | :success | :include_package - 'PRIVATE' | :developer | false | true | :success | :does_not_include_package - 'PRIVATE' | :guest | true | true | :success | :does_not_include_package - 'PRIVATE' | :guest | false | true | :success | :does_not_include_package - 'PRIVATE' | :anonymous | false | true | :success | :does_not_include_package - 'PRIVATE' | :guest | false | false | :unauthorized | nil - 'PRIVATE' | :guest | true | false | :unauthorized | nil - 'PRIVATE' | :developer | false | false | :unauthorized | nil - 'PRIVATE' | :developer | true | false | :unauthorized | nil - 'PUBLIC' | :developer | true | false | :unauthorized | nil - 'PUBLIC' | :guest | true | false | :unauthorized | nil - 'PUBLIC' | :developer | false | false | :unauthorized | nil - 'PUBLIC' | :guest | false | false | :unauthorized | nil + context 'with token auth' do + where(:project_visibility_level, :member_role, :token_type, :valid_token, :expected_status, :package_returned) do + :PUBLIC | :developer | :user | true | :success | true + :PUBLIC | :developer | :user | false | :unauthorized | false + :PUBLIC | :developer | :job | true | :success | true # Anonymous User - fallback + :PUBLIC | :guest | :user | true | :success | true + :PUBLIC | :guest | :user | false | :unauthorized | false + :PUBLIC | :guest | :job | true | :success | true # Anonymous User - fallback + :PUBLIC | nil | :user | true | :success | true + :PUBLIC | nil | :user | false | :unauthorized | false + :PUBLIC | nil | :job | true | :success | true # Anonymous User - fallback + :PUBLIC | nil | nil | nil | :success | true # Anonymous User + :PRIVATE | :developer | :user | true | :success | true + :PRIVATE | :developer | :user | false | :unauthorized | false + :PRIVATE | :developer | :job | true | :success | false # Anonymous User - fallback + :PRIVATE | :guest | :user | true | :success | false + :PRIVATE | :guest | :user | false | :unauthorized | false + :PRIVATE | :guest | :job | true | :success | false # Anonymous User - fallback + :PRIVATE | nil | :user | true | :success | false + :PRIVATE | nil | :user | false | :unauthorized | false + :PRIVATE | nil | nil | nil | :success | false # Anonymous User end with_them do - include_context 'Composer api project access', params[:project_visibility_level], params[:user_role], params[:user_token], :token do - it_behaves_like 'Composer package index', params[:user_role], params[:expected_status], params[:member], params[:include_package] + include_context 'Composer api project access', auth_method: :token, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like 'Composer package index', member_role: params[:member_role], expected_status: params[:expected_status], package_returned: params[:package_returned] end end end @@ -101,33 +105,44 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do it_behaves_like 'Composer access with deploy tokens' - context 'with access to the api' do - where(:project_visibility_level, :user_role, :member, :user_token, :include_package) do - 'PRIVATE' | :developer | true | true | :include_package - 'PRIVATE' | :guest | true | true | :does_not_include_package + context 'with basic auth' do + where(:member_role, :token_type, :valid_token, :shared_examples_name, :expected_status, :package_returned) do + :developer | :user | true | 'Composer package index' | :success | true + :developer | :user | false | 'process Composer api request' | :unauthorized | false + :developer | :job | true | 'Composer package index' | :success | true + :guest | :user | true | 'Composer package index' | :success | false + :guest | :user | false | 'process Composer api request' | :unauthorized | false + :guest | :job | true | 'Composer package index' | :success | false + nil | :user | true | 'Composer package index' | :not_found | false + nil | :user | false | 'process Composer api request' | :unauthorized | false + nil | :job | true | 'Composer package index' | :not_found | false + nil | nil | nil | 'process Composer api request' | :unauthorized | false # Anonymous User end with_them do - include_context 'Composer api project access', params[:project_visibility_level], params[:user_role], params[:user_token] do - it_behaves_like 'Composer package index', params[:user_role], :success, params[:member], params[:include_package] + include_context 'Composer api project access', auth_method: :basic, project_visibility_level: :PRIVATE, token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like params[:shared_examples_name], member_role: params[:member_role], expected_status: params[:expected_status], package_returned: params[:package_returned] end end end - context 'without access to the api' do - where(:project_visibility_level, :user_role, :member, :user_token) do - 'PRIVATE' | :developer | true | false - 'PRIVATE' | :developer | false | true - 'PRIVATE' | :developer | false | false - 'PRIVATE' | :guest | true | false - 'PRIVATE' | :guest | false | true - 'PRIVATE' | :guest | false | false - 'PRIVATE' | :anonymous | false | true + context 'with token auth' do + where(:member_role, :token_type, :valid_token, :shared_examples_name, :expected_status, :package_returned) do + :developer | :user | true | 'Composer package index' | :success | true + :developer | :user | false | 'process Composer api request' | :unauthorized | false + :developer | :job | true | 'process Composer api request' | :unauthorized | false + :guest | :user | true | 'Composer package index' | :success | false + :guest | :user | false | 'process Composer api request' | :unauthorized | false + :guest | :job | true | 'process Composer api request' | :unauthorized | false + nil | :user | true | 'Composer package index' | :not_found | false + nil | :user | false | 'Composer package index' | :unauthorized | false + nil | :job | true | 'process Composer api request' | :unauthorized | false + nil | nil | nil | 'process Composer api request' | :unauthorized | false # Anonymous User end with_them do - include_context 'Composer api project access', params[:project_visibility_level], params[:user_role], params[:user_token] do - it_behaves_like 'process Composer api request', params[:user_role], :not_found, params[:member] + include_context 'Composer api project access', auth_method: :token, project_visibility_level: :PRIVATE, token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like params[:shared_examples_name], member_role: params[:member_role], expected_status: params[:expected_status], package_returned: params[:package_returned] end end end @@ -145,30 +160,65 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do subject { get api(url), headers: headers } context 'with valid project' do - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do - 'PUBLIC' | :developer | true | true | 'Composer provider index' | :success - 'PUBLIC' | :developer | true | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :developer | false | true | 'Composer provider index' | :success - 'PUBLIC' | :developer | false | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :guest | true | true | 'Composer provider index' | :success - 'PUBLIC' | :guest | true | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :guest | false | true | 'Composer provider index' | :success - 'PUBLIC' | :guest | false | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :anonymous | false | true | 'Composer provider index' | :success - 'PRIVATE' | :developer | true | true | 'Composer provider index' | :success - 'PRIVATE' | :developer | true | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :developer | false | true | 'process Composer api request' | :not_found - 'PRIVATE' | :developer | false | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :guest | true | true | 'Composer empty provider index' | :success - 'PRIVATE' | :guest | true | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :guest | false | true | 'process Composer api request' | :not_found - 'PRIVATE' | :guest | false | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :anonymous | false | true | 'process Composer api request' | :not_found + context 'with basic auth' do + where(:project_visibility_level, :member_role, :token_type, :valid_token, :shared_examples_name, :expected_status) do + 'PUBLIC' | :developer | :user | true | 'Composer provider index' | :success + 'PUBLIC' | :developer | :user | false | 'Composer provider index' | :success # Anonymous User - fallback + 'PUBLIC' | :developer | :job | true | 'Composer provider index' | :success + 'PUBLIC' | :guest | :user | true | 'Composer provider index' | :success + 'PUBLIC' | :guest | :user | false | 'Composer provider index' | :success # Anonymous User - fallback + 'PUBLIC' | :guest | :job | true | 'Composer provider index' | :success + 'PUBLIC' | nil | :user | true | 'Composer provider index' | :success + 'PUBLIC' | nil | :user | false | 'Composer provider index' | :success # Anonymous User - fallback + 'PUBLIC' | nil | :job | true | 'Composer provider index' | :success + 'PUBLIC' | nil | nil | nil | 'Composer provider index' | :success # Anonymous User + 'PRIVATE' | :developer | :user | true | 'Composer provider index' | :success + 'PRIVATE' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :developer | :job | true | 'Composer provider index' | :success + 'PRIVATE' | :guest | :user | true | 'Composer empty provider index' | :success + 'PRIVATE' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :job | true | 'Composer empty provider index' | :success + 'PRIVATE' | nil | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :job | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | nil | nil | 'process Composer api request' | :unauthorized # Anonymous User + end + + with_them do + include_context 'Composer api group access', auth_method: :basic, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like params[:shared_examples_name], member_role: params[:member_role], expected_status: params[:expected_status] + end + end end - with_them do - include_context 'Composer api group access', params[:project_visibility_level], params[:user_role], params[:user_token] do - it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] + context 'with token auth' do + where(:project_visibility_level, :member_role, :token_type, :valid_token, :shared_examples_name, :expected_status) do + 'PUBLIC' | :developer | :user | true | 'Composer provider index' | :success + 'PUBLIC' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | :developer | :job | true | 'Composer provider index' | :success # Anonymous User - fallback + 'PUBLIC' | :guest | :user | true | 'Composer provider index' | :success + 'PUBLIC' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | :guest | :job | true | 'Composer provider index' | :success # Anonymous User - fallback + 'PUBLIC' | nil | :user | true | 'Composer provider index' | :success + 'PUBLIC' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | nil | :job | true | 'Composer provider index' | :success # Anonymous User - fallback + 'PUBLIC' | nil | nil | nil | 'Composer provider index' | :success # Anonymous User + 'PRIVATE' | :developer | :user | true | 'Composer provider index' | :success + 'PRIVATE' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :developer | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :user | true | 'Composer empty provider index' | :success + 'PRIVATE' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | nil | nil | 'process Composer api request' | :unauthorized # Anonymous User + end + + with_them do + include_context 'Composer api group access', auth_method: :token, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like params[:shared_examples_name], member_role: params[:member_role], expected_status: params[:expected_status] + end end end @@ -186,7 +236,7 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do subject { get api(url), headers: headers } context 'with no packages' do - include_context 'Composer user type', :developer, true do + include_context 'Composer user type', member_role: :developer do it_behaves_like 'returning response status', :not_found end end @@ -194,40 +244,73 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do context 'with valid project' do let!(:package) { create(:composer_package, :with_metadatum, name: package_name, project: project) } - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do - 'PUBLIC' | :developer | true | true | 'Composer package api request' | :success - 'PUBLIC' | :developer | true | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :developer | false | true | 'Composer package api request' | :success - 'PUBLIC' | :developer | false | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :guest | true | true | 'Composer package api request' | :success - 'PUBLIC' | :guest | true | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :guest | false | true | 'Composer package api request' | :success - 'PUBLIC' | :guest | false | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :anonymous | false | true | 'Composer package api request' | :success - 'PRIVATE' | :developer | true | true | 'Composer package api request' | :success - 'PRIVATE' | :developer | true | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :developer | false | true | 'process Composer api request' | :not_found - 'PRIVATE' | :developer | false | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :guest | true | true | 'process Composer api request' | :not_found - 'PRIVATE' | :guest | true | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :guest | false | true | 'process Composer api request' | :not_found - 'PRIVATE' | :guest | false | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :anonymous | false | true | 'process Composer api request' | :not_found + context 'with basic auth' do + where(:project_visibility_level, :member_role, :token_type, :valid_token, :shared_examples_name, :expected_status) do + 'PUBLIC' | :developer | :user | true | 'Composer package api request' | :success + 'PUBLIC' | :developer | :user | false | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | :developer | :job | true | 'Composer package api request' | :success + 'PUBLIC' | :guest | :user | true | 'Composer package api request' | :success + 'PUBLIC' | :guest | :user | false | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | :guest | :job | true | 'Composer package api request' | :success + 'PUBLIC' | nil | :user | true | 'Composer package api request' | :success + 'PUBLIC' | nil | :user | false | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | nil | :job | true | 'Composer package api request' | :success + 'PUBLIC' | nil | nil | nil | 'Composer package api request' | :success # Anonymous User + 'PRIVATE' | :developer | :user | true | 'Composer package api request' | :success + 'PRIVATE' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :developer | :job | true | 'Composer package api request' | :success + 'PRIVATE' | :guest | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :job | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :job | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | nil | nil | 'process Composer api request' | :unauthorized # Anonymous User + end + + with_them do + include_context 'Composer api group access', auth_method: :basic, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like params[:shared_examples_name], member_role: params[:member_role], expected_status: params[:expected_status] + end + end end - with_them do - include_context 'Composer api group access', params[:project_visibility_level], params[:user_role], params[:user_token] do - it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] + context 'with token auth' do + where(:project_visibility_level, :member_role, :token_type, :valid_token, :shared_examples_name, :expected_status) do + 'PUBLIC' | :developer | :user | true | 'Composer package api request' | :success + 'PUBLIC' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | :developer | :job | true | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | :guest | :user | true | 'Composer package api request' | :success + 'PUBLIC' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | :guest | :job | true | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | nil | :user | true | 'Composer package api request' | :success + 'PUBLIC' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | nil | :job | true | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | nil | nil | nil | 'Composer package api request' | :success # Anonymous User + 'PRIVATE' | :developer | :user | true | 'Composer package api request' | :success + 'PRIVATE' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :developer | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | nil | nil | 'process Composer api request' | :unauthorized # Anonymous User + end + + with_them do + include_context 'Composer api group access', auth_method: :token, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like params[:shared_examples_name], member_role: params[:member_role], expected_status: params[:expected_status] + end end end context 'without a sha' do let(:sha) { '' } - include_context 'Composer api group access', 'PRIVATE', :developer, true do - include_context 'Composer user type', :developer, true do - it_behaves_like 'process Composer api request', :developer, :not_found, true - end + include_context 'Composer api group access', project_visibility_level: 'PRIVATE', token_type: :user, auth_method: :token do + it_behaves_like 'process Composer api request', member_role: :developer, expected_status: :not_found end end @@ -244,7 +327,7 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do subject { get api(url), headers: headers } context 'with no packages' do - include_context 'Composer user type', :developer, true do + include_context 'Composer user type', member_role: :developer do it_behaves_like 'returning response status', :not_found end end @@ -252,30 +335,65 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do context 'with valid project' do let!(:package) { create(:composer_package, :with_metadatum, name: package_name, project: project) } - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do - 'PUBLIC' | :developer | true | true | 'Composer package api request' | :success - 'PUBLIC' | :developer | true | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :developer | false | true | 'Composer package api request' | :success - 'PUBLIC' | :developer | false | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :guest | true | true | 'Composer package api request' | :success - 'PUBLIC' | :guest | true | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :guest | false | true | 'Composer package api request' | :success - 'PUBLIC' | :guest | false | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :anonymous | false | true | 'Composer package api request' | :success - 'PRIVATE' | :developer | true | true | 'Composer package api request' | :success - 'PRIVATE' | :developer | true | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :developer | false | true | 'process Composer api request' | :not_found - 'PRIVATE' | :developer | false | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :guest | true | true | 'process Composer api request' | :not_found - 'PRIVATE' | :guest | true | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :guest | false | true | 'process Composer api request' | :not_found - 'PRIVATE' | :guest | false | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :anonymous | false | true | 'process Composer api request' | :not_found + context 'with basic auth' do + where(:project_visibility_level, :member_role, :token_type, :valid_token, :shared_examples_name, :expected_status) do + 'PUBLIC' | :developer | :user | true | 'Composer package api request' | :success + 'PUBLIC' | :developer | :user | false | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | :developer | :job | true | 'Composer package api request' | :success + 'PUBLIC' | :guest | :user | true | 'Composer package api request' | :success + 'PUBLIC' | :guest | :user | false | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | :guest | :job | true | 'Composer package api request' | :success + 'PUBLIC' | nil | :user | true | 'Composer package api request' | :success + 'PUBLIC' | nil | :user | false | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | nil | :job | true | 'Composer package api request' | :success + 'PUBLIC' | nil | nil | nil | 'Composer package api request' | :success # Anonymous User + 'PRIVATE' | :developer | :user | true | 'Composer package api request' | :success + 'PRIVATE' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :developer | :job | true | 'Composer package api request' | :success + 'PRIVATE' | :guest | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :job | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :job | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | nil | nil | 'process Composer api request' | :unauthorized # Anonymous User + end + + with_them do + include_context 'Composer api group access', auth_method: :basic, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like params[:shared_examples_name], member_role: params[:member_role], expected_status: params[:expected_status] + end + end end - with_them do - include_context 'Composer api group access', params[:project_visibility_level], params[:user_role], params[:user_token] do - it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] + context 'with token auth' do + where(:project_visibility_level, :member_role, :token_type, :valid_token, :shared_examples_name, :expected_status) do + 'PUBLIC' | :developer | :user | true | 'Composer package api request' | :success + 'PUBLIC' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | :developer | :job | true | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | :guest | :user | true | 'Composer package api request' | :success + 'PUBLIC' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | :guest | :job | true | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | nil | :user | true | 'Composer package api request' | :success + 'PUBLIC' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | nil | :job | true | 'Composer package api request' | :success # Anonymous User - fallback + 'PUBLIC' | nil | nil | nil | 'Composer package api request' | :success # Anonymous User + 'PRIVATE' | :developer | :user | true | 'Composer package api request' | :success + 'PRIVATE' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :developer | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | nil | nil | 'process Composer api request' | :unauthorized # Anonymous User + end + + with_them do + include_context 'Composer api group access', auth_method: :token, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like params[:shared_examples_name], member_role: params[:member_role], expected_status: params[:expected_status] + end end end @@ -297,30 +415,65 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do shared_examples 'composer package publish' do context 'with valid project' do - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do - 'PUBLIC' | :developer | true | true | 'Composer package creation' | :created - 'PUBLIC' | :developer | true | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :developer | false | true | 'process Composer api request' | :forbidden - 'PUBLIC' | :developer | false | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :guest | true | true | 'process Composer api request' | :forbidden - 'PUBLIC' | :guest | true | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :guest | false | true | 'process Composer api request' | :forbidden - 'PUBLIC' | :guest | false | false | 'process Composer api request' | :unauthorized - 'PUBLIC' | :anonymous | false | true | 'process Composer api request' | :unauthorized - 'PRIVATE' | :developer | true | true | 'Composer package creation' | :created - 'PRIVATE' | :developer | true | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :developer | false | true | 'process Composer api request' | :not_found - 'PRIVATE' | :developer | false | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :guest | true | true | 'process Composer api request' | :forbidden - 'PRIVATE' | :guest | true | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :guest | false | true | 'process Composer api request' | :not_found - 'PRIVATE' | :guest | false | false | 'process Composer api request' | :unauthorized - 'PRIVATE' | :anonymous | false | true | 'process Composer api request' | :unauthorized + context 'with basic auth' do + where(:project_visibility_level, :member_role, :token_type, :valid_token, :shared_examples_name, :expected_status) do + 'PUBLIC' | :developer | :user | true | 'Composer package creation' | :created + 'PUBLIC' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | :developer | :job | true | 'Composer package creation' | :created + 'PUBLIC' | :guest | :user | true | 'process Composer api request' | :forbidden + 'PUBLIC' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | :guest | :job | true | 'process Composer api request' | :forbidden + 'PUBLIC' | nil | :user | true | 'process Composer api request' | :forbidden + 'PUBLIC' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | nil | :job | true | 'process Composer api request' | :forbidden + 'PUBLIC' | nil | nil | nil | 'process Composer api request' | :unauthorized # Anonymous User + 'PRIVATE' | :developer | :user | true | 'Composer package creation' | :created + 'PRIVATE' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :developer | :job | true | 'Composer package creation' | :created + 'PRIVATE' | :guest | :user | true | 'process Composer api request' | :forbidden + 'PRIVATE' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :job | true | 'process Composer api request' | :forbidden + 'PRIVATE' | nil | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :job | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | nil | nil | 'process Composer api request' | :unauthorized # Anonymous User + end + + with_them do + include_context 'Composer api project access', auth_method: :basic, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like params[:shared_examples_name], member_role: params[:member_role], expected_status: params[:expected_status] + end + end end - with_them do - include_context 'Composer api project access', params[:project_visibility_level], params[:user_role], params[:user_token] do - it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] + context 'with token auth' do + where(:project_visibility_level, :member_role, :token_type, :valid_token, :shared_examples_name, :expected_status) do + 'PUBLIC' | :developer | :user | true | 'Composer package creation' | :created + 'PUBLIC' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | :developer | :job | true | 'process Composer api request' | :unauthorized # Anonymous User - fallback + 'PUBLIC' | :guest | :user | true | 'process Composer api request' | :forbidden + 'PUBLIC' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | :guest | :job | true | 'process Composer api request' | :unauthorized # Anonymous User - fallback + 'PUBLIC' | nil | :user | true | 'process Composer api request' | :forbidden + 'PUBLIC' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PUBLIC' | nil | :job | true | 'process Composer api request' | :unauthorized # Anonymous User - fallback + 'PUBLIC' | nil | nil | nil | 'process Composer api request' | :unauthorized # Anonymous User + 'PRIVATE' | :developer | :user | true | 'Composer package creation' | :created + 'PRIVATE' | :developer | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :developer | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :user | true | 'process Composer api request' | :forbidden + 'PRIVATE' | :guest | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | :guest | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :user | true | 'process Composer api request' | :not_found + 'PRIVATE' | nil | :user | false | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | :job | true | 'process Composer api request' | :unauthorized + 'PRIVATE' | nil | nil | nil | 'process Composer api request' | :unauthorized # Anonymous User + end + + with_them do + include_context 'Composer api project access', auth_method: :token, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like params[:shared_examples_name], member_role: params[:member_role], expected_status: params[:expected_status] + end end end @@ -331,7 +484,7 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do end context 'with existing package' do - include_context 'Composer api project access', 'PRIVATE', :developer, true, true + include_context 'Composer api project access', auth_method: :token, project_visibility_level: 'PRIVATE', token_type: :user let_it_be_with_reload(:existing_package) { create(:composer_package, name: package_name, version: '1.2.99', project: project) } @@ -362,7 +515,7 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do context 'with no tag or branch params' do let(:headers) { basic_auth_header(user.username, personal_access_token.token) } - it_behaves_like 'process Composer api request', :developer, :bad_request + it_behaves_like 'process Composer api request', member_role: :developer, expected_status: :bad_request end context 'with a tag' do @@ -376,7 +529,7 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do let(:params) { { tag: 'non-existing-tag' } } let(:headers) { basic_auth_header(user.username, personal_access_token.token) } - it_behaves_like 'process Composer api request', :developer, :not_found + it_behaves_like 'process Composer api request', member_role: :developer, expected_status: :not_found end end @@ -391,7 +544,7 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do let(:params) { { branch: 'non-existing-branch' } } let(:headers) { basic_auth_header(user.username, personal_access_token.token) } - it_behaves_like 'process Composer api request', :developer, :not_found + it_behaves_like 'process Composer api request', member_role: :developer, expected_status: :not_found end end @@ -407,19 +560,19 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do context 'with a missing composer.json file' do let(:files) { { 'some_other_file' => '' } } - it_behaves_like 'process Composer api request', :developer, :unprocessable_entity + it_behaves_like 'process Composer api request', member_role: :developer, expected_status: :unprocessable_entity end context 'with an empty composer.json file' do let(:files) { { 'composer.json' => '' } } - it_behaves_like 'process Composer api request', :developer, :unprocessable_entity + it_behaves_like 'process Composer api request', member_role: :developer, expected_status: :unprocessable_entity end context 'with a malformed composer.json file' do let(:files) { { 'composer.json' => 'not_valid_JSON' } } - it_behaves_like 'process Composer api request', :developer, :unprocessable_entity + it_behaves_like 'process Composer api request', member_role: :developer, expected_status: :unprocessable_entity end end end @@ -446,10 +599,10 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do context 'anonymous' do let(:headers) { {} } - it_behaves_like 'process Composer api request', :anonymous, :unauthorized + it_behaves_like 'process Composer api request', expected_status: :unauthorized end - it_behaves_like 'process Composer api request', :developer, :not_found + it_behaves_like 'process Composer api request', member_role: :developer, expected_status: :not_found end context 'when the package name does not match the sha' do @@ -460,60 +613,116 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do context 'anonymous' do let(:headers) { {} } - it_behaves_like 'process Composer api request', :anonymous, :unauthorized + it_behaves_like 'process Composer api request', expected_status: :unauthorized end - it_behaves_like 'process Composer api request', :developer, :not_found + it_behaves_like 'process Composer api request', member_role: :developer, expected_status: :not_found end context 'with a match package name and sha' do let(:branch) { project.repository.find_branch('master') } let(:sha) { branch.target } - where(:project_visibility_level, :user_role, :member, :user_token, :expected_status) do - 'PUBLIC' | :developer | true | true | :success - 'PUBLIC' | :developer | true | false | :success - 'PUBLIC' | :developer | false | true | :success - 'PUBLIC' | :developer | false | false | :success - 'PUBLIC' | :guest | true | true | :success - 'PUBLIC' | :guest | true | false | :success - 'PUBLIC' | :guest | false | true | :success - 'PUBLIC' | :guest | false | false | :success - 'PUBLIC' | :anonymous | false | true | :success - 'PRIVATE' | :developer | true | true | :success - 'PRIVATE' | :developer | true | false | :unauthorized - 'PRIVATE' | :developer | false | true | :not_found - 'PRIVATE' | :developer | false | false | :unauthorized - 'PRIVATE' | :guest | true | true | :forbidden - 'PRIVATE' | :guest | true | false | :unauthorized - 'PRIVATE' | :guest | false | true | :not_found - 'PRIVATE' | :guest | false | false | :unauthorized - 'PRIVATE' | :anonymous | false | true | :unauthorized - end + context 'with basic auth' do + where(:project_visibility_level, :member_role, :token_type, :valid_token, :expected_status) do + 'PUBLIC' | :developer | :user | true | :success + 'PUBLIC' | :developer | :user | false | :success # Anonymous User - fallback + 'PUBLIC' | :developer | :job | true | :success + 'PUBLIC' | :guest | :user | true | :success + 'PUBLIC' | :guest | :user | false | :success # Anonymous User - fallback + 'PUBLIC' | :guest | :job | true | :success + 'PUBLIC' | nil | :user | true | :success + 'PUBLIC' | nil | :user | false | :success # Anonymous User - fallback + 'PUBLIC' | nil | :job | true | :success + 'PUBLIC' | nil | nil | nil | :success # Anonymous User + 'PRIVATE' | :developer | :user | true | :success + 'PRIVATE' | :developer | :user | false | :unauthorized + 'PRIVATE' | :developer | :job | true | :success + 'PRIVATE' | :guest | :user | true | :forbidden + 'PRIVATE' | :guest | :user | false | :unauthorized + 'PRIVATE' | :guest | :job | true | :forbidden + 'PRIVATE' | nil | :user | true | :not_found + 'PRIVATE' | nil | :user | false | :unauthorized + 'PRIVATE' | nil | :job | true | :not_found + 'PRIVATE' | nil | nil | nil | :unauthorized # Anonymous User + end - with_them do - let(:token) { user_token ? personal_access_token.token : 'wrong' } - let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) } + with_them do + include_context 'Composer api project access', auth_method: :basic, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like 'process Composer api request', member_role: params[:member_role], expected_status: params[:expected_status] do + if params[:expected_status] == :success + let(:snowplow_gitlab_standard_context) do + if valid_token && (member_role || project_visibility_level == 'PUBLIC') + { project: project, namespace: project.namespace, property: 'i_package_composer_user', user: user } + else + { project: project, namespace: project.namespace, property: 'i_package_composer_user' } + end + end + + it_behaves_like 'a package tracking event', described_class.name, 'pull_package' + else + it_behaves_like 'not a package tracking event' + end + end - before do - project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false)) + context 'with another project' do + include Ci::JobTokenScopeHelpers + + let_it_be(:project_two) { create(:project, group: group) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project_two) } + + before do + add_inbound_accessible_linkage(project_two, project) + end + + it_behaves_like 'process Composer api request', member_role: params[:member_role], expected_status: params[:expected_status] + end + end end + end - it_behaves_like 'process Composer api request', params[:user_role], params[:expected_status], params[:member] + context 'with token auth' do + where(:project_visibility_level, :member_role, :token_type, :valid_token, :expected_status) do + 'PUBLIC' | :developer | :user | true | :success + 'PUBLIC' | :developer | :user | false | :unauthorized + 'PUBLIC' | :developer | :job | true | :success # Anonymous User - fallback + 'PUBLIC' | :guest | :user | true | :success + 'PUBLIC' | :guest | :user | false | :unauthorized + 'PUBLIC' | :guest | :job | true | :success # Anonymous User - fallback + 'PUBLIC' | nil | :user | true | :success + 'PUBLIC' | nil | :user | false | :unauthorized + 'PUBLIC' | nil | :job | true | :success # Anonymous User - fallback + 'PUBLIC' | nil | nil | nil | :success # Anonymous User + 'PRIVATE' | :developer | :user | true | :success + 'PRIVATE' | :developer | :user | false | :unauthorized + 'PRIVATE' | :developer | :job | true | :unauthorized + 'PRIVATE' | :guest | :user | true | :forbidden + 'PRIVATE' | :guest | :user | false | :unauthorized + 'PRIVATE' | :guest | :job | true | :unauthorized + 'PRIVATE' | nil | :user | true | :not_found + 'PRIVATE' | nil | :user | false | :unauthorized + 'PRIVATE' | nil | :job | true | :unauthorized + 'PRIVATE' | nil | nil | nil | :unauthorized # Anonymous User + end - include_context 'Composer user type', params[:user_role], params[:member] do - if params[:expected_status] == :success - let(:snowplow_gitlab_standard_context) do - if user_role == :anonymous || (project_visibility_level == 'PUBLIC' && user_token == false) - { project: project, namespace: project.namespace, property: 'i_package_composer_user' } + with_them do + include_context 'Composer api project access', auth_method: :token, project_visibility_level: params[:project_visibility_level], token_type: params[:token_type], valid_token: params[:valid_token] do + it_behaves_like 'process Composer api request', member_role: params[:member_role], expected_status: params[:expected_status] do + if params[:expected_status] == :success + let(:snowplow_gitlab_standard_context) do + # Job tokens sent over token auth means current_user is nil + if valid_token && token_type != :job && (member_role || project_visibility_level == 'PUBLIC') + { project: project, namespace: project.namespace, property: 'i_package_composer_user', user: user } + else + { project: project, namespace: project.namespace, property: 'i_package_composer_user' } + end + end + + it_behaves_like 'a package tracking event', described_class.name, 'pull_package' else - { project: project, namespace: project.namespace, property: 'i_package_composer_user', user: user } + it_behaves_like 'not a package tracking event' end end - - it_behaves_like 'a package tracking event', described_class.name, 'pull_package' - else - it_behaves_like 'not a package tracking event' end end end diff --git a/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb b/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb index ef159e41d3d..8d980a9e8ea 100644 --- a/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb @@ -36,8 +36,6 @@ RSpec.describe 'Destroying a container repository', feature_category: :container it 'marks the container repository as delete_scheduled' do expect(::Packages::CreateEventService) .to receive(:new).with(nil, user, event_name: :delete_repository, scope: :container).and_call_original - expect(DeleteContainerRepositoryWorker) - .not_to receive(:perform_async) subject @@ -50,9 +48,6 @@ RSpec.describe 'Destroying a container repository', feature_category: :container shared_examples 'denying the mutation request' do it 'does not destroy the container repository' do - expect(DeleteContainerRepositoryWorker) - .not_to receive(:perform_async).with(user.id, container_repository.id) - subject expect(mutation_response).to be_nil diff --git a/spec/requests/api/graphql/mutations/work_items/linked_items/add_spec.rb b/spec/requests/api/graphql/mutations/work_items/linked_items/add_spec.rb index f30b7d0ea73..5040ee39712 100644 --- a/spec/requests/api/graphql/mutations/work_items/linked_items/add_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/linked_items/add_spec.rb @@ -5,15 +5,17 @@ require 'spec_helper' RSpec.describe "Add linked items to a work item", feature_category: :portfolio_management do include GraphqlHelpers - let_it_be(:project) { create(:project, :private) } - let_it_be(:reporter) { create(:user).tap { |user| project.add_reporter(user) } } - let_it_be(:work_item) { create(:work_item, project: project) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :private, group: group) } + let_it_be(:reporter) { create(:user).tap { |user| group.add_reporter(user) } } + let_it_be(:project_work_item) { create(:work_item, project: project) } let_it_be(:related1) { create(:work_item, project: project) } let_it_be(:related2) { create(:work_item, project: project) } let(:mutation_response) { graphql_mutation_response(:work_item_add_linked_items) } let(:mutation) { graphql_mutation(:workItemAddLinkedItems, input, fields) } + let(:work_item) { project_work_item } let(:ids_to_link) { [related1.to_global_id.to_s, related2.to_global_id.to_s] } let(:input) { { 'id' => work_item.to_global_id.to_s, 'workItemsIds' => ids_to_link } } @@ -70,6 +72,18 @@ RSpec.describe "Add linked items to a work item", feature_category: :portfolio_m ) end + context 'when work item is created at the group level' do + let(:work_item) { create(:work_item, :group_level, namespace: group) } + + it 'links the work item' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to change { WorkItems::RelatedWorkItemLink.count }.by(2) + + expect(mutation_response['message']).to eq("Successfully linked ID(s): #{related1.id} and #{related2.id}.") + end + end + context 'when linking a work item fails' do let_it_be(:private_project) { create(:project, :private) } let_it_be(:related2) { create(:work_item, project: private_project) } diff --git a/spec/requests/api/project_container_repositories_spec.rb b/spec/requests/api/project_container_repositories_spec.rb index a2e1a1c1721..f51b94bb78e 100644 --- a/spec/requests/api/project_container_repositories_spec.rb +++ b/spec/requests/api/project_container_repositories_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe API::ProjectContainerRepositories, feature_category: :package_registry do +RSpec.describe API::ProjectContainerRepositories, feature_category: :container_registry do include ExclusiveLeaseHelpers let_it_be(:project) { create(:project, :private) } @@ -142,7 +142,6 @@ RSpec.describe API::ProjectContainerRepositories, feature_category: :package_reg let(:api_user) { maintainer } it 'marks the repository as delete_scheduled' do - expect(DeleteContainerRepositoryWorker).not_to receive(:perform_async) expect { subject }.to change { root_repository.reload.status }.from(nil).to('delete_scheduled') expect(response).to have_gitlab_http_status(:accepted) diff --git a/spec/support/shared_examples/ref_extraction_shared_examples.rb b/spec/support/shared_examples/ref_extraction_shared_examples.rb new file mode 100644 index 00000000000..f51c3a16406 --- /dev/null +++ b/spec/support/shared_examples/ref_extraction_shared_examples.rb @@ -0,0 +1,165 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'extracts ref vars' do + describe '#extract!' do + context 'when ref contains %20' do + let(:ref) { 'foo%20bar' } + + it 'is not converted to a space in @id' do + container.repository.add_branch(owner, 'foo%20bar', 'master') + + ref_extractor.extract! + + expect(ref_extractor.id).to start_with('foo%20bar/') + end + end + + context 'when ref contains trailing space' do + let(:ref) { 'master ' } + + it 'strips surrounding space' do + ref_extractor.extract! + + expect(ref_extractor.ref).to eq('master') + end + end + + context 'when ref contains leading space' do + let(:ref) { ' master ' } + + it 'strips surrounding space' do + ref_extractor.extract! + + expect(ref_extractor.ref).to eq('master') + end + end + + context 'when path contains space' do + let(:ref) { '38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e' } + let(:path) { 'with space' } + + it 'is not converted to %20 in @path' do + ref_extractor.extract! + + expect(ref_extractor.path).to eq(path) + end + end + + context 'when override_id is given' do + let(:ref_extractor) do + described_class.new(container, params, override_id: '38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e') + end + + it 'uses override_id' do + ref_extractor.extract! + + expect(ref_extractor.id).to eq('38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e') + end + end + end +end + +RSpec.shared_examples 'extracts ref method' do + describe '#extract_ref' do + it 'returns an empty pair when no repository_container is set' do + allow_next_instance_of(described_class) do |instance| + allow(instance).to receive(:repository_container).and_return(nil) + end + expect(ref_extractor.extract_ref('master/CHANGELOG')).to eq(['', '']) + end + + context 'without a path' do + it 'extracts a valid branch' do + expect(ref_extractor.extract_ref('master')).to eq(['master', '']) + end + + it 'extracts a valid tag' do + expect(ref_extractor.extract_ref('v2.0.0')).to eq(['v2.0.0', '']) + end + + it 'extracts a valid commit ref' do + expect(ref_extractor.extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062')).to eq( + ['f4b14494ef6abf3d144c28e4af0c20143383e062', ''] + ) + end + + it 'falls back to a primitive split for an invalid ref' do + expect(ref_extractor.extract_ref('stable')).to eq(['stable', '']) + end + + it 'does not fetch ref names when there is no slash' do + expect(ref_extractor).not_to receive(:ref_names) + + ref_extractor.extract_ref('master') + end + + it 'fetches ref names when there is a slash' do + expect(ref_extractor).to receive(:ref_names).and_call_original + + ref_extractor.extract_ref('release/app/v1.0.0') + end + end + + context 'with a path' do + it 'extracts a valid branch' do + expect(ref_extractor.extract_ref('foo/bar/baz/CHANGELOG')).to eq( + ['foo/bar/baz', 'CHANGELOG']) + end + + it 'extracts a valid tag' do + expect(ref_extractor.extract_ref('v2.0.0/CHANGELOG')).to eq(['v2.0.0', 'CHANGELOG']) + end + + it 'extracts a valid commit SHA' do + expect(ref_extractor.extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062/CHANGELOG')).to eq( + %w[f4b14494ef6abf3d144c28e4af0c20143383e062 CHANGELOG] + ) + end + + it 'falls back to a primitive split for an invalid ref' do + expect(ref_extractor.extract_ref('stable/CHANGELOG')).to eq(%w[stable CHANGELOG]) + end + + it 'extracts the longest matching ref' do + expect(ref_extractor.extract_ref('release/app/v1.0.0/README.md')).to eq( + ['release/app/v1.0.0', 'README.md']) + end + + context 'when the repository does not have ambiguous refs' do + before do + allow(container.repository).to receive(:has_ambiguous_refs?).and_return(false) + end + + it 'does not fetch all ref names when the first path component is a ref' do + expect(ref_extractor).not_to receive(:ref_names) + expect(container.repository).to receive(:branch_names_include?).with('v1.0.0').and_return(false) + expect(container.repository).to receive(:tag_names_include?).with('v1.0.0').and_return(true) + + expect(ref_extractor.extract_ref('v1.0.0/doc/README.md')).to eq(['v1.0.0', 'doc/README.md']) + end + + it 'fetches all ref names when the first path component is not a ref' do + expect(ref_extractor).to receive(:ref_names).and_call_original + expect(container.repository).to receive(:branch_names_include?).with('release').and_return(false) + expect(container.repository).to receive(:tag_names_include?).with('release').and_return(false) + + expect(ref_extractor.extract_ref('release/app/doc/README.md')).to eq(['release/app', 'doc/README.md']) + end + end + + context 'when the repository has ambiguous refs' do + before do + allow(container.repository).to receive(:has_ambiguous_refs?).and_return(true) + end + + it 'always fetches all ref names' do + expect(ref_extractor).to receive(:ref_names).and_call_original + expect(container.repository).not_to receive(:branch_names_include?) + expect(container.repository).not_to receive(:tag_names_include?) + + expect(ref_extractor.extract_ref('v1.0.0/doc/README.md')).to eq(['v1.0.0', 'doc/README.md']) + end + end + end + end +end diff --git a/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb index 6a77de4266f..7e0efd05dd7 100644 --- a/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb @@ -1,42 +1,45 @@ # frozen_string_literal: true -RSpec.shared_context 'Composer user type' do |user_type, add_member| +RSpec.shared_context 'Composer user type' do |member_role: nil| before do - group.send("add_#{user_type}", user) if add_member && user_type != :anonymous - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous + if member_role + group.send("add_#{member_role}", user) + project.send("add_#{member_role}", user) + end end end -RSpec.shared_examples 'Composer package index with version' do |schema_path| +RSpec.shared_examples 'Composer package index with version' do |schema_path, expected_status| it 'returns the package index' do subject - expect(response).to have_gitlab_http_status(status) + expect(response).to have_gitlab_http_status(expected_status) - if status == :success + if expected_status == :success expect(response).to match_response_schema(schema_path) expect(json_response).to eq presenter.root end end end -RSpec.shared_examples 'Composer package index' do |user_type, status, add_member, include_package| - include_context 'Composer user type', user_type, add_member do - let(:expected_packages) { include_package == :include_package ? [package] : [] } - let(:presenter) { ::Packages::Composer::PackagesPresenter.new(group, expected_packages ) } +RSpec.shared_examples 'Composer package index' do |member_role:, expected_status:, package_returned:| + include_context 'Composer user type', member_role: member_role do + let_it_be(:expected_packages) { package_returned ? [package] : [] } + let_it_be(:presenter) { ::Packages::Composer::PackagesPresenter.new(group, expected_packages ) } - it_behaves_like 'Composer package index with version', 'public_api/v4/packages/composer/index' + it_behaves_like 'Composer package index with version', 'public_api/v4/packages/composer/index', expected_status context 'with version 2' do + let_it_be(:presenter) { ::Packages::Composer::PackagesPresenter.new(group, expected_packages, true ) } let(:headers) { super().merge('User-Agent' => 'Composer/2.0.9 (Darwin; 19.6.0; PHP 7.4.8; cURL 7.71.1)') } - it_behaves_like 'Composer package index with version', 'public_api/v4/packages/composer/index_v2' + it_behaves_like 'Composer package index with version', 'public_api/v4/packages/composer/index_v2', expected_status end end end -RSpec.shared_examples 'Composer empty provider index' do |user_type, status, add_member = true| - include_context 'Composer user type', user_type, add_member do +RSpec.shared_examples 'Composer empty provider index' do |member_role:, expected_status:| + include_context 'Composer user type', member_role: member_role do it 'returns the package index' do subject @@ -47,24 +50,24 @@ RSpec.shared_examples 'Composer empty provider index' do |user_type, status, add end end -RSpec.shared_examples 'Composer provider index' do |user_type, status, add_member = true| - include_context 'Composer user type', user_type, add_member do +RSpec.shared_examples 'Composer provider index' do |member_role:, expected_status:| + include_context 'Composer user type', member_role: member_role do it 'returns the package index' do subject - expect(response).to have_gitlab_http_status(status) + expect(response).to have_gitlab_http_status(expected_status) expect(response).to match_response_schema('public_api/v4/packages/composer/provider') expect(json_response['providers']).to include(package.name) end end end -RSpec.shared_examples 'Composer package api request' do |user_type, status, add_member = true| - include_context 'Composer user type', user_type, add_member do +RSpec.shared_examples 'Composer package api request' do |member_role:, expected_status:| + include_context 'Composer user type', member_role: member_role do it 'returns the package index' do subject - expect(response).to have_gitlab_http_status(status) + expect(response).to have_gitlab_http_status(expected_status) expect(response).to match_response_schema('public_api/v4/packages/composer/package') expect(json_response['packages']).to include(package.name) expect(json_response['packages'][package.name]).to include(package.version) @@ -72,18 +75,13 @@ RSpec.shared_examples 'Composer package api request' do |user_type, status, add_ end end -RSpec.shared_examples 'Composer package creation' do |user_type, status, add_member = true| - context "for user type #{user_type}" do - before do - group.send("add_#{user_type}", user) if add_member && user_type != :anonymous - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous - end - +RSpec.shared_examples 'Composer package creation' do |expected_status:, member_role: nil| + include_context 'Composer user type', member_role: member_role do it 'creates package files' do expect { subject } .to change { project.packages.composer.count }.by(1) - expect(response).to have_gitlab_http_status(status) + expect(response).to have_gitlab_http_status(expected_status) end it_behaves_like 'a package tracking event', described_class.name, 'push_package' @@ -100,42 +98,38 @@ RSpec.shared_examples 'Composer package creation' do |user_type, status, add_mem end end -RSpec.shared_examples 'process Composer api request' do |user_type, status, add_member = true| - context "for user type #{user_type}" do - before do - group.send("add_#{user_type}", user) if add_member && user_type != :anonymous - project.send("add_#{user_type}", user) if add_member && user_type != :anonymous - end - - it_behaves_like 'returning response status', status - it_behaves_like 'bumping the package last downloaded at field' if status == :success +RSpec.shared_examples 'process Composer api request' do |expected_status:, member_role: nil, **extra| + include_context 'Composer user type', member_role: member_role do + it_behaves_like 'returning response status', expected_status + it_behaves_like 'bumping the package last downloaded at field' if expected_status == :success end end -RSpec.shared_context 'Composer auth headers' do |user_role, user_token, auth_method = :token| - let(:token) { user_token ? personal_access_token.token : 'wrong' } - +RSpec.shared_context 'Composer auth headers' do |token_type:, valid_token:, auth_method: :token| let(:headers) do - if user_role == :anonymous - {} - elsif auth_method == :token - { 'Private-Token' => token } + if token_type == :user + token = valid_token ? personal_access_token.token : 'wrong' + auth_method == :token ? { 'Private-Token' => token } : basic_auth_header(user.username, token) + elsif token_type == :job && valid_token + auth_method == :token ? { 'Job-Token' => job.token } : job_basic_auth_header(job) else - basic_auth_header(user.username, token) + {} # Anonymous user end end end -RSpec.shared_context 'Composer api project access' do |project_visibility_level, user_role, user_token, auth_method| - include_context 'Composer auth headers', user_role, user_token, auth_method do +RSpec.shared_context 'Composer api project access' do |auth_method:, project_visibility_level:, token_type:, + valid_token: true| + include_context 'Composer auth headers', auth_method: auth_method, token_type: token_type, valid_token: valid_token do before do project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false)) end end end -RSpec.shared_context 'Composer api group access' do |project_visibility_level, user_role, user_token| - include_context 'Composer auth headers', user_role, user_token do +RSpec.shared_context 'Composer api group access' do |auth_method:, project_visibility_level:, token_type:, + valid_token: true| + include_context 'Composer auth headers', auth_method: auth_method, token_type: token_type, valid_token: valid_token do before do project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false)) group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false)) @@ -148,13 +142,13 @@ RSpec.shared_examples 'rejects Composer access with unknown group id' do let(:group) { double(id: non_existing_record_id) } context 'as anonymous' do - it_behaves_like 'process Composer api request', :anonymous, :not_found + it_behaves_like 'process Composer api request', expected_status: :unauthorized end context 'as authenticated user' do subject { get api(url), headers: basic_auth_header(user.username, personal_access_token.token) } - it_behaves_like 'process Composer api request', :anonymous, :not_found + it_behaves_like 'process Composer api request', expected_status: :not_found end end end @@ -164,13 +158,13 @@ RSpec.shared_examples 'rejects Composer access with unknown project id' do let(:project) { double(id: non_existing_record_id) } context 'as anonymous' do - it_behaves_like 'process Composer api request', :anonymous, :unauthorized + it_behaves_like 'process Composer api request', expected_status: :unauthorized end context 'as authenticated user' do subject { get api(url), params: params, headers: basic_auth_header(user.username, personal_access_token.token) } - it_behaves_like 'process Composer api request', :anonymous, :not_found + it_behaves_like 'process Composer api request', expected_status: :not_found end end end @@ -191,7 +185,7 @@ RSpec.shared_examples 'Composer access with deploy tokens' do context 'invalid token' do let(:headers) { basic_auth_header(deploy_token.username, 'bar') } - it_behaves_like 'returning response status', :not_found + it_behaves_like 'returning response status', :unauthorized end end end diff --git a/spec/views/projects/merge_requests/edit.html.haml_spec.rb b/spec/views/projects/merge_requests/edit.html.haml_spec.rb index bb8a4455775..5affe2dd9b4 100644 --- a/spec/views/projects/merge_requests/edit.html.haml_spec.rb +++ b/spec/views/projects/merge_requests/edit.html.haml_spec.rb @@ -46,65 +46,28 @@ RSpec.describe 'projects/merge_requests/edit.html.haml' do end end - context 'with the visible_label_selection_on_metadata feature flag enabled' do - before do - stub_feature_flags(visible_label_selection_on_metadata: true) - end - - context 'when a merge request without fork' do - it_behaves_like 'merge request shows editable fields' - - it "shows editable fields" do - unlink_project.execute - closed_merge_request.reload - - render + context 'when a merge request without fork' do + it_behaves_like 'merge request shows editable fields' - expect(rendered).not_to have_selector('#merge_request_target_branch', visible: false) - expect(rendered).to have_selector('.js-issuable-form-label-selector') - end - end - - context 'when a merge request with an existing source project is closed' do - it_behaves_like 'merge request shows editable fields' + it "shows editable fields" do + unlink_project.execute + closed_merge_request.reload - it "shows editable fields" do - render + render - expect(rendered).to have_selector('#merge_request_target_branch', visible: false) - expect(rendered).to have_selector('.js-issuable-form-label-selector') - end + expect(rendered).not_to have_selector('#merge_request_target_branch', visible: false) + expect(rendered).to have_selector('.js-issuable-form-label-selector') end end - context 'with the visible_label_selection_on_metadata feature flag disabled' do - before do - stub_feature_flags(visible_label_selection_on_metadata: false) - end - - context 'when a merge request without fork' do - it_behaves_like 'merge request shows editable fields' - - it "shows editable fields" do - unlink_project.execute - closed_merge_request.reload - - render - - expect(rendered).not_to have_selector('#merge_request_target_branch', visible: false) - expect(rendered).not_to have_selector('.js-issuable-form-label-selector') - end - end - - context 'when a merge request with an existing source project is closed' do - it_behaves_like 'merge request shows editable fields' + context 'when a merge request with an existing source project is closed' do + it_behaves_like 'merge request shows editable fields' - it "shows editable fields" do - render + it "shows editable fields" do + render - expect(rendered).to have_selector('#merge_request_target_branch', visible: false) - expect(rendered).not_to have_selector('.js-issuable-form-label-selector') - end + expect(rendered).to have_selector('#merge_request_target_branch', visible: false) + expect(rendered).to have_selector('.js-issuable-form-label-selector') end end end diff --git a/spec/workers/delete_container_repository_worker_spec.rb b/spec/workers/delete_container_repository_worker_spec.rb deleted file mode 100644 index 6260bea6949..00000000000 --- a/spec/workers/delete_container_repository_worker_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe DeleteContainerRepositoryWorker, feature_category: :container_registry do - let_it_be(:repository) { create(:container_repository) } - - let(:project) { repository.project } - let(:user) { project.first_owner } - let(:worker) { described_class.new } - - describe '#perform' do - subject(:perform) { worker.perform(user.id, repository.id) } - - it 'is a no op' do - expect { subject }.to not_change { ContainerRepository.count } - end - end -end diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb index c5964d3460f..453d64e94fa 100644 --- a/spec/workers/every_sidekiq_worker_spec.rb +++ b/spec/workers/every_sidekiq_worker_spec.rb @@ -198,7 +198,6 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'Database::LockTablesWorker' => false, 'Database::BatchedBackgroundMigration::CiExecutionWorker' => 0, 'Database::BatchedBackgroundMigration::MainExecutionWorker' => 0, - 'DeleteContainerRepositoryWorker' => 3, 'DeleteDiffFilesWorker' => 3, 'DeleteMergedBranchesWorker' => 3, 'DeleteStoredFilesWorker' => 3, |