diff options
Diffstat (limited to 'spec')
34 files changed, 1099 insertions, 283 deletions
diff --git a/spec/controllers/projects/autocomplete_sources_controller_spec.rb b/spec/controllers/projects/autocomplete_sources_controller_spec.rb new file mode 100644 index 00000000000..4bc72042710 --- /dev/null +++ b/spec/controllers/projects/autocomplete_sources_controller_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::AutocompleteSourcesController do + set(:group) { create(:group) } + set(:project) { create(:project, namespace: group) } + set(:issue) { create(:issue, project: project) } + set(:user) { create(:user) } + + describe 'GET members' do + before do + group.add_owner(user) + sign_in(user) + end + + it 'returns an array of member object' do + get :members, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } + + all = json_response.find {|member| member["username"] == 'all'} + the_group = json_response.find {|member| member["username"] == group.full_path} + the_user = json_response.find {|member| member["username"] == user.username} + + expect(all.symbolize_keys).to include(username: 'all', + name: 'All Project and Group Members', + count: 1) + + expect(the_group.symbolize_keys).to include(type: group.class.name, + name: group.full_name, + avatar_url: group.avatar_url, + count: 1) + + expect(the_user.symbolize_keys).to include(type: user.class.name, + name: user.name, + avatar_url: user.avatar_url) + end + end +end diff --git a/spec/factories/import_state.rb b/spec/factories/import_states.rb index d6de26dccbc..d6de26dccbc 100644 --- a/spec/factories/import_state.rb +++ b/spec/factories/import_states.rb diff --git a/spec/factories/personal_access_tokens.rb b/spec/factories/personal_access_tokens.rb index 1b12f84d7b8..e7fd22a96b2 100644 --- a/spec/factories/personal_access_tokens.rb +++ b/spec/factories/personal_access_tokens.rb @@ -1,13 +1,14 @@ FactoryBot.define do factory :personal_access_token do user - token { SecureRandom.hex(50) } sequence(:name) { |n| "PAT #{n}" } revoked false expires_at { 5.days.from_now } scopes ['api'] impersonation false + after(:build) { |personal_access_token| personal_access_token.ensure_token } + trait :impersonation do impersonation true end @@ -21,7 +22,7 @@ FactoryBot.define do end trait :invalid do - token nil + token_digest nil end end end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index a47bd7cafca..1d2b724a5e5 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -73,11 +73,16 @@ FactoryBot.define do end after(:create) do |user, evaluator| - user.identities << create( - :identity, + identity_attrs = { provider: evaluator.provider, extern_uid: evaluator.extern_uid - ) + } + + if evaluator.respond_to?(:saml_provider) + identity_attrs[:saml_provider] = evaluator.saml_provider + end + + user.identities << create(:identity, identity_attrs) end end diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index 6c4b04ab76b..9d1c1e3acc7 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -114,7 +114,16 @@ describe 'Dashboard Projects' do end end - context 'when on Starred projects tab' do + context 'when on Starred projects tab', :js do + it 'shows the empty state when there are no starred projects' do + visit(starred_dashboard_projects_path) + + element = page.find('.row.empty-state') + + expect(element).to have_content("You don't have starred projects yet.") + expect(element.find('.svg-content img')['src']).to have_content('illustrations/starred_empty') + end + it 'shows only starred projects' do user.toggle_star(project2) diff --git a/spec/lib/bitbucket_server/client_spec.rb b/spec/lib/bitbucket_server/client_spec.rb index b021e69800a..4f0d57ca8a6 100644 --- a/spec/lib/bitbucket_server/client_spec.rb +++ b/spec/lib/bitbucket_server/client_spec.rb @@ -64,7 +64,7 @@ describe BitbucketServer::Client do let(:url) { "#{base_uri}rest/api/1.0/projects/SOME-PROJECT/repos/my-repo/branches" } it 'requests Bitbucket to create a branch' do - stub_request(:post, url).to_return(status: 204, headers: headers, body: '{}') + stub_request(:post, url).to_return(status: 204, headers: headers, body: nil) subject.create_branch(project, repo_slug, branch, sha) @@ -78,7 +78,7 @@ describe BitbucketServer::Client do let(:url) { "#{base_uri}rest/branch-utils/1.0/projects/SOME-PROJECT/repos/my-repo/branches" } it 'requests Bitbucket to create a branch' do - stub_request(:delete, url).to_return(status: 204, headers: headers, body: '{}') + stub_request(:delete, url).to_return(status: 204, headers: headers, body: nil) subject.delete_branch(project, repo_slug, branch, sha) diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb index 630732614b2..a7163048370 100644 --- a/spec/lib/feature_spec.rb +++ b/spec/lib/feature_spec.rb @@ -186,13 +186,14 @@ describe Feature do describe Feature::Target do describe '#targets' do let(:project) { create(:project) } + let(:group) { create(:group) } let(:user_name) { project.owner.username } - subject { described_class.new(user: user_name, project: project.full_path) } + subject { described_class.new(user: user_name, project: project.full_path, group: group.full_path) } it 'returns all found targets' do expect(subject.targets).to be_an(Array) - expect(subject.targets).to eq([project.owner, project]) + expect(subject.targets).to eq([project.owner, project, group]) end end end diff --git a/spec/lib/gitlab/background_migration/digest_column_spec.rb b/spec/lib/gitlab/background_migration/digest_column_spec.rb index 3e107ac3027..a25dcb06005 100644 --- a/spec/lib/gitlab/background_migration/digest_column_spec.rb +++ b/spec/lib/gitlab/background_migration/digest_column_spec.rb @@ -22,7 +22,7 @@ describe Gitlab::BackgroundMigration::DigestColumn, :migration, schema: 20180913 it 'erases token' do expect { subject.perform(PersonalAccessToken, :token, :token_digest, 1, 2) }.to( - change { PersonalAccessToken.find(1).token }.from('token-01').to(nil)) + change { PersonalAccessToken.find(1).read_attribute(:token) }.from('token-01').to(nil)) end end @@ -39,7 +39,7 @@ describe Gitlab::BackgroundMigration::DigestColumn, :migration, schema: 20180913 it 'leaves token empty' do expect { subject.perform(PersonalAccessToken, :token, :token_digest, 1, 2) }.not_to( - change { PersonalAccessToken.find(1).token }.from(nil)) + change { PersonalAccessToken.find(1).read_attribute(:token) }.from(nil)) end end end diff --git a/spec/lib/gitlab/chat/command_spec.rb b/spec/lib/gitlab/chat/command_spec.rb new file mode 100644 index 00000000000..46d23ab2b62 --- /dev/null +++ b/spec/lib/gitlab/chat/command_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Chat::Command do + let(:chat_name) { create(:chat_name) } + + let(:command) do + described_class.new( + project: project, + chat_name: chat_name, + name: 'spinach', + arguments: 'foo', + channel: '123', + response_url: 'http://example.com' + ) + end + + describe '#try_create_pipeline' do + let(:project) { create(:project) } + + it 'returns nil when the command is not valid' do + expect(command) + .to receive(:valid?) + .and_return(false) + + expect(command.try_create_pipeline).to be_nil + end + + it 'tries to create the pipeline when a command is valid' do + expect(command) + .to receive(:valid?) + .and_return(true) + + expect(command) + .to receive(:create_pipeline) + + command.try_create_pipeline + end + end + + describe '#create_pipeline' do + let(:project) { create(:project, :test_repo) } + let(:pipeline) { command.create_pipeline } + + before do + stub_repository_ci_yaml_file(sha: project.commit.id) + + project.add_developer(chat_name.user) + end + + it 'creates the pipeline' do + expect(pipeline).to be_persisted + end + + it 'creates the chat data for the pipeline' do + expect(pipeline.chat_data).to be_an_instance_of(Ci::PipelineChatData) + end + + it 'stores the chat name ID in the chat data' do + expect(pipeline.chat_data.chat_name_id).to eq(chat_name.id) + end + + it 'stores the response URL in the chat data' do + expect(pipeline.chat_data.response_url).to eq('http://example.com') + end + + it 'creates the environment variables for the pipeline' do + vars = pipeline.variables.each_with_object({}) do |row, hash| + hash[row.key] = row.value + end + + expect(vars['CHAT_INPUT']).to eq('foo') + expect(vars['CHAT_CHANNEL']).to eq('123') + end + end +end diff --git a/spec/lib/gitlab/chat/output_spec.rb b/spec/lib/gitlab/chat/output_spec.rb new file mode 100644 index 00000000000..b179f9e9d0a --- /dev/null +++ b/spec/lib/gitlab/chat/output_spec.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Chat::Output do + let(:build) do + create(:ci_build, pipeline: create(:ci_pipeline, source: :chat)) + end + + let(:output) { described_class.new(build) } + + describe '#to_s' do + it 'returns the build output as a String' do + trace = Gitlab::Ci::Trace.new(build) + + trace.set("echo hello\nhello") + + allow(build) + .to receive(:trace) + .and_return(trace) + + allow(output) + .to receive(:read_offset_and_length) + .and_return([0, 13]) + + expect(output.to_s).to eq('he') + end + end + + describe '#read_offset_and_length' do + context 'without the chat_reply trace section' do + it 'falls back to using the build_script trace section' do + expect(output) + .to receive(:find_build_trace_section) + .with('chat_reply') + .and_return(nil) + + expect(output) + .to receive(:find_build_trace_section) + .with('build_script') + .and_return({ name: 'build_script', byte_start: 1, byte_end: 4 }) + + expect(output.read_offset_and_length).to eq([1, 3]) + end + end + + context 'without the build_script trace section' do + it 'raises MissingBuildSectionError' do + expect { output.read_offset_and_length } + .to raise_error(described_class::MissingBuildSectionError) + end + end + + context 'with the chat_reply trace section' do + it 'returns the read offset and length as an Array' do + trace = Gitlab::Ci::Trace.new(build) + + allow(build) + .to receive(:trace) + .and_return(trace) + + allow(trace) + .to receive(:extract_sections) + .and_return([{ name: 'chat_reply', byte_start: 1, byte_end: 4 }]) + + expect(output.read_offset_and_length).to eq([1, 3]) + end + end + end + + describe '#without_executed_command_line' do + it 'returns the input without the first line' do + expect(output.without_executed_command_line("hello\nworld")) + .to eq('world') + end + + it 'returns an empty String when the input is empty' do + expect(output.without_executed_command_line('')).to eq('') + end + + it 'returns an empty String when the input consits of a single newline' do + expect(output.without_executed_command_line("\n")).to eq('') + end + end + + describe '#find_build_trace_section' do + it 'returns nil when no section could be found' do + expect(output.find_build_trace_section('foo')).to be_nil + end + + it 'returns the trace section when it could be found' do + section = { name: 'chat_reply', byte_start: 1, byte_end: 4 } + + allow(output) + .to receive(:trace_sections) + .and_return([section]) + + expect(output.find_build_trace_section('chat_reply')).to eq(section) + end + end +end diff --git a/spec/lib/gitlab/chat/responder/base_spec.rb b/spec/lib/gitlab/chat/responder/base_spec.rb new file mode 100644 index 00000000000..7fa9bad9d38 --- /dev/null +++ b/spec/lib/gitlab/chat/responder/base_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Chat::Responder::Base do + let(:project) { double(:project) } + let(:pipeline) { double(:pipeline, project: project) } + let(:build) { double(:build, pipeline: pipeline) } + let(:responder) { described_class.new(build) } + + describe '#pipeline' do + it 'returns the pipeline' do + expect(responder.pipeline).to eq(pipeline) + end + end + + describe '#project' do + it 'returns the project' do + expect(responder.project).to eq(project) + end + end + + describe '#success' do + it 'raises NotImplementedError' do + expect { responder.success }.to raise_error(NotImplementedError) + end + end + + describe '#failure' do + it 'raises NotImplementedError' do + expect { responder.failure }.to raise_error(NotImplementedError) + end + end + + describe '#send_response' do + it 'raises NotImplementedError' do + expect { responder.send_response('hello') } + .to raise_error(NotImplementedError) + end + end + + describe '#scheduled_output' do + it 'raises NotImplementedError' do + expect { responder.scheduled_output } + .to raise_error(NotImplementedError) + end + end +end diff --git a/spec/lib/gitlab/chat/responder/slack_spec.rb b/spec/lib/gitlab/chat/responder/slack_spec.rb new file mode 100644 index 00000000000..a1553232b32 --- /dev/null +++ b/spec/lib/gitlab/chat/responder/slack_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Chat::Responder::Slack do + let(:chat_name) { create(:chat_name, chat_id: 'U123') } + + let(:pipeline) do + pipeline = create(:ci_pipeline) + + pipeline.create_chat_data!( + response_url: 'http://example.com', + chat_name_id: chat_name.id + ) + + pipeline + end + + let(:build) { create(:ci_build, pipeline: pipeline) } + let(:responder) { described_class.new(build) } + + describe '#send_response' do + it 'sends a response back to Slack' do + expect(Gitlab::HTTP).to receive(:post).with( + 'http://example.com', + { headers: { Accept: 'application/json' }, body: 'hello'.to_json } + ) + + responder.send_response('hello') + end + end + + describe '#success' do + it 'returns the output for a successful build' do + expect(responder) + .to receive(:send_response) + .with(hash_including(text: /<@U123>:.+hello/, response_type: :in_channel)) + + responder.success('hello') + end + + it 'limits the output to a fixed size' do + expect(responder) + .to receive(:send_response) + .with(hash_including(text: /The output is too large/)) + + responder.success('a' * 4000) + end + + it 'does not send a response if the output is empty' do + expect(responder).not_to receive(:send_response) + + responder.success('') + end + end + + describe '#failure' do + it 'returns the output for a failed build' do + expect(responder).to receive(:send_response).with( + hash_including( + text: /<@U123>:.+Sorry, the build failed!/, + response_type: :in_channel + ) + ) + + responder.failure + end + end + + describe '#scheduled_output' do + it 'returns the output for a scheduled build' do + output = responder.scheduled_output + + expect(output).to eq({ text: '' }) + end + end +end diff --git a/spec/lib/gitlab/chat/responder_spec.rb b/spec/lib/gitlab/chat/responder_spec.rb new file mode 100644 index 00000000000..9893689cba9 --- /dev/null +++ b/spec/lib/gitlab/chat/responder_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Chat::Responder do + describe '.responder_for' do + context 'using a regular build' do + it 'returns nil' do + build = create(:ci_build) + + expect(described_class.responder_for(build)).to be_nil + end + end + + context 'using a chat build' do + it 'returns the responder for the build' do + pipeline = create(:ci_pipeline) + build = create(:ci_build, pipeline: pipeline) + service = double(:service, chat_responder: Gitlab::Chat::Responder::Slack) + chat_name = double(:chat_name, service: service) + chat_data = double(:chat_data, chat_name: chat_name) + + allow(pipeline) + .to receive(:chat_data) + .and_return(chat_data) + + expect(described_class.responder_for(build)) + .to be_an_instance_of(Gitlab::Chat::Responder::Slack) + end + end + end +end diff --git a/spec/lib/gitlab/chat_spec.rb b/spec/lib/gitlab/chat_spec.rb new file mode 100644 index 00000000000..d61c4b36668 --- /dev/null +++ b/spec/lib/gitlab/chat_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe Gitlab::Chat, :use_clean_rails_memory_store_caching do + describe '.available?' do + it 'returns true when the chatops feature is available' do + allow(Feature) + .to receive(:enabled?) + .with(:chatops, default_enabled: true) + .and_return(true) + + expect(described_class).to be_available + end + + it 'returns false when the chatops feature is not available' do + allow(Feature) + .to receive(:enabled?) + .with(:chatops, default_enabled: true) + .and_return(false) + + expect(described_class).not_to be_available + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb new file mode 100644 index 00000000000..7c1c016b4bb --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs do + let(:project) { create(:project, :repository) } + + let(:pipeline) do + build(:ci_pipeline_with_one_job, project: project, ref: 'master') + end + + let(:command) do + double(:command, project: project, chat_data: { command: 'echo' }) + end + + describe '#perform!' do + it 'removes unwanted jobs for chat pipelines' do + allow(pipeline).to receive(:chat?).and_return(true) + + pipeline.config_processor.jobs[:echo] = double(:job) + + described_class.new(pipeline, command).perform! + + expect(pipeline.config_processor.jobs.keys).to eq([:echo]) + end + end + + it 'does not remove any jobs for non-chat pipelines' do + described_class.new(pipeline, command).perform! + + expect(pipeline.config_processor.jobs.keys).to eq([:rspec]) + end +end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index c15b360b563..f2eccec4635 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -131,6 +131,7 @@ ci_pipelines: - merge_request - deployments - environments +- chat_data pipeline_variables: - pipeline stages: diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb index 27c802f34ec..95b6b3fd953 100644 --- a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb @@ -30,7 +30,7 @@ describe Gitlab::Kubernetes::Helm::Pod do it 'should generate the appropriate specifications for the container' do container = subject.generate.spec.containers.first expect(container.name).to eq('helm') - expect(container.image).to eq('registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/2.12.2-kube-1.11.0') + expect(container.image).to eq('registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/2.12.3-kube-1.11.7') expect(container.env.count).to eq(3) expect(container.env.map(&:name)).to match_array([:HELM_VERSION, :TILLER_NAMESPACE, :COMMAND_SCRIPT]) expect(container.command).to match_array(["/bin/sh"]) diff --git a/spec/lib/gitlab/slash_commands/application_help_spec.rb b/spec/lib/gitlab/slash_commands/application_help_spec.rb new file mode 100644 index 00000000000..b203a1ee79c --- /dev/null +++ b/spec/lib/gitlab/slash_commands/application_help_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::SlashCommands::ApplicationHelp do + let(:params) { { command: '/gitlab', text: 'help' } } + + describe '#execute' do + subject do + described_class.new(params).execute + end + + it 'displays the help section' do + expect(subject[:response_type]).to be(:ephemeral) + expect(subject[:text]).to include('Available commands') + expect(subject[:text]).to include('/gitlab [project name or alias] issue show') + end + end +end diff --git a/spec/lib/gitlab/slash_commands/presenters/error_spec.rb b/spec/lib/gitlab/slash_commands/presenters/error_spec.rb new file mode 100644 index 00000000000..30ff81510c1 --- /dev/null +++ b/spec/lib/gitlab/slash_commands/presenters/error_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::SlashCommands::Presenters::Error do + subject { described_class.new('Error').message } + + it { is_expected.to be_a(Hash) } + + it 'shows the error message' do + expect(subject[:response_type]).to be(:ephemeral) + expect(subject[:status]).to eq(200) + expect(subject[:text]).to eq('Error') + end +end diff --git a/spec/lib/gitlab/slash_commands/presenters/run_spec.rb b/spec/lib/gitlab/slash_commands/presenters/run_spec.rb new file mode 100644 index 00000000000..f3ab01ef6bb --- /dev/null +++ b/spec/lib/gitlab/slash_commands/presenters/run_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::SlashCommands::Presenters::Run do + let(:presenter) { described_class.new } + + describe '#present' do + context 'when no builds are present' do + it 'returns an error' do + builds = double(:builds, take: nil) + pipeline = double(:pipeline, builds: builds) + + expect(presenter) + .to receive(:unsupported_chat_service) + + presenter.present(pipeline) + end + end + + context 'when a responder could be found' do + it 'returns the output for a scheduled pipeline' do + responder = double(:responder, scheduled_output: 'hello') + build = double(:build) + builds = double(:builds, take: build) + pipeline = double(:pipeline, builds: builds) + + allow(Gitlab::Chat::Responder) + .to receive(:responder_for) + .with(build) + .and_return(responder) + + expect(presenter) + .to receive(:in_channel_response) + .with('hello') + + presenter.present(pipeline) + end + end + + context 'when a responder could not be found' do + it 'returns an error' do + build = double(:build) + builds = double(:builds, take: build) + pipeline = double(:pipeline, builds: builds) + + allow(Gitlab::Chat::Responder) + .to receive(:responder_for) + .with(build) + .and_return(nil) + + expect(presenter) + .to receive(:unsupported_chat_service) + + presenter.present(pipeline) + end + end + end + + describe '#unsupported_chat_service' do + it 'returns an ephemeral response' do + expect(presenter) + .to receive(:ephemeral_response) + .with(text: /Sorry, this chat service is currently not supported/) + + presenter.unsupported_chat_service + end + end + + describe '#failed_to_schedule' do + it 'returns an ephemeral response' do + expect(presenter) + .to receive(:ephemeral_response) + .with(text: /The command could not be scheduled/) + + presenter.failed_to_schedule('foo') + end + end +end diff --git a/spec/lib/gitlab/slash_commands/run_spec.rb b/spec/lib/gitlab/slash_commands/run_spec.rb new file mode 100644 index 00000000000..900fae05719 --- /dev/null +++ b/spec/lib/gitlab/slash_commands/run_spec.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::SlashCommands::Run do + describe '.available?' do + it 'returns true when builds are enabled for the project' do + project = double(:project, builds_enabled?: true) + + allow(Gitlab::Chat) + .to receive(:available?) + .and_return(true) + + expect(described_class.available?(project)).to eq(true) + end + + it 'returns false when builds are disabled for the project' do + project = double(:project, builds_enabled?: false) + + expect(described_class.available?(project)).to eq(false) + end + + it 'returns false when chatops is not available' do + allow(Gitlab::Chat) + .to receive(:available?) + .and_return(false) + + project = double(:project, builds_enabled?: true) + + expect(described_class.available?(project)).to eq(false) + end + end + + describe '.allowed?' do + it 'returns true when the user can create a pipeline' do + project = create(:project) + + expect(described_class.allowed?(project, project.creator)).to eq(true) + end + + it 'returns false when the user can not create a pipeline' do + project = create(:project) + user = create(:user) + + expect(described_class.allowed?(project, user)).to eq(false) + end + end + + describe '#execute' do + let(:chat_name) { create(:chat_name) } + let(:project) { create(:project) } + + let(:command) do + described_class.new(project, chat_name, response_url: 'http://example.com') + end + + context 'when a pipeline could not be scheduled' do + it 'returns an error' do + expect_any_instance_of(Gitlab::Chat::Command) + .to receive(:try_create_pipeline) + .and_return(nil) + + expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run) + .to receive(:failed_to_schedule) + .with('foo') + + command.execute(command: 'foo', arguments: '') + end + end + + context 'when a pipeline could be created but the chat service was not supported' do + it 'returns an error' do + build = double(:build) + pipeline = double( + :pipeline, + builds: double(:relation, take: build), + persisted?: true + ) + + expect_any_instance_of(Gitlab::Chat::Command) + .to receive(:try_create_pipeline) + .and_return(pipeline) + + expect(Gitlab::Chat::Responder) + .to receive(:responder_for) + .with(build) + .and_return(nil) + + expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run) + .to receive(:unsupported_chat_service) + + command.execute(command: 'foo', arguments: '') + end + end + + context 'using a valid pipeline' do + it 'schedules the pipeline' do + responder = double(:responder, scheduled_output: 'hello') + build = double(:build) + pipeline = double( + :pipeline, + builds: double(:relation, take: build), + persisted?: true + ) + + expect_any_instance_of(Gitlab::Chat::Command) + .to receive(:try_create_pipeline) + .and_return(pipeline) + + expect(Gitlab::Chat::Responder) + .to receive(:responder_for) + .with(build) + .and_return(responder) + + expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run) + .to receive(:in_channel_response) + .with(responder.scheduled_output) + + command.execute(command: 'foo', arguments: '') + end + end + end +end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 460b5c8cd31..b9567ab4d65 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -22,6 +22,7 @@ describe Ci::Pipeline, :mailer do it { is_expected.to have_many(:builds) } it { is_expected.to have_many(:auto_canceled_pipelines) } it { is_expected.to have_many(:auto_canceled_jobs) } + it { is_expected.to have_one(:chat_data) } it { is_expected.to validate_presence_of(:sha) } it { is_expected.to validate_presence_of(:status) } diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb index 2250ef301aa..81708b0c2ed 100644 --- a/spec/models/clusters/applications/prometheus_spec.rb +++ b/spec/models/clusters/applications/prometheus_spec.rb @@ -27,65 +27,6 @@ describe Clusters::Applications::Prometheus do end end - describe '#ready' do - let(:project) { create(:project) } - let(:cluster) { create(:cluster, projects: [project]) } - - it 'returns true when installed' do - application = build(:clusters_applications_prometheus, :installed, cluster: cluster) - - expect(application).to be_ready - end - - it 'returns false when not_installable' do - application = build(:clusters_applications_prometheus, :not_installable, cluster: cluster) - - expect(application).not_to be_ready - end - - it 'returns false when installable' do - application = build(:clusters_applications_prometheus, :installable, cluster: cluster) - - expect(application).not_to be_ready - end - - it 'returns false when scheduled' do - application = build(:clusters_applications_prometheus, :scheduled, cluster: cluster) - - expect(application).not_to be_ready - end - - it 'returns false when installing' do - application = build(:clusters_applications_prometheus, :installing, cluster: cluster) - - expect(application).not_to be_ready - end - - it 'returns false when errored' do - application = build(:clusters_applications_prometheus, :errored, cluster: cluster) - - expect(application).not_to be_ready - end - - it 'returns true when updating' do - application = build(:clusters_applications_prometheus, :updating, cluster: cluster) - - expect(application).to be_ready - end - - it 'returns true when updated' do - application = build(:clusters_applications_prometheus, :updated, cluster: cluster) - - expect(application).to be_ready - end - - it 'returns true when errored' do - application = build(:clusters_applications_prometheus, :update_errored, cluster: cluster) - - expect(application).to be_ready - end - end - describe '#prometheus_client' do context 'cluster is nil' do it 'returns nil' do diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb index 55d83bc3a6b..40cb4eef60a 100644 --- a/spec/models/concerns/token_authenticatable_spec.rb +++ b/spec/models/concerns/token_authenticatable_spec.rb @@ -97,14 +97,31 @@ describe ApplicationSetting, 'TokenAuthenticatable' do end describe PersonalAccessToken, 'TokenAuthenticatable' do - let(:personal_access_token_name) { 'test-pat-01' } + shared_examples 'changes personal access token' do + it 'sets new token' do + subject + + expect(personal_access_token.token).to eq(token_value) + expect(personal_access_token.token_digest).to eq(Gitlab::CryptoHelper.sha256(token_value)) + end + end + + shared_examples 'does not change personal access token' do + it 'sets new token' do + subject + + expect(personal_access_token.token).to be(nil) + expect(personal_access_token.token_digest).to eq(token_digest) + end + end + let(:token_value) { 'token' } + let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) } let(:user) { create(:user) } let(:personal_access_token) do - described_class.new(name: personal_access_token_name, + described_class.new(name: 'test-pat-01', user_id: user.id, scopes: [:api], - token: token, token_digest: token_digest) end @@ -115,239 +132,71 @@ describe PersonalAccessToken, 'TokenAuthenticatable' do describe '.find_by_token' do subject { PersonalAccessToken.find_by_token(token_value) } - before do + it 'finds the token' do personal_access_token.save - end - context 'token_digest already exists' do - let(:token) { nil } - let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) } - - it 'finds the token' do - expect(subject).not_to be_nil - expect(subject.name).to eql(personal_access_token_name) - end - end - - context 'token_digest does not exist' do - let(:token) { token_value } - let(:token_digest) { nil } - - it 'finds the token' do - expect(subject).not_to be_nil - expect(subject.name).to eql(personal_access_token_name) - end + expect(subject).to eq(personal_access_token) end end describe '#set_token' do let(:new_token_value) { 'new-token' } - subject { personal_access_token.set_token(new_token_value) } - - context 'token_digest already exists' do - let(:token) { nil } - let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) } - it 'overwrites token_digest' do - subject - - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to eql(new_token_value) - expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(new_token_value)) - end - end - - context 'token_digest does not exist but token does' do - let(:token) { token_value } - let(:token_digest) { nil } - - it 'creates new token_digest and clears token' do - subject - - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to eql(new_token_value) - expect(personal_access_token.token_digest).to eql(Gitlab::CryptoHelper.sha256(new_token_value)) - end - end - - context 'token_digest does not exist, nor token' do - let(:token) { nil } - let(:token_digest) { nil } + subject { personal_access_token.set_token(new_token_value) } - it 'creates new token_digest' do - subject + it 'sets new token' do + subject - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to eql(new_token_value) - expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(new_token_value)) - end + expect(personal_access_token.token).to eq(new_token_value) + expect(personal_access_token.token_digest).to eq(Gitlab::CryptoHelper.sha256(new_token_value)) end end describe '#ensure_token' do subject { personal_access_token.ensure_token } - context 'token_digest already exists' do - let(:token) { nil } - let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) } - - it 'does not change token fields' do - subject - - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to be_nil - expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value)) - end - end - - context 'token_digest does not exist but token does' do - let(:token) { token_value } + context 'token_digest does not exist' do let(:token_digest) { nil } - it 'does not change token fields' do - subject - - expect(personal_access_token.read_attribute('token')).to eql(token_value) - expect(personal_access_token.token).to eql(token_value) - expect(personal_access_token.token_digest).to be_nil - end + it_behaves_like 'changes personal access token' end - context 'token_digest does not exist, nor token' do - let(:token) { nil } - let(:token_digest) { nil } - - it 'creates token_digest' do - subject + context 'token_digest already generated' do + let(:token_digest) { 's3cr3t' } - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to eql(token_value) - expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value)) - end + it_behaves_like 'does not change personal access token' end end describe '#ensure_token!' do subject { personal_access_token.ensure_token! } - context 'token_digest already exists' do - let(:token) { nil } - let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) } - - it 'does not change token fields' do - subject - - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to be_nil - expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value)) - end - end - - context 'token_digest does not exist but token does' do - let(:token) { token_value } + context 'token_digest does not exist' do let(:token_digest) { nil } - it 'does not change token fields' do - subject - - expect(personal_access_token.read_attribute('token')).to eql(token_value) - expect(personal_access_token.token).to eql(token_value) - expect(personal_access_token.token_digest).to be_nil - end + it_behaves_like 'changes personal access token' end - context 'token_digest does not exist, nor token' do - let(:token) { nil } - let(:token_digest) { nil } + context 'token_digest already generated' do + let(:token_digest) { 's3cr3t' } - it 'creates token_digest' do - subject - - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to eql(token_value) - expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value)) - end + it_behaves_like 'does not change personal access token' end end describe '#reset_token!' do subject { personal_access_token.reset_token! } - context 'token_digest already exists' do - let(:token) { nil } - let(:token_digest) { Gitlab::CryptoHelper.sha256('old-token') } - - it 'creates new token_digest' do - subject - - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to eql(token_value) - expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value)) - end - end - - context 'token_digest does not exist but token does' do - let(:token) { 'old-token' } - let(:token_digest) { nil } - - it 'creates new token_digest and clears token' do - subject - - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to eql(token_value) - expect(personal_access_token.token_digest).to eql(Gitlab::CryptoHelper.sha256(token_value)) - end - end - - context 'token_digest does not exist, nor token' do - let(:token) { nil } + context 'token_digest does not exist' do let(:token_digest) { nil } - it 'creates new token_digest' do - subject - - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to eql(token_value) - expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value)) - end - end - - context 'token_digest exists and newly generated token would be the same' do - let(:token) { nil } - let(:token_digest) { Gitlab::CryptoHelper.sha256('old-token') } - - before do - personal_access_token.save - allow(Devise).to receive(:friendly_token).and_return( - 'old-token', token_value, 'boom!') - end - - it 'regenerates a new token_digest' do - subject - - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to eql(token_value) - expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value)) - end + it_behaves_like 'changes personal access token' end - context 'token exists and newly generated token would be the same' do - let(:token) { 'old-token' } - let(:token_digest) { nil } - - before do - personal_access_token.save - allow(Devise).to receive(:friendly_token).and_return( - 'old-token', token_value, 'boom!') - end + context 'token_digest already generated' do + let(:token_digest) { 's3cr3t' } - it 'regenerates a new token_digest' do - subject - - expect(personal_access_token.read_attribute('token')).to be_nil - expect(personal_access_token.token).to eql(token_value) - expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value)) - end + it_behaves_like 'changes personal access token' end end end diff --git a/spec/models/project_services/slack_slash_commands_service_spec.rb b/spec/models/project_services/slack_slash_commands_service_spec.rb index 0d95f454819..5c4bce90ace 100644 --- a/spec/models/project_services/slack_slash_commands_service_spec.rb +++ b/spec/models/project_services/slack_slash_commands_service_spec.rb @@ -38,4 +38,11 @@ describe SlackSlashCommandsService do end end end + + describe '#chat_responder' do + it 'returns the responder to use for Slack' do + expect(described_class.new.chat_responder) + .to eq(Gitlab::Chat::Responder::Slack) + end + end end diff --git a/spec/requests/api/features_spec.rb b/spec/requests/api/features_spec.rb index 22a9e36ca31..57a57e69a00 100644 --- a/spec/requests/api/features_spec.rb +++ b/spec/requests/api/features_spec.rb @@ -163,6 +163,40 @@ describe API::Features do end end + context 'when enabling for a group by path' do + context 'when the group exists' do + it 'sets the feature gate' do + group = create(:group) + + post api("/features/#{feature_name}", admin), params: { value: 'true', group: group.full_path } + + expect(response).to have_gitlab_http_status(201) + expect(json_response).to eq( + 'name' => 'my_feature', + 'state' => 'conditional', + 'gates' => [ + { 'key' => 'boolean', 'value' => false }, + { 'key' => 'actors', 'value' => ["Group:#{group.id}"] } + ]) + end + end + + context 'when the group does not exist' do + it 'sets no new values and keeps the feature disabled' do + post api("/features/#{feature_name}", admin), params: { value: 'true', group: 'not/a/group' } + + expect(response).to have_gitlab_http_status(201) + expect(json_response).to eq( + "name" => "my_feature", + "state" => "off", + "gates" => [ + { "key" => "boolean", "value" => false } + ] + ) + end + end + end + it 'creates a feature with the given percentage if passed an integer' do post api("/features/#{feature_name}", admin), params: { value: '50' } diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 04908378a24..d10ee6cc320 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -359,10 +359,38 @@ describe API::Issues do expect_paginated_array_response([]) end - it 'sorts by created_at descending by default' do - get api('/issues', user) + context 'without sort params' do + it 'sorts by created_at descending by default' do + get api('/issues', user) - expect_paginated_array_response([issue.id, closed_issue.id]) + expect_paginated_array_response([issue.id, closed_issue.id]) + end + + context 'with 2 issues with same created_at' do + let!(:closed_issue2) do + create :closed_issue, + author: user, + assignees: [user], + project: project, + milestone: milestone, + created_at: closed_issue.created_at, + updated_at: 1.hour.ago, + title: issue_title, + description: issue_description + end + + it 'page breaks first page correctly' do + get api('/issues?per_page=2', user) + + expect_paginated_array_response([issue.id, closed_issue2.id]) + end + + it 'page breaks second page correctly' do + get api('/issues?per_page=2&page=2', user) + + expect_paginated_array_response([closed_issue.id]) + end + end end it 'sorts ascending when requested' do @@ -393,6 +421,24 @@ describe API::Issues do expect(response).to have_gitlab_http_status(200) expect(response).to match_response_schema('public_api/v4/issues') end + + it 'returns a related merge request count of 0 if there are no related merge requests' do + get api('/issues', user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/issues') + expect(json_response.first).to include('merge_requests_count' => 0) + end + + it 'returns a related merge request count > 0 if there are related merge requests' do + create(:merge_requests_closing_issues, issue: issue) + + get api('/issues', user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/issues') + expect(json_response.first).to include('merge_requests_count' => 1) + end end end @@ -613,10 +659,38 @@ describe API::Issues do expect_paginated_array_response(group_confidential_issue.id) end - it 'sorts by created_at descending by default' do - get api(base_url, user) + context 'without sort params' do + it 'sorts by created_at descending by default' do + get api(base_url, user) - expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id, group_issue.id]) + expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id, group_issue.id]) + end + + context 'with 2 issues with same created_at' do + let!(:group_issue2) do + create :issue, + author: user, + assignees: [user], + project: group_project, + milestone: group_milestone, + updated_at: 1.hour.ago, + title: issue_title, + description: issue_description, + created_at: group_issue.created_at + end + + it 'page breaks first page correctly' do + get api("#{base_url}?per_page=3", user) + + expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id, group_issue2.id]) + end + + it 'page breaks second page correctly' do + get api("#{base_url}?per_page=3&page=2", user) + + expect_paginated_array_response([group_issue.id]) + end + end end it 'sorts ascending when requested' do @@ -828,10 +902,38 @@ describe API::Issues do expect_paginated_array_response([issue.id, closed_issue.id]) end - it 'sorts by created_at descending by default' do - get api("#{base_url}/issues", user) + context 'without sort params' do + it 'sorts by created_at descending by default' do + get api("#{base_url}/issues", user) - expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id]) + expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id]) + end + + context 'with 2 issues with same created_at' do + let!(:closed_issue2) do + create :closed_issue, + author: user, + assignees: [user], + project: project, + milestone: milestone, + created_at: closed_issue.created_at, + updated_at: 1.hour.ago, + title: issue_title, + description: issue_description + end + + it 'page breaks first page correctly' do + get api("#{base_url}/issues?per_page=3", user) + + expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue2.id]) + end + + it 'page breaks second page correctly' do + get api("#{base_url}/issues?per_page=3&page=2", user) + + expect_paginated_array_response([closed_issue.id]) + end + end end it 'sorts ascending when requested' do @@ -1792,7 +1894,7 @@ describe API::Issues do description: "See #{issue.to_reference}" } create(:merge_request, attributes).tap do |merge_request| - create(:note, :system, project: project, noteable: issue, author: user, note: merge_request.to_reference(full: true)) + create(:note, :system, project: issue.project, noteable: issue, author: user, note: merge_request.to_reference(full: true)) end end @@ -1829,6 +1931,24 @@ describe API::Issues do expect_paginated_array_response(related_mr.id) end + it 'returns merge requests cross-project wide' do + project2 = create(:project, :public, creator_id: user.id, namespace: user.namespace) + merge_request = create_referencing_mr(user, project2, issue) + + get_related_merge_requests(project.id, issue.iid, user) + + expect_paginated_array_response([related_mr.id, merge_request.id]) + end + + it 'does not generate references to projects with no access' do + private_project = create(:project, :private) + create_referencing_mr(private_project.creator, private_project, issue) + + get_related_merge_requests(project.id, issue.iid, user) + + expect_paginated_array_response(related_mr.id) + end + context 'no merge request mentioned a issue' do it 'returns empty array' do get_related_merge_requests(project.id, closed_issue.iid, user) diff --git a/spec/services/prometheus/adapter_service_spec.rb b/spec/services/prometheus/adapter_service_spec.rb index 335fc5844aa..505e2935e93 100644 --- a/spec/services/prometheus/adapter_service_spec.rb +++ b/spec/services/prometheus/adapter_service_spec.rb @@ -22,7 +22,15 @@ describe Prometheus::AdapterService do context "prometheus service can't execute queries" do let(:prometheus_service) { double(:prometheus_service, can_query?: false) } - context 'with cluster with prometheus installed' do + context 'with cluster with prometheus not available' do + let!(:prometheus) { create(:clusters_applications_prometheus, :installable, cluster: cluster) } + + it 'returns nil' do + expect(subject.prometheus_adapter).to be_nil + end + end + + context 'with cluster with prometheus available' do let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) } it 'returns application handling all environments' do diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb index 5945a7dc0ad..747e04fb18c 100644 --- a/spec/services/web_hook_service_spec.rb +++ b/spec/services/web_hook_service_spec.rb @@ -102,7 +102,7 @@ describe WebHookService do exception = exception_class.new('Exception message') WebMock.stub_request(:post, project_hook.url).to_raise(exception) - expect(service_instance.execute).to eq({ status: :error, message: exception.message }) + expect(service_instance.execute).to eq({ status: :error, message: exception.to_s }) expect { service_instance.execute }.not_to raise_error end end diff --git a/spec/support/shared_contexts/services_shared_context.rb b/spec/support/shared_contexts/services_shared_context.rb index 23f9b46ae0c..d92e8318fa0 100644 --- a/spec/support/shared_contexts/services_shared_context.rb +++ b/spec/support/shared_contexts/services_shared_context.rb @@ -19,7 +19,7 @@ Service.available_services_names.each do |service| elsif service == 'irker' && k == :server_port hash.merge!(k => 1234) elsif service == 'jira' && k == :jira_issue_transition_id - hash.merge!(k => 1234) + hash.merge!(k => '1,2,3') else hash.merge!(k => "someword") end diff --git a/spec/support/shared_examples/requests/api/merge_requests_list.rb b/spec/support/shared_examples/requests/api/merge_requests_list.rb index 453f42251c8..6713ec47ace 100644 --- a/spec/support/shared_examples/requests/api/merge_requests_list.rb +++ b/spec/support/shared_examples/requests/api/merge_requests_list.rb @@ -257,6 +257,38 @@ shared_examples 'merge requests list' do expect(response_dates).to eq(response_dates.sort.reverse) end + context '2 merge requests with equal created_at' do + let!(:closed_mr2) do + create :merge_request, + state: 'closed', + milestone: milestone1, + author: user, + assignee: user, + source_project: project, + target_project: project, + title: "Test", + created_at: @mr_earlier.created_at + end + + it 'page breaks first page correctly' do + get api("#{endpoint_path}?sort=desc&per_page=4", user) + + response_ids = json_response.map { |merge_request| merge_request['id'] } + + expect(response_ids).to include(closed_mr2.id) + expect(response_ids).not_to include(@mr_earlier.id) + end + + it 'page breaks second page correctly' do + get api("#{endpoint_path}?sort=desc&per_page=4&page=2", user) + + response_ids = json_response.map { |merge_request| merge_request['id'] } + + expect(response_ids).not_to include(closed_mr2.id) + expect(response_ids).to include(@mr_earlier.id) + end + end + it 'returns an array of merge_requests ordered by updated_at' do path = endpoint_path + '?order_by=updated_at' diff --git a/spec/support/shared_examples/requests/api/notes.rb b/spec/support/shared_examples/requests/api/notes.rb index 71499e85654..57eefd5ef01 100644 --- a/spec/support/shared_examples/requests/api/notes.rb +++ b/spec/support/shared_examples/requests/api/notes.rb @@ -8,13 +8,45 @@ shared_examples 'noteable API' do |parent_type, noteable_type, id_name| create_list(:note, 3, params) end - it 'sorts by created_at in descending order by default' do - get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user) + context 'without sort params' do + it 'sorts by created_at in descending order by default' do + get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user) - response_dates = json_response.map { |note| note['created_at'] } + response_dates = json_response.map { |note| note['created_at'] } - expect(json_response.length).to eq(4) - expect(response_dates).to eq(response_dates.sort.reverse) + expect(json_response.length).to eq(4) + expect(response_dates).to eq(response_dates.sort.reverse) + end + + context '2 notes with equal created_at' do + before do + @first_note = Note.first + + params = { noteable: noteable, author: user } + params[:project] = parent if parent.is_a?(Project) + params[:created_at] = @first_note.created_at + + @note2 = create(:note, params) + end + + it 'page breaks first page correctly' do + get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?per_page=4", user) + + response_ids = json_response.map { |note| note['id'] } + + expect(response_ids).to include(@note2.id) + expect(response_ids).not_to include(@first_note.id) + end + + it 'page breaks second page correctly' do + get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?per_page=4&page=2", user) + + response_ids = json_response.map { |note| note['id'] } + + expect(response_ids).not_to include(@note2.id) + expect(response_ids).to include(@first_note.id) + end + end end it 'sorts by ascending order when requested' do diff --git a/spec/workers/build_finished_worker_spec.rb b/spec/workers/build_finished_worker_spec.rb index acd8da11d8d..ccb26849e67 100644 --- a/spec/workers/build_finished_worker_spec.rb +++ b/spec/workers/build_finished_worker_spec.rb @@ -26,5 +26,24 @@ describe BuildFinishedWorker do .not_to raise_error end end + + it 'schedules a ChatNotification job for a chat build' do + build = create(:ci_build, :success, pipeline: create(:ci_pipeline, source: :chat)) + + expect(ChatNotificationWorker) + .to receive(:perform_async) + .with(build.id) + + described_class.new.perform(build.id) + end + + it 'does not schedule a ChatNotification job for a regular build' do + build = create(:ci_build, :success, pipeline: create(:ci_pipeline)) + + expect(ChatNotificationWorker) + .not_to receive(:perform_async) + + described_class.new.perform(build.id) + end end end diff --git a/spec/workers/chat_notification_worker_spec.rb b/spec/workers/chat_notification_worker_spec.rb new file mode 100644 index 00000000000..91695674f5d --- /dev/null +++ b/spec/workers/chat_notification_worker_spec.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ChatNotificationWorker do + let(:worker) { described_class.new } + let(:chat_build) do + create(:ci_build, pipeline: create(:ci_pipeline, source: :chat)) + end + + describe '#perform' do + it 'does nothing when the build no longer exists' do + expect(worker).not_to receive(:send_response) + + worker.perform(-1) + end + + it 'sends a response for an existing build' do + expect(worker) + .to receive(:send_response) + .with(an_instance_of(Ci::Build)) + + worker.perform(chat_build.id) + end + + it 'reschedules the job if the trace sections could not be found' do + expect(worker) + .to receive(:send_response) + .and_raise(Gitlab::Chat::Output::MissingBuildSectionError) + + expect(described_class) + .to receive(:perform_in) + .with(described_class::RESCHEDULE_INTERVAL, chat_build.id) + + worker.perform(chat_build.id) + end + end + + describe '#send_response' do + context 'when a responder could not be found' do + it 'does nothing' do + expect(Gitlab::Chat::Responder) + .to receive(:responder_for) + .with(chat_build) + .and_return(nil) + + expect(worker.send_response(chat_build)).to be_nil + end + end + + context 'when a responder could be found' do + let(:responder) { double(:responder) } + + before do + allow(Gitlab::Chat::Responder) + .to receive(:responder_for) + .with(chat_build) + .and_return(responder) + end + + it 'sends the response for a succeeded build' do + output = double(:output, to_s: 'this is the build output') + + expect(chat_build) + .to receive(:success?) + .and_return(true) + + expect(responder) + .to receive(:success) + .with(an_instance_of(String)) + + expect(Gitlab::Chat::Output) + .to receive(:new) + .with(chat_build) + .and_return(output) + + worker.send_response(chat_build) + end + + it 'sends the response for a failed build' do + expect(chat_build) + .to receive(:success?) + .and_return(false) + + expect(responder).to receive(:failure) + + worker.send_response(chat_build) + end + end + end +end |