Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-03-06 12:08:54 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-03-06 12:08:54 +0300
commit4903f95b04db58edc1931ec917d2313c916a06b2 (patch)
tree722e74b8e49f12f8f7aebffc2fa58c9ccc126fb1 /spec
parent6367f9bd9cca41c932997c94a338556ded4ced8f (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/profiles/user_deletes_saved_reply_spec.rb27
-rw-r--r--spec/features/projects/work_items/work_item_children_spec.rb (renamed from spec/features/work_items/work_item_children_spec.rb)0
-rw-r--r--spec/features/projects/work_items/work_item_spec.rb (renamed from spec/features/work_items/work_item_spec.rb)0
-rw-r--r--spec/frontend/saved_replies/components/__snapshots__/list_item_spec.js.snap31
-rw-r--r--spec/frontend/saved_replies/components/list_item_spec.js32
-rw-r--r--spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb104
-rw-r--r--spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb10
-rw-r--r--spec/migrations/20230221093533_add_tmp_partial_index_on_vulnerability_report_types_spec.rb22
-rw-r--r--spec/requests/users_controller_spec.rb100
-rw-r--r--spec/serializers/profile/event_entity_spec.rb149
10 files changed, 452 insertions, 23 deletions
diff --git a/spec/features/profiles/user_deletes_saved_reply_spec.rb b/spec/features/profiles/user_deletes_saved_reply_spec.rb
new file mode 100644
index 00000000000..35bd6018ee3
--- /dev/null
+++ b/spec/features/profiles/user_deletes_saved_reply_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Profile > Saved replies > User deletes saved reply', :js,
+ feature_category: :user_profile do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:saved_reply) { create(:saved_reply, user: user) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'shows the user a list of their saved replies' do
+ visit profile_saved_replies_path
+
+ find('[data-testid="saved-reply-delete-btn"]').click
+
+ page.within('.gl-modal') do
+ click_button 'Delete'
+ end
+
+ wait_for_requests
+
+ expect(page).not_to have_content(saved_reply.name)
+ end
+end
diff --git a/spec/features/work_items/work_item_children_spec.rb b/spec/features/projects/work_items/work_item_children_spec.rb
index 43a6b2771f6..43a6b2771f6 100644
--- a/spec/features/work_items/work_item_children_spec.rb
+++ b/spec/features/projects/work_items/work_item_children_spec.rb
diff --git a/spec/features/work_items/work_item_spec.rb b/spec/features/projects/work_items/work_item_spec.rb
index 3c71a27ff82..3c71a27ff82 100644
--- a/spec/features/work_items/work_item_spec.rb
+++ b/spec/features/projects/work_items/work_item_spec.rb
diff --git a/spec/frontend/saved_replies/components/__snapshots__/list_item_spec.js.snap b/spec/frontend/saved_replies/components/__snapshots__/list_item_spec.js.snap
index 3abdfcdaf20..154ce2bd089 100644
--- a/spec/frontend/saved_replies/components/__snapshots__/list_item_spec.js.snap
+++ b/spec/frontend/saved_replies/components/__snapshots__/list_item_spec.js.snap
@@ -10,6 +10,21 @@ exports[`Saved replies list item component renders list item 1`] = `
<strong>
test
</strong>
+
+ <div
+ class="gl-ml-auto"
+ >
+ <gl-button-stub
+ aria-label="Delete"
+ buttontextclasses=""
+ category="secondary"
+ data-testid="saved-reply-delete-btn"
+ icon="remove"
+ size="medium"
+ title="Delete"
+ variant="danger"
+ />
+ </div>
</div>
<div
@@ -17,5 +32,21 @@ exports[`Saved replies list item component renders list item 1`] = `
>
/assign_reviewer
</div>
+
+ <gl-modal-stub
+ actionprimary="[object Object]"
+ actionsecondary="[object Object]"
+ arialabel=""
+ dismisslabel="Close"
+ modalclass=""
+ modalid="delete-saved-reply-2"
+ size="sm"
+ title="Delete saved reply"
+ titletag="h4"
+ >
+ <gl-sprintf-stub
+ message="Are you sure you want to delete %{name}? This action cannot be undone."
+ />
+ </gl-modal-stub>
</li>
`;
diff --git a/spec/frontend/saved_replies/components/list_item_spec.js b/spec/frontend/saved_replies/components/list_item_spec.js
index cad1000473b..1d80844f30a 100644
--- a/spec/frontend/saved_replies/components/list_item_spec.js
+++ b/spec/frontend/saved_replies/components/list_item_spec.js
@@ -1,11 +1,31 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import { shallowMount } from '@vue/test-utils';
+import { GlModal } from '@gitlab/ui';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { createMockDirective } from 'helpers/vue_mock_directive';
+import waitForPromises from 'helpers/wait_for_promises';
import ListItem from '~/saved_replies/components/list_item.vue';
+import deleteSavedReplyMutation from '~/saved_replies/queries/delete_saved_reply.mutation.graphql';
let wrapper;
+let deleteSavedReplyMutationResponse;
function createComponent(propsData = {}) {
+ Vue.use(VueApollo);
+
+ deleteSavedReplyMutationResponse = jest
+ .fn()
+ .mockResolvedValue({ data: { savedReplyDestroy: { errors: [] } } });
+
return shallowMount(ListItem, {
propsData,
+ directives: {
+ GlModal: createMockDirective('gl-modal'),
+ },
+ apolloProvider: createMockApollo([
+ [deleteSavedReplyMutation, deleteSavedReplyMutationResponse],
+ ]),
});
}
@@ -19,4 +39,16 @@ describe('Saved replies list item component', () => {
expect(wrapper.element).toMatchSnapshot();
});
+
+ describe('delete button', () => {
+ it('calls Apollo mutate', async () => {
+ wrapper = createComponent({ reply: { name: 'test', content: '/assign_reviewer', id: 1 } });
+
+ wrapper.findComponent(GlModal).vm.$emit('primary');
+
+ await waitForPromises();
+
+ expect(deleteSavedReplyMutationResponse).toHaveBeenCalledWith({ id: 1 });
+ });
+ });
});
diff --git a/spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb b/spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb
index 08d255a4bb8..4dd510499ab 100644
--- a/spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Gitlab::Database::AsyncConstraints::MigrationHelpers, feature_cat
let(:column_name) { 'parent_id' }
let(:fk_name) { nil }
- context 'with regular tables' do
+ context 'with async FK validation on regular tables' do
before do
allow(migration).to receive(:puts)
allow(migration.connection).to receive(:transaction_open?).and_return(false)
@@ -183,4 +183,106 @@ RSpec.describe Gitlab::Database::AsyncConstraints::MigrationHelpers, feature_cat
end
end
end
+
+ context 'with async check constraint validations' do
+ let(:table_name) { '_test_async_check_constraints' }
+ let(:check_name) { 'partitioning_constraint' }
+
+ before do
+ allow(migration).to receive(:puts)
+ allow(migration.connection).to receive(:transaction_open?).and_return(false)
+
+ connection.create_table(table_name) do |t|
+ t.integer column_name
+ end
+
+ migration.add_check_constraint(
+ table_name, "#{column_name} = 1",
+ check_name, validate: false)
+ end
+
+ describe '#prepare_async_check_constraint_validation' do
+ it 'creates the record for async validation' do
+ expect do
+ migration.prepare_async_check_constraint_validation(table_name, name: check_name)
+ end.to change { constraint_model.where(name: check_name).count }.by(1)
+
+ record = constraint_model.find_by(name: check_name)
+
+ expect(record.table_name).to eq(table_name)
+ expect(record).to be_check_constraint
+ end
+
+ context 'when the check constraint does not exist' do
+ it 'returns an error' do
+ expect do
+ migration.prepare_async_check_constraint_validation(table_name, name: 'missing')
+ end.to raise_error RuntimeError, /Could not find check constraint "missing" on table "#{table_name}"/
+ end
+ end
+
+ context 'when the record already exists' do
+ it 'does attempt to create the record' do
+ create(:postgres_async_constraint_validation,
+ table_name: table_name,
+ name: check_name,
+ constraint_type: :check_constraint)
+
+ expect do
+ migration.prepare_async_check_constraint_validation(table_name, name: check_name)
+ end.not_to change { constraint_model.where(name: check_name).count }
+ end
+ end
+
+ context 'when the async validation table does not exist' do
+ it 'does not raise an error' do
+ connection.drop_table(constraint_model.table_name)
+
+ expect(constraint_model).not_to receive(:safe_find_or_create_by!)
+
+ expect { migration.prepare_async_check_constraint_validation(table_name, name: check_name) }
+ .not_to raise_error
+ end
+ end
+ end
+
+ describe '#unprepare_async_check_constraint_validation' do
+ context 'with check constraints' do
+ before do
+ migration.prepare_async_check_constraint_validation(table_name, name: check_name)
+ end
+
+ it 'destroys the record' do
+ expect do
+ migration.unprepare_async_check_constraint_validation(table_name, name: check_name)
+ end.to change { constraint_model.where(name: check_name).count }.by(-1)
+ end
+
+ context 'when the async validation table does not exist' do
+ it 'does not raise an error' do
+ connection.drop_table(constraint_model.table_name)
+
+ expect(constraint_model).not_to receive(:find_by)
+
+ expect { migration.unprepare_async_check_constraint_validation(table_name, name: check_name) }
+ .not_to raise_error
+ end
+ end
+ end
+
+ context 'with other types of constraints' do
+ let(:constraint) { create(:postgres_async_constraint_validation, table_name: table_name, name: check_name) }
+
+ it 'does not destroy the record' do
+ constraint.update_column(:constraint_type, 99)
+
+ expect do
+ migration.unprepare_async_check_constraint_validation(table_name, name: check_name)
+ end.not_to change { constraint_model.where(name: check_name).count }
+
+ expect(constraint).to be_present
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb b/spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb
index 7fea2eb1bcc..da9cc18f7d7 100644
--- a/spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb
+++ b/spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb
@@ -48,6 +48,16 @@ RSpec.describe Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValida
is_expected.to match_array([failed_validation, new_validation])
end
end
+
+ describe '.check_constraint_type' do
+ before do
+ new_validation.update!(constraint_type: :check_constraint)
+ end
+
+ subject { described_class.check_constraint_type }
+
+ it { is_expected.to eq([new_validation]) }
+ end
end
describe '.table_available?' do
diff --git a/spec/migrations/20230221093533_add_tmp_partial_index_on_vulnerability_report_types_spec.rb b/spec/migrations/20230221093533_add_tmp_partial_index_on_vulnerability_report_types_spec.rb
new file mode 100644
index 00000000000..cbf6b2882c4
--- /dev/null
+++ b/spec/migrations/20230221093533_add_tmp_partial_index_on_vulnerability_report_types_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+require_migration!
+
+RSpec.describe AddTmpPartialIndexOnVulnerabilityReportTypes, feature_category: :vulnerability_management do
+ let(:async_index) { Gitlab::Database::AsyncIndexes::PostgresAsyncIndex }
+ let(:index_name) { described_class::INDEX_NAME }
+
+ it "schedules the index" do
+ reversible_migration do |migration|
+ migration.before -> do
+ expect(async_index.where(name: index_name).count).to be(0)
+ end
+
+ migration.after -> do
+ expect(async_index.where(name: index_name).count).to be(1)
+ end
+ end
+ end
+end
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index 11d8be24e06..75ae0c970c0 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -174,39 +174,95 @@ RSpec.describe UsersController, feature_category: :user_management do
end
context 'requested in json format' do
- let(:project) { create(:project) }
+ context 'when profile_tabs_vue feature flag is turned OFF' do
+ let(:project) { create(:project) }
- before do
- project.add_developer(user)
- Gitlab::DataBuilder::Push.build_sample(project, user)
+ before do
+ project.add_developer(user)
+ Gitlab::DataBuilder::Push.build_sample(project, user)
+ stub_feature_flags(profile_tabs_vue: false)
+ sign_in(user)
+ end
- sign_in(user)
- end
+ it 'loads events' do
+ get user_activity_url user.username, format: :json
- it 'loads events' do
- get user_activity_url user.username, format: :json
+ expect(response.media_type).to eq('application/json')
+ expect(Gitlab::Json.parse(response.body)['count']).to eq(1)
+ end
- expect(response.media_type).to eq('application/json')
- expect(Gitlab::Json.parse(response.body)['count']).to eq(1)
- end
+ it 'hides events if the user cannot read cross project' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
- it 'hides events if the user cannot read cross project' do
- allow(Ability).to receive(:allowed?).and_call_original
- expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ get user_activity_url user.username, format: :json
- get user_activity_url user.username, format: :json
+ expect(response.media_type).to eq('application/json')
+ expect(Gitlab::Json.parse(response.body)['count']).to eq(0)
+ end
- expect(response.media_type).to eq('application/json')
- expect(Gitlab::Json.parse(response.body)['count']).to eq(0)
+ it 'hides events if the user has a private profile' do
+ Gitlab::DataBuilder::Push.build_sample(project, private_user)
+
+ get user_activity_url private_user.username, format: :json
+
+ expect(response.media_type).to eq('application/json')
+ expect(Gitlab::Json.parse(response.body)['count']).to eq(0)
+ end
end
- it 'hides events if the user has a private profile' do
- Gitlab::DataBuilder::Push.build_sample(project, private_user)
+ context 'when profile_tabs_vue feature flag is turned ON' do
+ let(:project) { create(:project) }
- get user_activity_url private_user.username, format: :json
+ before do
+ project.add_developer(user)
+ Gitlab::DataBuilder::Push.build_sample(project, user)
+ stub_feature_flags(profile_tabs_vue: true)
+ sign_in(user)
+ end
- expect(response.media_type).to eq('application/json')
- expect(Gitlab::Json.parse(response.body)['count']).to eq(0)
+ it 'loads events' do
+ get user_activity_url user.username, format: :json
+
+ expect(response.media_type).to eq('application/json')
+ expect(Gitlab::Json.parse(response.body).count).to eq(1)
+ end
+
+ it 'hides events if the user cannot read cross project' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ get user_activity_url user.username, format: :json
+
+ expect(response.media_type).to eq('application/json')
+ expect(Gitlab::Json.parse(response.body).count).to eq(0)
+ end
+
+ it 'hides events if the user has a private profile' do
+ Gitlab::DataBuilder::Push.build_sample(project, private_user)
+
+ get user_activity_url private_user.username, format: :json
+
+ expect(response.media_type).to eq('application/json')
+ expect(Gitlab::Json.parse(response.body).count).to eq(0)
+ end
+
+ it 'hides events if the user has a private profile' do
+ project = create(:project, :private)
+ private_event_user = create(:user, include_private_contributions: true)
+ push_data = Gitlab::DataBuilder::Push.build_sample(project, private_event_user)
+ EventCreateService.new.push(project, private_event_user, push_data)
+
+ get user_activity_url private_event_user.username, format: :json
+
+ response_body = Gitlab::Json.parse(response.body)
+ event = response_body.first
+ expect(response.media_type).to eq('application/json')
+ expect(response_body.count).to eq(1)
+ expect(event).to include('created_at', 'author', 'action')
+ expect(event['action']).to eq('private')
+ expect(event).not_to include('ref', 'commit', 'target', 'resource_parent')
+ end
end
end
end
diff --git a/spec/serializers/profile/event_entity_spec.rb b/spec/serializers/profile/event_entity_spec.rb
new file mode 100644
index 00000000000..1551fc76466
--- /dev/null
+++ b/spec/serializers/profile/event_entity_spec.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Profile::EventEntity, feature_category: :user_profile do
+ let_it_be(:group) { create(:group) } # rubocop:disable RSpec/FactoryBot/AvoidCreate
+ let_it_be(:project) { build(:project_empty_repo, group: group) }
+ let_it_be(:user) { create(:user) } # rubocop:disable RSpec/FactoryBot/AvoidCreate
+ let_it_be(:merge_request) { create(:merge_request, source_project: project, target_project: project) } # rubocop:disable RSpec/FactoryBot/AvoidCreate
+
+ let(:target_user) { user }
+ let(:event) { build(:event, :merged, author: user, project: project, target: merge_request) }
+ let(:request) { double(described_class, current_user: user, target_user: target_user) } # rubocop:disable RSpec/VerifiedDoubles
+ let(:entity) { described_class.new(event, request: request) }
+
+ subject { entity.as_json }
+
+ before do
+ group.add_maintainer(user)
+ end
+
+ it 'exposes fields', :aggregate_failures do
+ expect(subject[:created_at]).to eq(event.created_at)
+ expect(subject[:action]).to eq(event.action)
+ expect(subject[:author][:id]).to eq(target_user.id)
+ expect(subject[:author][:name]).to eq(target_user.name)
+ expect(subject[:author][:path]).to eq(target_user.username)
+ end
+
+ context 'for push events' do
+ let_it_be(:commit_from) { Gitlab::Git::BLANK_SHA }
+ let_it_be(:commit_title) { 'My commit' }
+ let(:event) { build(:push_event, project: project, author: target_user) }
+
+ it 'exposes ref fields' do
+ build(:push_event_payload, event: event, ref_count: 3)
+
+ expect(subject[:ref][:type]).to eq(event.ref_type)
+ expect(subject[:ref][:count]).to eq(event.ref_count)
+ expect(subject[:ref][:name]).to eq(event.ref_name)
+ expect(subject[:ref][:path]).to be_nil
+ end
+
+ shared_examples 'returns ref path' do
+ specify do
+ expect(subject[:ref][:path]).to be_present
+ end
+ end
+
+ context 'with tag' do
+ before do
+ allow(project.repository).to receive(:tag_exists?).and_return(true)
+ build(:push_event_payload, event: event, ref_type: :tag)
+ end
+
+ it_behaves_like 'returns ref path'
+ end
+
+ context 'with branch' do
+ before do
+ allow(project.repository).to receive(:branch_exists?).and_return(true)
+ build(:push_event_payload, event: event, ref_type: :branch)
+ end
+
+ it_behaves_like 'returns ref path'
+ end
+
+ it 'exposes commit fields' do
+ build(:push_event_payload, event: event, commit_title: commit_title, commit_from: commit_from, commit_count: 2)
+
+ compare_path = "/#{group.path}/#{project.path}/-/compare/#{commit_from}...#{event.commit_to}"
+ expect(subject[:commit][:compare_path]).to eq(compare_path)
+ expect(event.commit_id).to include(subject[:commit][:truncated_sha])
+ expect(subject[:commit][:path]).to be_present
+ expect(subject[:commit][:title]).to eq(commit_title)
+ expect(subject[:commit][:count]).to eq(2)
+ expect(commit_from).to include(subject[:commit][:from_truncated_sha])
+ expect(event.commit_to).to include(subject[:commit][:to_truncated_sha])
+ expect(subject[:commit][:create_mr_path]).to be_nil
+ end
+
+ it 'exposes create_mr_path' do
+ allow(project).to receive(:default_branch).and_return('main')
+ allow(project.repository).to receive(:branch_exists?).and_return(true)
+ build(:push_event_payload, event: event, action: :created, commit_from: commit_from, commit_count: 2)
+
+ new_mr_path = "/#{group.path}/#{project.path}/-/merge_requests/new?" \
+ "merge_request%5Bsource_branch%5D=#{event.branch_name}"
+ expect(subject[:commit][:create_mr_path]).to eq(new_mr_path)
+ end
+ end
+
+ context 'with target' do
+ let_it_be(:note) { build(:note_on_merge_request, :with_attachment, noteable: merge_request, project: project) }
+
+ context 'when target does not responds to :reference_link_text' do
+ let(:event) { build(:event, :commented, project: project, target: note, author: target_user) }
+
+ it 'exposes target fields' do
+ expect(subject[:target]).not_to include(:reference_link_text)
+ expect(subject[:target][:target_type]).to eq(note.class.to_s)
+ expect(subject[:target][:target_url]).to be_present
+ expect(subject[:target][:title]).to eq(note.title)
+ expect(subject[:target][:first_line_in_markdown]).to be_present
+ expect(subject[:target][:attachment][:url]).to eq(note.attachment.url)
+ end
+ end
+
+ context 'when target responds to :reference_link_text' do
+ it 'exposes reference_link_text' do
+ expect(subject[:target][:reference_link_text]).to eq(merge_request.reference_link_text)
+ end
+ end
+ end
+
+ context 'with resource parent' do
+ it 'exposes resource parent fields' do
+ resource_parent = event.resource_parent
+
+ expect(subject[:resource_parent][:type]).to eq('project')
+ expect(subject[:resource_parent][:full_name]).to eq(resource_parent.full_name)
+ expect(subject[:resource_parent][:full_path]).to eq(resource_parent.full_path)
+ end
+ end
+
+ context 'for private events' do
+ let(:event) { build(:event, :merged, author: target_user) }
+
+ context 'when include_private_contributions? is true' do
+ let(:target_user) { build(:user, include_private_contributions: true) }
+
+ it 'exposes only created_at, action, and author', :aggregate_failures do
+ expect(subject[:created_at]).to eq(event.created_at)
+ expect(subject[:action]).to eq('private')
+ expect(subject[:author][:id]).to eq(target_user.id)
+ expect(subject[:author][:name]).to eq(target_user.name)
+ expect(subject[:author][:path]).to eq(target_user.username)
+
+ is_expected.not_to include(:ref, :commit, :target, :resource_parent)
+ end
+ end
+
+ context 'when include_private_contributions? is false' do
+ let(:target_user) { build(:user, include_private_contributions: false) }
+
+ it { is_expected.to be_empty }
+ end
+ end
+end