diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-01-26 18:08:58 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-01-26 18:08:58 +0300 |
commit | ff89c3c372cd3b317915fb21940f9c8c065d94c0 (patch) | |
tree | ce6674c2d661f25ffc156679846b0d6b48247e8e /spec | |
parent | 0121231095b3a50a18aaf4eb4a7058b55fe62fc1 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r-- | spec/controllers/groups_controller_spec.rb | 60 | ||||
-rw-r--r-- | spec/lib/gitlab/changelog/committer_spec.rb | 90 | ||||
-rw-r--r-- | spec/lib/gitlab/changelog/config_spec.rb | 96 | ||||
-rw-r--r-- | spec/lib/gitlab/changelog/generator_spec.rb | 164 | ||||
-rw-r--r-- | spec/lib/gitlab/changelog/release_spec.rb | 107 | ||||
-rw-r--r-- | spec/lib/gitlab/changelog/template/compiler_spec.rb | 129 | ||||
-rw-r--r-- | spec/lib/gitlab/suggestions/commit_message_spec.rb | 11 | ||||
-rw-r--r-- | spec/models/release_spec.rb | 57 | ||||
-rw-r--r-- | spec/requests/api/graphql/mutations/releases/create_spec.rb | 8 | ||||
-rw-r--r-- | spec/requests/api/graphql/project/release_spec.rb | 4 | ||||
-rw-r--r-- | spec/requests/api/suggestions_spec.rb | 27 | ||||
-rw-r--r-- | spec/services/suggestions/apply_service_spec.rb | 14 | ||||
-rw-r--r-- | spec/support/helpers/snowplow_helpers.rb | 10 | ||||
-rw-r--r-- | spec/support/snowplow.rb | 1 | ||||
-rw-r--r-- | spec/validators/nested_attributes_duplicates_validator_spec.rb (renamed from spec/validators/variable_duplicates_validator_spec.rb) | 50 |
15 files changed, 752 insertions, 76 deletions
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 939c36a98b2..9e5f68820d9 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -306,66 +306,6 @@ RSpec.describe GroupsController, factory_default: :keep do end end end - - describe 'tracking group creation for onboarding issues experiment' do - before do - sign_in(user) - end - - subject(:create_namespace) { post :create, params: { group: { name: 'new_group', path: 'new_group' } } } - - context 'experiment disabled' do - before do - stub_experiment(onboarding_issues: false) - end - - it 'does not track anything', :snowplow do - create_namespace - - expect_no_snowplow_event - end - end - - context 'experiment enabled' do - before do - stub_experiment(onboarding_issues: true) - end - - context 'and the user is part of the control group' do - before do - stub_experiment_for_subject(onboarding_issues: false) - end - - it 'tracks the event with the "created_namespace" action with the "control_group" property', :snowplow do - create_namespace - - expect_snowplow_event( - category: 'Growth::Conversion::Experiment::OnboardingIssues', - action: 'created_namespace', - label: anything, - property: 'control_group' - ) - end - end - - context 'and the user is part of the experimental group' do - before do - stub_experiment_for_subject(onboarding_issues: true) - end - - it 'tracks the event with the "created_namespace" action with the "experimental_group" property', :snowplow do - create_namespace - - expect_snowplow_event( - category: 'Growth::Conversion::Experiment::OnboardingIssues', - action: 'created_namespace', - label: anything, - property: 'experimental_group' - ) - end - end - end - end end describe 'GET #index' do diff --git a/spec/lib/gitlab/changelog/committer_spec.rb b/spec/lib/gitlab/changelog/committer_spec.rb new file mode 100644 index 00000000000..71a80264f29 --- /dev/null +++ b/spec/lib/gitlab/changelog/committer_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Changelog::Committer do + let(:project) { create(:project, :repository) } + let(:user) { project.creator } + let(:committer) { described_class.new(project, user) } + let(:config) { Gitlab::Changelog::Config.new(project) } + + describe '#commit' do + context "when the release isn't in the changelog" do + it 'commits the changes' do + release = Gitlab::Changelog::Release + .new(version: '1.0.0', date: Time.utc(2020, 1, 1), config: config) + + committer.commit( + release: release, + file: 'CHANGELOG.md', + branch: 'master', + message: 'Test commit' + ) + + content = project.repository.blob_at('master', 'CHANGELOG.md').data + + expect(content).to eq(<<~MARKDOWN) + ## 1.0.0 (2020-01-01) + + No changes. + MARKDOWN + end + end + + context 'when the release is already in the changelog' do + it "doesn't commit the changes" do + release = Gitlab::Changelog::Release + .new(version: '1.0.0', date: Time.utc(2020, 1, 1), config: config) + + 2.times do + committer.commit( + release: release, + file: 'CHANGELOG.md', + branch: 'master', + message: 'Test commit' + ) + end + + content = project.repository.blob_at('master', 'CHANGELOG.md').data + + expect(content).to eq(<<~MARKDOWN) + ## 1.0.0 (2020-01-01) + + No changes. + MARKDOWN + end + end + + context 'when committing the changes fails' do + it 'retries the operation' do + release = Gitlab::Changelog::Release + .new(version: '1.0.0', date: Time.utc(2020, 1, 1), config: config) + + service = instance_spy(Files::MultiService) + errored = false + + allow(Files::MultiService) + .to receive(:new) + .and_return(service) + + allow(service).to receive(:execute) do + if errored + { status: :success } + else + errored = true + { status: :error } + end + end + + expect do + committer.commit( + release: release, + file: 'CHANGELOG.md', + branch: 'master', + message: 'Test commit' + ) + end.not_to raise_error + end + end + end +end diff --git a/spec/lib/gitlab/changelog/config_spec.rb b/spec/lib/gitlab/changelog/config_spec.rb new file mode 100644 index 00000000000..adf82fa3ac2 --- /dev/null +++ b/spec/lib/gitlab/changelog/config_spec.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Changelog::Config do + let(:project) { build_stubbed(:project) } + + describe '.from_git' do + it 'retrieves the configuration from Git' do + allow(project.repository) + .to receive(:changelog_config) + .and_return("---\ndate_format: '%Y'") + + expect(described_class) + .to receive(:from_hash) + .with(project, 'date_format' => '%Y') + + described_class.from_git(project) + end + + it 'returns the default configuration when no YAML file exists in Git' do + allow(project.repository) + .to receive(:changelog_config) + .and_return(nil) + + expect(described_class) + .to receive(:new) + .with(project) + + described_class.from_git(project) + end + end + + describe '.from_hash' do + it 'sets the configuration according to a Hash' do + config = described_class.from_hash( + project, + 'date_format' => 'foo', + 'template' => 'bar', + 'categories' => { 'foo' => 'bar' } + ) + + expect(config.date_format).to eq('foo') + expect(config.template).to be_instance_of(Gitlab::Changelog::Template::Template) + expect(config.categories).to eq({ 'foo' => 'bar' }) + end + + it 'raises ConfigError when the categories are not a Hash' do + expect { described_class.from_hash(project, 'categories' => 10) } + .to raise_error(described_class::ConfigError) + end + end + + describe '#contributor?' do + it 'returns true if a user is a contributor' do + user = build_stubbed(:author) + + allow(project.team).to receive(:contributor?).with(user).and_return(true) + + expect(described_class.new(project).contributor?(user)).to eq(true) + end + + it "returns true if a user isn't a contributor" do + user = build_stubbed(:author) + + allow(project.team).to receive(:contributor?).with(user).and_return(false) + + expect(described_class.new(project).contributor?(user)).to eq(false) + end + end + + describe '#category' do + it 'returns the name of a category' do + config = described_class.new(project) + + config.categories['foo'] = 'Foo' + + expect(config.category('foo')).to eq('Foo') + end + + it 'returns the raw category name when no alternative name is configured' do + config = described_class.new(project) + + expect(config.category('bla')).to eq('bla') + end + end + + describe '#format_date' do + it 'formats a date according to the configured date format' do + config = described_class.new(project) + time = Time.utc(2021, 1, 5) + + expect(config.format_date(time)).to eq('2021-01-05') + end + end +end diff --git a/spec/lib/gitlab/changelog/generator_spec.rb b/spec/lib/gitlab/changelog/generator_spec.rb new file mode 100644 index 00000000000..bc4a7c5dd6b --- /dev/null +++ b/spec/lib/gitlab/changelog/generator_spec.rb @@ -0,0 +1,164 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Changelog::Generator do + describe '#add' do + let(:project) { build_stubbed(:project) } + let(:author) { build_stubbed(:user) } + let(:commit) { build_stubbed(:commit) } + let(:config) { Gitlab::Changelog::Config.new(project) } + + it 'generates the Markdown for the first release' do + release = Gitlab::Changelog::Release.new( + version: '1.0.0', + date: Time.utc(2021, 1, 5), + config: config + ) + + release.add_entry( + title: 'This is a new change', + commit: commit, + category: 'added', + author: author + ) + + gen = described_class.new('') + + expect(gen.add(release)).to eq(<<~MARKDOWN) + ## 1.0.0 (2021-01-05) + + ### added (1 change) + + - [This is a new change](#{commit.to_reference(full: true)}) + MARKDOWN + end + + it 'generates the Markdown for a newer release' do + release = Gitlab::Changelog::Release.new( + version: '2.0.0', + date: Time.utc(2021, 1, 5), + config: config + ) + + release.add_entry( + title: 'This is a new change', + commit: commit, + category: 'added', + author: author + ) + + gen = described_class.new(<<~MARKDOWN) + This is a changelog file. + + ## 1.0.0 + + This is the changelog for version 1.0.0. + MARKDOWN + + expect(gen.add(release)).to eq(<<~MARKDOWN) + This is a changelog file. + + ## 2.0.0 (2021-01-05) + + ### added (1 change) + + - [This is a new change](#{commit.to_reference(full: true)}) + + ## 1.0.0 + + This is the changelog for version 1.0.0. + MARKDOWN + end + + it 'generates the Markdown for a patch release' do + release = Gitlab::Changelog::Release.new( + version: '1.1.0', + date: Time.utc(2021, 1, 5), + config: config + ) + + release.add_entry( + title: 'This is a new change', + commit: commit, + category: 'added', + author: author + ) + + gen = described_class.new(<<~MARKDOWN) + This is a changelog file. + + ## 2.0.0 + + This is another release. + + ## 1.0.0 + + This is the changelog for version 1.0.0. + MARKDOWN + + expect(gen.add(release)).to eq(<<~MARKDOWN) + This is a changelog file. + + ## 2.0.0 + + This is another release. + + ## 1.1.0 (2021-01-05) + + ### added (1 change) + + - [This is a new change](#{commit.to_reference(full: true)}) + + ## 1.0.0 + + This is the changelog for version 1.0.0. + MARKDOWN + end + + it 'generates the Markdown for an old release' do + release = Gitlab::Changelog::Release.new( + version: '0.5.0', + date: Time.utc(2021, 1, 5), + config: config + ) + + release.add_entry( + title: 'This is a new change', + commit: commit, + category: 'added', + author: author + ) + + gen = described_class.new(<<~MARKDOWN) + This is a changelog file. + + ## 2.0.0 + + This is another release. + + ## 1.0.0 + + This is the changelog for version 1.0.0. + MARKDOWN + + expect(gen.add(release)).to eq(<<~MARKDOWN) + This is a changelog file. + + ## 2.0.0 + + This is another release. + + ## 1.0.0 + + This is the changelog for version 1.0.0. + + ## 0.5.0 (2021-01-05) + + ### added (1 change) + + - [This is a new change](#{commit.to_reference(full: true)}) + MARKDOWN + end + end +end diff --git a/spec/lib/gitlab/changelog/release_spec.rb b/spec/lib/gitlab/changelog/release_spec.rb new file mode 100644 index 00000000000..50a23d23299 --- /dev/null +++ b/spec/lib/gitlab/changelog/release_spec.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Changelog::Release do + describe '#to_markdown' do + let(:config) { Gitlab::Changelog::Config.new(build_stubbed(:project)) } + let(:commit) { build_stubbed(:commit) } + let(:author) { build_stubbed(:user) } + let(:mr) { build_stubbed(:merge_request) } + let(:release) do + described_class + .new(version: '1.0.0', date: Time.utc(2021, 1, 5), config: config) + end + + context 'when there are no entries' do + it 'includes a notice about the lack of entries' do + expect(release.to_markdown).to eq(<<~OUT) + ## 1.0.0 (2021-01-05) + + No changes. + + OUT + end + end + + context 'when all data is present' do + it 'includes all data' do + allow(config).to receive(:contributor?).with(author).and_return(true) + + release.add_entry( + title: 'Entry title', + commit: commit, + category: 'fixed', + author: author, + merge_request: mr + ) + + expect(release.to_markdown).to eq(<<~OUT) + ## 1.0.0 (2021-01-05) + + ### fixed (1 change) + + - [Entry title](#{commit.to_reference(full: true)}) \ + by #{author.to_reference(full: true)} \ + ([merge request](#{mr.to_reference(full: true)})) + + OUT + end + end + + context 'when no merge request is present' do + it "doesn't include a merge request link" do + allow(config).to receive(:contributor?).with(author).and_return(true) + + release.add_entry( + title: 'Entry title', + commit: commit, + category: 'fixed', + author: author + ) + + expect(release.to_markdown).to eq(<<~OUT) + ## 1.0.0 (2021-01-05) + + ### fixed (1 change) + + - [Entry title](#{commit.to_reference(full: true)}) \ + by #{author.to_reference(full: true)} + + OUT + end + end + + context 'when the author is not a contributor' do + it "doesn't include the author" do + allow(config).to receive(:contributor?).with(author).and_return(false) + + release.add_entry( + title: 'Entry title', + commit: commit, + category: 'fixed', + author: author + ) + + expect(release.to_markdown).to eq(<<~OUT) + ## 1.0.0 (2021-01-05) + + ### fixed (1 change) + + - [Entry title](#{commit.to_reference(full: true)}) + + OUT + end + end + end + + describe '#header_start_position' do + it 'returns a regular expression for finding the start of a release section' do + config = Gitlab::Changelog::Config.new(build_stubbed(:project)) + release = described_class + .new(version: '1.0.0', date: Time.utc(2021, 1, 5), config: config) + + expect(release.header_start_pattern).to eq(/^##\s*1\.0\.0/) + end + end +end diff --git a/spec/lib/gitlab/changelog/template/compiler_spec.rb b/spec/lib/gitlab/changelog/template/compiler_spec.rb new file mode 100644 index 00000000000..d940fbaec89 --- /dev/null +++ b/spec/lib/gitlab/changelog/template/compiler_spec.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Changelog::Template::Compiler do + def compile(template, data = {}) + Gitlab::Changelog::Template::Compiler.new.compile(template).render(data) + end + + describe '#compile' do + it 'compiles an empty template' do + expect(compile('')).to eq('') + end + + it 'compiles a template with an undefined variable' do + expect(compile('{{number}}')).to eq('') + end + + it 'compiles a template with a defined variable' do + expect(compile('{{number}}', 'number' => 42)).to eq('42') + end + + it 'compiles a template with the special "it" variable' do + expect(compile('{{it}}', 'values' => 10)).to eq({ 'values' => 10 }.to_s) + end + + it 'compiles a template containing an if statement' do + expect(compile('{% if foo %}yes{% end %}', 'foo' => true)).to eq('yes') + end + + it 'compiles a template containing an if/else statement' do + expect(compile('{% if foo %}yes{% else %}no{% end %}', 'foo' => false)) + .to eq('no') + end + + it 'compiles a template that iterates over an Array' do + expect(compile('{% each numbers %}{{it}}{% end %}', 'numbers' => [1, 2, 3])) + .to eq('123') + end + + it 'compiles a template that iterates over a Hash' do + output = compile( + '{% each pairs %}{{0}}={{1}}{% end %}', + 'pairs' => { 'key' => 'value' } + ) + + expect(output).to eq('key=value') + end + + it 'compiles a template that iterates over a Hash of Arrays' do + output = compile( + '{% each values %}{{key}}{% end %}', + 'values' => [{ 'key' => 'value' }] + ) + + expect(output).to eq('value') + end + + it 'compiles a template with a variable path' do + output = compile('{{foo.bar}}', 'foo' => { 'bar' => 10 }) + + expect(output).to eq('10') + end + + it 'compiles a template with a variable path that uses an Array index' do + output = compile('{{foo.values.1}}', 'foo' => { 'values' => [10, 20] }) + + expect(output).to eq('20') + end + + it 'compiles a template with a variable path that uses a Hash and a numeric index' do + output = compile('{{foo.1}}', 'foo' => { 'key' => 'value' }) + + expect(output).to eq('') + end + + it 'compiles a template with a variable path that uses an Array and a String based index' do + output = compile('{{foo.numbers.bla}}', 'foo' => { 'numbers' => [10, 20] }) + + expect(output).to eq('') + end + + it 'ignores ERB tags provided by the user' do + input = '<% exit %> <%= exit %> <%= foo -%>' + + expect(compile(input)).to eq(input) + end + + it 'removes newlines introduced by end statements on their own lines' do + output = compile(<<~TPL, 'foo' => true) + {% if foo %} + foo + {% end %} + TPL + + expect(output).to eq("foo\n") + end + + it 'supports escaping of trailing newlines' do + output = compile(<<~TPL) + foo \ + bar\ + baz + TPL + + expect(output).to eq("foo barbaz\n") + end + + # rubocop: disable Lint/InterpolationCheck + it 'ignores embedded Ruby expressions' do + input = '#{exit}' + + expect(compile(input)).to eq(input) + end + # rubocop: enable Lint/InterpolationCheck + + it 'ignores ERB tags inside variable tags' do + input = '{{<%= exit %>}}' + + expect(compile(input)).to eq(input) + end + + it 'ignores malicious code that tries to escape a variable' do + input = "{{') ::Kernel.exit # '}}" + + expect(compile(input)).to eq(input) + end + end +end diff --git a/spec/lib/gitlab/suggestions/commit_message_spec.rb b/spec/lib/gitlab/suggestions/commit_message_spec.rb index 1411f64f8b7..965960f0c3e 100644 --- a/spec/lib/gitlab/suggestions/commit_message_spec.rb +++ b/spec/lib/gitlab/suggestions/commit_message_spec.rb @@ -72,6 +72,17 @@ RSpec.describe Gitlab::Suggestions::CommitMessage do end end + context 'when a custom commit message is specified' do + let(:message) { "i'm a project message. a user's custom message takes precedence over me :(" } + let(:custom_message) { "hello there! i'm a cool custom commit message." } + + it 'shows the custom commit message' do + expect(Gitlab::Suggestions::CommitMessage + .new(user, suggestion_set, custom_message) + .message).to eq(custom_message) + end + end + context 'is specified and includes all placeholders' do let(:message) do '*** %{branch_name} %{files_count} %{file_paths} %{project_name} %{project_path} %{user_full_name} %{username} %{suggestions_count} ***' diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb index b436c2e1088..209ac471210 100644 --- a/spec/models/release_spec.rb +++ b/spec/models/release_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Release do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :public, :repository) } - let_it_be(:release) { create(:release, project: project, author: user) } + let(:release) { create(:release, project: project, author: user) } it { expect(release).to be_valid } @@ -89,6 +89,61 @@ RSpec.describe Release do end end + describe '#update' do + subject { release.update(params) } + + context 'when links do not exist' do + context 'when params are specified for creation' do + let(:params) do + { links_attributes: [{ name: 'test', url: 'https://www.google.com/' }] } + end + + it 'creates a link successfuly' do + is_expected.to eq(true) + + expect(release.links.count).to eq(1) + expect(release.links.first.name).to eq('test') + expect(release.links.first.url).to eq('https://www.google.com/') + end + end + end + + context 'when a link exists' do + let!(:link1) { create(:release_link, release: release, name: 'test1', url: 'https://www.google1.com/') } + let!(:link2) { create(:release_link, release: release, name: 'test2', url: 'https://www.google2.com/') } + + before do + release.reload + end + + context 'when params are specified for update' do + let(:params) do + { links_attributes: [{ id: link1.id, name: 'new' }] } + end + + it 'updates the link successfully' do + is_expected.to eq(true) + + expect(release.links.count).to eq(2) + expect(release.links.first.name).to eq('new') + end + end + + context 'when params are specified for deletion' do + let(:params) do + { links_attributes: [{ id: link1.id, _destroy: true }] } + end + + it 'removes the link successfuly' do + is_expected.to eq(true) + + expect(release.links.count).to eq(1) + expect(release.links.first.name).to eq(link2.name) + end + end + end + end + describe '#sources' do subject { release.sources } diff --git a/spec/requests/api/graphql/mutations/releases/create_spec.rb b/spec/requests/api/graphql/mutations/releases/create_spec.rb index 79bdcec7944..a4918cd560c 100644 --- a/spec/requests/api/graphql/mutations/releases/create_spec.rb +++ b/spec/requests/api/graphql/mutations/releases/create_spec.rb @@ -309,10 +309,7 @@ RSpec.describe 'Creation of a new release' do let(:asset_link_2) { { name: 'My link', url: 'https://example.com/2' } } let(:assets) { { links: [asset_link_1, asset_link_2] } } - # Right now the raw Postgres error message is sent to the user as the validation message. - # We should catch this validation error and return a nicer message: - # https://gitlab.com/gitlab-org/gitlab/-/issues/277087 - it_behaves_like 'errors-as-data with message', 'PG::UniqueViolation' + it_behaves_like 'errors-as-data with message', %r{Validation failed: Links have duplicate values \(My link\)} end context 'when two release assets share the same URL' do @@ -320,8 +317,7 @@ RSpec.describe 'Creation of a new release' do let(:asset_link_2) { { name: 'My second link', url: 'https://example.com' } } let(:assets) { { links: [asset_link_1, asset_link_2] } } - # Same note as above about the ugly error message - it_behaves_like 'errors-as-data with message', 'PG::UniqueViolation' + it_behaves_like 'errors-as-data with message', %r{Validation failed: Links have duplicate values \(https://example.com\)} end context 'when the provided tag name is HEAD' do diff --git a/spec/requests/api/graphql/project/release_spec.rb b/spec/requests/api/graphql/project/release_spec.rb index ccc2825da25..963b594a202 100644 --- a/spec/requests/api/graphql/project/release_spec.rb +++ b/spec/requests/api/graphql/project/release_spec.rb @@ -283,7 +283,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do let_it_be(:project) { create(:project, :repository, :private) } let_it_be(:milestone_1) { create(:milestone, project: project) } let_it_be(:milestone_2) { create(:milestone, project: project) } - let_it_be(:release) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2], released_at: released_at) } + let_it_be(:release, reload: true) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2], released_at: released_at) } let_it_be(:release_link_1) { create(:release_link, release: release) } let_it_be(:release_link_2) { create(:release_link, release: release, filepath: link_filepath) } @@ -324,7 +324,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do let_it_be(:project) { create(:project, :repository, :public) } let_it_be(:milestone_1) { create(:milestone, project: project) } let_it_be(:milestone_2) { create(:milestone, project: project) } - let_it_be(:release) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2], released_at: released_at) } + let_it_be(:release, reload: true) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2], released_at: released_at) } let_it_be(:release_link_1) { create(:release_link, release: release) } let_it_be(:release_link_2) { create(:release_link, release: release, filepath: link_filepath) } diff --git a/spec/requests/api/suggestions_spec.rb b/spec/requests/api/suggestions_spec.rb index 78a2688ac5e..7f53d379af5 100644 --- a/spec/requests/api/suggestions_spec.rb +++ b/spec/requests/api/suggestions_spec.rb @@ -65,6 +65,19 @@ RSpec.describe API::Suggestions do end end + context 'when a custom commit message is included' do + it 'renders an ok response and returns json content' do + project.add_maintainer(user) + + message = "cool custom commit message!" + + put api(url, user), params: { commit_message: message } + + expect(response).to have_gitlab_http_status(:ok) + expect(project.repository.commit.message).to eq(message) + end + end + context 'when not able to apply patch' do let(:url) { "/suggestions/#{unappliable_suggestion.id}/apply" } @@ -113,9 +126,11 @@ RSpec.describe API::Suggestions do let(:url) { "/suggestions/batch_apply" } context 'when successfully applies multiple patches as a batch' do - it 'renders an ok response and returns json content' do + before do project.add_maintainer(user) + end + it 'renders an ok response and returns json content' do put api(url, user), params: { ids: [suggestion.id, suggestion2.id] } expect(response).to have_gitlab_http_status(:ok) @@ -123,6 +138,16 @@ RSpec.describe API::Suggestions do 'appliable', 'applied', 'from_content', 'to_content')) end + + it 'provides a custom commit message' do + message = "cool custom commit message!" + + put api(url, user), params: { ids: [suggestion.id, suggestion2.id], + commit_message: message } + + expect(response).to have_gitlab_http_status(:ok) + expect(project.repository.commit.message).to eq(message) + end end context 'when not able to apply one or more of the patches' do diff --git a/spec/services/suggestions/apply_service_spec.rb b/spec/services/suggestions/apply_service_spec.rb index e31dd7031eb..77d0e892725 100644 --- a/spec/services/suggestions/apply_service_spec.rb +++ b/spec/services/suggestions/apply_service_spec.rb @@ -32,8 +32,8 @@ RSpec.describe Suggestions::ApplyService do create(:suggestion, :content_from_repo, suggestion_args) end - def apply(suggestions) - result = apply_service.new(user, *suggestions).execute + def apply(suggestions, custom_message = nil) + result = apply_service.new(user, *suggestions, message: custom_message).execute suggestions.map { |suggestion| suggestion.reload } @@ -111,6 +111,16 @@ RSpec.describe Suggestions::ApplyService do end end end + + context 'with a user suggested commit message' do + let(:message) { "i'm a custom commit message!" } + + it "uses the user's commit message" do + apply(suggestions, message) + + expect(project.repository.commit.message).to(eq(message)) + end + end end subject(:apply_service) { described_class } diff --git a/spec/support/helpers/snowplow_helpers.rb b/spec/support/helpers/snowplow_helpers.rb index 70a4eadd8de..a6de8dabdac 100644 --- a/spec/support/helpers/snowplow_helpers.rb +++ b/spec/support/helpers/snowplow_helpers.rb @@ -46,7 +46,7 @@ module SnowplowHelpers # } # ] # ) - def expect_snowplow_event(category:, action:, context: nil, **kwargs) + def expect_snowplow_event(category:, action:, context: nil, standard_context: nil, **kwargs) if context kwargs[:context] = [] context.each do |c| @@ -56,6 +56,14 @@ module SnowplowHelpers end end + if standard_context + expect(Gitlab::Tracking::StandardContext) + .to have_received(:new) + .with(**standard_context) + + kwargs[:standard_context] = an_instance_of(Gitlab::Tracking::StandardContext) + end + expect(Gitlab::Tracking).to have_received(:event) # rubocop:disable RSpec/ExpectGitlabTracking .with(category, action, **kwargs).at_least(:once) end diff --git a/spec/support/snowplow.rb b/spec/support/snowplow.rb index 0d6102f1705..91325d867d4 100644 --- a/spec/support/snowplow.rb +++ b/spec/support/snowplow.rb @@ -18,6 +18,7 @@ RSpec.configure do |config| stub_application_setting(snowplow_enabled: true) allow(SnowplowTracker::SelfDescribingJson).to receive(:new).and_call_original + allow(Gitlab::Tracking::StandardContext).to receive(:new).and_call_original allow(Gitlab::Tracking).to receive(:event).and_call_original # rubocop:disable RSpec/ExpectGitlabTracking end diff --git a/spec/validators/variable_duplicates_validator_spec.rb b/spec/validators/nested_attributes_duplicates_validator_spec.rb index acc47ff225f..f59e422e5d4 100644 --- a/spec/validators/variable_duplicates_validator_spec.rb +++ b/spec/validators/nested_attributes_duplicates_validator_spec.rb @@ -2,13 +2,16 @@ require 'spec_helper' -RSpec.describe VariableDuplicatesValidator do - let(:validator) { described_class.new(attributes: [:variables], **options) } +RSpec.describe NestedAttributesDuplicatesValidator do + let(:validator) { described_class.new(attributes: [attribute], **options) } describe '#validate_each' do let(:project) { build(:project) } + let(:record) { project } + let(:attribute) { :variables } + let(:value) { project.variables } - subject { validator.validate_each(project, :variables, project.variables) } + subject { validator.validate_each(record, attribute, value) } context 'with no scope' do let(:options) { {} } @@ -65,5 +68,46 @@ RSpec.describe VariableDuplicatesValidator do end end end + + context 'with a child attribute' do + let(:release) { build(:release) } + let(:first_link) { build(:release_link, name: 'test1', url: 'https://www.google1.com', release: release) } + let(:second_link) { build(:release_link, name: 'test2', url: 'https://www.google2.com', release: release) } + let(:record) { release } + let(:attribute) { :links } + let(:value) { release.links } + let(:options) { { scope: :release, child_attributes: %i[name url] } } + + before do + release.links << first_link + release.links << second_link + end + + it 'does not have any errors' do + subject + + expect(release.errors.empty?).to be true + end + + context 'when name is duplicated' do + let(:second_link) { build(:release_link, name: 'test1', release: release) } + + it 'has a duplicate error' do + subject + + expect(release.errors).to have_key(attribute) + end + end + + context 'when url is duplicated' do + let(:second_link) { build(:release_link, url: 'https://www.google1.com', release: release) } + + it 'has a duplicate error' do + subject + + expect(release.errors).to have_key(attribute) + end + end + end end end |