diff options
Diffstat (limited to 'spec/support/shared_examples/models')
10 files changed, 301 insertions, 94 deletions
diff --git a/spec/support/shared_examples/models/application_setting_shared_examples.rb b/spec/support/shared_examples/models/application_setting_shared_examples.rb index 38f5c7be393..74ec6474e80 100644 --- a/spec/support/shared_examples/models/application_setting_shared_examples.rb +++ b/spec/support/shared_examples/models/application_setting_shared_examples.rb @@ -239,7 +239,7 @@ RSpec.shared_examples 'application settings examples' do describe '#allowed_key_types' do it 'includes all key types by default' do - expect(setting.allowed_key_types).to contain_exactly(*described_class::SUPPORTED_KEY_TYPES) + expect(setting.allowed_key_types).to contain_exactly(*Gitlab::SSHPublicKey.supported_types) end it 'excludes disabled key types' do diff --git a/spec/support/shared_examples/models/concerns/bulk_users_by_email_load_shared_examples.rb b/spec/support/shared_examples/models/concerns/bulk_users_by_email_load_shared_examples.rb new file mode 100644 index 00000000000..c3e9ff5c91a --- /dev/null +++ b/spec/support/shared_examples/models/concerns/bulk_users_by_email_load_shared_examples.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'a BulkUsersByEmailLoad model' do + describe '#users_by_emails' do + let_it_be(:user1) { create(:user, emails: [create(:email, email: 'user1@example.com')]) } + let_it_be(:user2) { create(:user, emails: [create(:email, email: 'user2@example.com')]) } + + subject(:model) { described_class.new(id: non_existing_record_id) } + + context 'when nothing is loaded' do + let(:passed_emails) { [user1.emails.first.email, user2.email] } + + it 'preforms the yielded query and supplies the data with only emails desired' do + expect(model.users_by_emails(passed_emails).keys).to contain_exactly(*passed_emails) + end + end + + context 'when store is preloaded', :request_store do + let(:passed_emails) { [user1.emails.first.email, user2.email, user1.email] } + let(:resource_data) do + { + user1.emails.first.email => instance_double('User'), + user2.email => instance_double('User') + } + end + + before do + Gitlab::SafeRequestStore["user_by_email_for_users:#{model.class.name}:#{model.id}"] = resource_data + end + + it 'passes back loaded data and does not update the items that already exist' do + users_by_emails = model.users_by_emails(passed_emails) + + expect(users_by_emails.keys).to contain_exactly(*passed_emails) + expect(users_by_emails).to include(resource_data.merge(user1.email => user1)) + end + end + end +end diff --git a/spec/support/shared_examples/models/concerns/from_set_operator_shared_examples.rb b/spec/support/shared_examples/models/concerns/from_set_operator_shared_examples.rb index 6b208c0024d..e625ba785d2 100644 --- a/spec/support/shared_examples/models/concerns/from_set_operator_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/from_set_operator_shared_examples.rb @@ -20,6 +20,12 @@ RSpec.shared_examples 'from set operator' do |sql_klass| expect(query.to_sql).to match(/FROM \(\(SELECT.+\)\n#{operator_keyword}\n\(SELECT.+\)\) users/m) end + it "returns empty set when passing empty array" do + query = model.public_send(operator_method, []) + + expect(query.to_sql).to match(/WHERE \(1=0\)/m) + end + it 'supports the use of a custom alias for the sub query' do query = model.public_send(operator_method, [model.where(id: 1), model.where(id: 2)], diff --git a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb index d6415e98289..da5c35c970a 100644 --- a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb @@ -227,9 +227,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name end context 'for confidential notes' do - before_all do - issue_note.update!(confidential: true) - end + let_it_be(:issue_note) { create(:note_on_issue, project: project, note: "issue note", confidential: true) } it 'falls back to note channel' do expect(::Slack::Messenger).to execute_with_options(channel: ['random']) diff --git a/spec/support/shared_examples/models/group_shared_examples.rb b/spec/support/shared_examples/models/group_shared_examples.rb new file mode 100644 index 00000000000..9f3359ba4ab --- /dev/null +++ b/spec/support/shared_examples/models/group_shared_examples.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'checks self and root ancestor feature flag' do + let_it_be(:root_group) { create(:group) } + let_it_be(:group) { create(:group, parent: root_group) } + let_it_be(:project) { create(:project, group: group) } + + subject { group.public_send(feature_flag_method) } + + context 'when FF is enabled for the root group' do + before do + stub_feature_flags(feature_flag => root_group) + end + + it { is_expected.to be_truthy } + end + + context 'when FF is enabled for the group' do + before do + stub_feature_flags(feature_flag => group) + end + + it { is_expected.to be_truthy } + + context 'when root_group is the actor' do + it 'is not enabled if the FF is enabled for a child' do + expect(root_group.public_send(feature_flag_method)).to be_falsey + end + end + end + + context 'when FF is disabled globally' do + before do + stub_feature_flags(feature_flag => false) + end + + it { is_expected.to be_falsey } + end + + context 'when FF is enabled globally' do + it { is_expected.to be_truthy } + end +end diff --git a/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb b/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb deleted file mode 100644 index 13ffc1b7f87..00000000000 --- a/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -# This shared example requires a `builder` and `user` variable -RSpec.shared_examples 'issuable hook data' do |kind| - let(:data) { builder.build(user: user) } - - include_examples 'project hook data' do - let(:project) { builder.issuable.project } - end - - include_examples 'deprecated repository hook data' - - context "with a #{kind}" do - it 'contains issuable data' do - expect(data[:object_kind]).to eq(kind) - expect(data[:user]).to eq(user.hook_attrs) - expect(data[:project]).to eq(builder.issuable.project.hook_attrs) - expect(data[:object_attributes]).to eq(builder.issuable.hook_attrs) - expect(data[:changes]).to eq({}) - expect(data[:repository]).to eq(builder.issuable.project.hook_attrs.slice(:name, :url, :description, :homepage)) - end - - it 'does not contain certain keys' do - expect(data).not_to have_key(:assignees) - expect(data).not_to have_key(:assignee) - end - - describe 'changes are given' do - let(:changes) do - { - cached_markdown_version: %w[foo bar], - description: ['A description', 'A cool description'], - description_html: %w[foo bar], - in_progress_merge_commit_sha: %w[foo bar], - lock_version: %w[foo bar], - merge_jid: %w[foo bar], - title: ['A title', 'Hello World'], - title_html: %w[foo bar] - } - end - - let(:data) { builder.build(user: user, changes: changes) } - - it 'populates the :changes hash' do - expect(data[:changes]).to match(hash_including({ - title: { previous: 'A title', current: 'Hello World' }, - description: { previous: 'A description', current: 'A cool description' } - })) - end - - it 'does not contain certain keys' do - expect(data[:changes]).not_to have_key('cached_markdown_version') - expect(data[:changes]).not_to have_key('description_html') - expect(data[:changes]).not_to have_key('lock_version') - expect(data[:changes]).not_to have_key('title_html') - expect(data[:changes]).not_to have_key('in_progress_merge_commit_sha') - expect(data[:changes]).not_to have_key('merge_jid') - end - end - end -end diff --git a/spec/support/shared_examples/models/issuable_link_shared_examples.rb b/spec/support/shared_examples/models/issuable_link_shared_examples.rb index ca98c2597a2..9892e66b582 100644 --- a/spec/support/shared_examples/models/issuable_link_shared_examples.rb +++ b/spec/support/shared_examples/models/issuable_link_shared_examples.rb @@ -55,6 +55,19 @@ RSpec.shared_examples 'issuable link' do end end + describe 'scopes' do + describe '.for_source_or_target' do + it 'returns only links where id is either source or target id' do + link1 = create(issuable_link_factory, source: issuable_link.source) + link2 = create(issuable_link_factory, target: issuable_link.source) + # unrelated link, should not be included in result list + create(issuable_link_factory) # rubocop: disable Rails/SaveBang + + expect(described_class.for_source_or_target(issuable_link.source_id)).to match_array([issuable_link, link1, link2]) + end + end + end + describe '.link_type' do it { is_expected.to define_enum_for(:link_type).with_values(relates_to: 0, blocks: 1) } diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb index 17026f085bb..a329a6dca91 100644 --- a/spec/support/shared_examples/models/member_shared_examples.rb +++ b/spec/support/shared_examples/models/member_shared_examples.rb @@ -88,19 +88,55 @@ RSpec.shared_examples_for "member creation" do expect(member).to be_persisted end - context 'when admin mode is enabled', :enable_admin_mode do + context 'when adding a project_bot' do + let_it_be(:project_bot) { create(:user, :project_bot) } + + before_all do + source.add_owner(user) + end + + context 'when project_bot is already a member' do + before do + source.add_developer(project_bot) + end + + it 'does not update the member' do + member = described_class.new(source, project_bot, :maintainer, current_user: user).execute + + expect(source.users.reload).to include(project_bot) + expect(member).to be_persisted + expect(member.access_level).to eq(Gitlab::Access::DEVELOPER) + expect(member.errors.full_messages).to include(/not authorized to update member/) + end + end + + context 'when project_bot is not already a member' do + it 'adds the member' do + member = described_class.new(source, project_bot, :maintainer, current_user: user).execute + + expect(source.users.reload).to include(project_bot) + expect(member).to be_persisted + end + end + end + + context 'when admin mode is enabled', :enable_admin_mode, :aggregate_failures do it 'sets members.created_by to the given admin current_user' do member = described_class.new(source, user, :maintainer, current_user: admin).execute + expect(member).to be_persisted + expect(source.users.reload).to include(user) expect(member.created_by).to eq(admin) end end context 'when admin mode is disabled' do - it 'rejects setting members.created_by to the given admin current_user' do + it 'rejects setting members.created_by to the given admin current_user', :aggregate_failures do member = described_class.new(source, user, :maintainer, current_user: admin).execute - expect(member.created_by).to be_nil + expect(member).not_to be_persisted + expect(source.users.reload).not_to include(user) + expect(member.errors.full_messages).to include(/not authorized to create member/) end end @@ -142,7 +178,7 @@ RSpec.shared_examples_for "member creation" do end context 'when called with an unknown user id' do - it 'adds the user as a member' do + it 'does not add the user as a member' do expect(source.users).not_to include(user) described_class.new(source, non_existing_record_id, :maintainer).execute @@ -410,6 +446,22 @@ RSpec.shared_examples_for "bulk member creation" do end end + it 'with the same user sent more than once by user and by email' do + members = described_class.add_users(source, [user1, user1.email], :maintainer) + + expect(members.map(&:user)).to contain_exactly(user1) + expect(members).to all(be_a(member_type)) + expect(members).to all(be_persisted) + end + + it 'with the same user sent more than once by user id and by email' do + members = described_class.add_users(source, [user1.id, user1.email], :maintainer) + + expect(members.map(&:user)).to contain_exactly(user1) + expect(members).to all(be_a(member_type)) + expect(members).to all(be_persisted) + end + context 'when a member already exists' do before do source.add_user(user1, :developer) diff --git a/spec/support/shared_examples/models/project_shared_examples.rb b/spec/support/shared_examples/models/project_shared_examples.rb new file mode 100644 index 00000000000..475ac1da04b --- /dev/null +++ b/spec/support/shared_examples/models/project_shared_examples.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'returns true if project is inactive' do + using RSpec::Parameterized::TableSyntax + + where(:storage_size, :last_activity_at, :expected_result) do + 1.megabyte | 1.month.ago | false + 1.megabyte | 3.years.ago | false + 8.megabytes | 1.month.ago | false + 8.megabytes | 3.years.ago | true + end + + with_them do + before do + stub_application_setting(inactive_projects_min_size_mb: 5) + stub_application_setting(inactive_projects_send_warning_email_after_months: 24) + + project.statistics.storage_size = storage_size + project.last_activity_at = last_activity_at + project.save! + end + + it 'returns expected result' do + expect(project.inactive?).to eq(expected_result) + end + end +end diff --git a/spec/support/shared_examples/models/wiki_shared_examples.rb b/spec/support/shared_examples/models/wiki_shared_examples.rb index b3f79d9fe6e..03e9dd65e33 100644 --- a/spec/support/shared_examples/models/wiki_shared_examples.rb +++ b/spec/support/shared_examples/models/wiki_shared_examples.rb @@ -11,6 +11,10 @@ RSpec.shared_examples 'wiki model' do subject { wiki } + it 'VALID_USER_MARKUPS contains all valid markups' do + expect(described_class::VALID_USER_MARKUPS.keys).to match_array(%i(markdown rdoc asciidoc org)) + end + it 'container class includes HasWiki' do # NOTE: This is not enforced at runtime, since we also need to support Geo::DeletedProject expect(wiki_container).to be_kind_of(HasWiki) @@ -427,45 +431,131 @@ RSpec.shared_examples 'wiki model' do end describe '#update_page' do - let(:page) { create(:wiki_page, wiki: subject, title: 'update-page') } + shared_examples 'update_page tests' do + with_them do + let!(:page) { create(:wiki_page, wiki: subject, title: original_title, format: original_format, content: 'original content') } + + let(:message) { 'updated page' } + let(:updated_content) { 'updated content' } + + def update_page + subject.update_page( + page.page, + content: updated_content, + title: updated_title, + format: updated_format, + message: message + ) + end + + specify :aggregate_failures do + expect(subject).to receive(:after_wiki_activity) + expect(update_page).to eq true + + page = subject.find_page(updated_title.presence || original_title) - def update_page - subject.update_page( - page.page, - content: 'some other content', - format: :markdown, - message: 'updated page' - ) + expect(page.raw_content).to eq(updated_content) + expect(page.path).to eq(expected_path) + expect(page.version.message).to eq(message) + expect(user.commit_email).not_to eq(user.email) + expect(commit.author_email).to eq(user.commit_email) + expect(commit.committer_email).to eq(user.commit_email) + end + end end - it 'updates the content of the page' do - update_page - page = subject.find_page('update-page') + shared_context 'common examples' do + using RSpec::Parameterized::TableSyntax + + where(:original_title, :original_format, :updated_title, :updated_format, :expected_path) do + 'test page' | :markdown | 'new test page' | :markdown | 'new-test-page.md' + 'test page' | :markdown | 'test page' | :markdown | 'test-page.md' + 'test page' | :markdown | 'test page' | :asciidoc | 'test-page.asciidoc' + + 'test page' | :markdown | 'new dir/new test page' | :markdown | 'new-dir/new-test-page.md' + 'test page' | :markdown | 'new dir/test page' | :markdown | 'new-dir/test-page.md' - expect(page.raw_content).to eq('some other content') + 'test dir/test page' | :markdown | 'new dir/new test page' | :markdown | 'new-dir/new-test-page.md' + 'test dir/test page' | :markdown | 'test dir/test page' | :markdown | 'test-dir/test-page.md' + 'test dir/test page' | :markdown | 'test dir/test page' | :asciidoc | 'test-dir/test-page.asciidoc' + + 'test dir/test page' | :markdown | 'new test page' | :markdown | 'new-test-page.md' + 'test dir/test page' | :markdown | 'test page' | :markdown | 'test-page.md' + + 'test page' | :markdown | nil | :markdown | 'test-page.md' + 'test.page' | :markdown | nil | :markdown | 'test.page.md' + end end - it 'sets the correct commit message' do - update_page - page = subject.find_page('update-page') + # There are two bugs in Gollum. THe first one is when the title and the format are updated + # at the same time https://gitlab.com/gitlab-org/gitlab/-/issues/243519. + # The second one is when the wiki page is within a dir and the `title` argument + # we pass to the update method is `nil`. Gollum will remove the dir and move the page. + # + # We can include this context into the former once it is fixed + # or when Gollum is removed since the Gitaly approach already fixes it. + shared_context 'extended examples' do + using RSpec::Parameterized::TableSyntax + + where(:original_title, :original_format, :updated_title, :updated_format, :expected_path) do + 'test page' | :markdown | 'new test page' | :asciidoc | 'new-test-page.asciidoc' + 'test page' | :markdown | 'new dir/new test page' | :asciidoc | 'new-dir/new-test-page.asciidoc' + 'test dir/test page' | :markdown | 'new dir/new test page' | :asciidoc | 'new-dir/new-test-page.asciidoc' + 'test dir/test page' | :markdown | 'new test page' | :asciidoc | 'new-test-page.asciidoc' + 'test page' | :markdown | nil | :asciidoc | 'test-page.asciidoc' + 'test dir/test page' | :markdown | nil | :asciidoc | 'test-dir/test-page.asciidoc' + 'test dir/test page' | :markdown | nil | :markdown | 'test-dir/test-page.md' + 'test page' | :markdown | '' | :markdown | 'test-page.md' + 'test.page' | :markdown | '' | :markdown | 'test.page.md' + end + end - expect(page.version.message).to eq('updated page') + it_behaves_like 'update_page tests' do + include_context 'common examples' + include_context 'extended examples' end - it 'sets the correct commit email' do - update_page + context 'when format is invalid' do + let!(:page) { create(:wiki_page, wiki: subject, title: 'test page') } - expect(user.commit_email).not_to eq(user.email) - expect(commit.author_email).to eq(user.commit_email) - expect(commit.committer_email).to eq(user.commit_email) + it 'returns false and sets error message' do + expect(subject.update_page(page.page, content: 'new content', format: :foobar)).to eq false + expect(subject.error_message).to match(/Invalid format selected/) + end end - it 'runs after_wiki_activity callbacks' do - page + context 'when format is not allowed' do + let!(:page) { create(:wiki_page, wiki: subject, title: 'test page') } - expect(subject).to receive(:after_wiki_activity) + it 'returns false and sets error message' do + expect(subject.update_page(page.page, content: 'new content', format: :creole)).to eq false + expect(subject.error_message).to match(/Invalid format selected/) + end + end + + context 'when page path does not have a default extension' do + let!(:page) { create(:wiki_page, wiki: subject, title: 'test page') } + + context 'when format is not different' do + it 'does not change the default extension' do + path = 'test-page.markdown' + page.page.instance_variable_set(:@path, path) - update_page + expect(subject.repository).to receive(:update_file).with(user, path, anything, anything) + + subject.update_page(page.page, content: 'new content', format: :markdown) + end + end + end + + context 'when feature flag :gitaly_replace_wiki_update_page is disabled' do + before do + stub_feature_flags(gitaly_replace_wiki_update_page: false) + end + + it_behaves_like 'update_page tests' do + include_context 'common examples' + end end end |