diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-13 09:09:48 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-13 09:09:48 +0300 |
commit | e7b6c527c40962adc6623521ff190771e4ac50e4 (patch) | |
tree | 8e8191419a1e82c99545a0aa755055703167c16f /spec/tooling/lib | |
parent | 49d65a7ae290040834a890fffd2481a606ba97f2 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/tooling/lib')
-rw-r--r-- | spec/tooling/lib/tooling/api/job_spec.rb | 95 | ||||
-rw-r--r-- | spec/tooling/lib/tooling/api/pipeline_spec.rb | 63 | ||||
-rw-r--r-- | spec/tooling/lib/tooling/api/request_spec.rb | 53 | ||||
-rw-r--r-- | spec/tooling/lib/tooling/debug_spec.rb | 76 |
4 files changed, 287 insertions, 0 deletions
diff --git a/spec/tooling/lib/tooling/api/job_spec.rb b/spec/tooling/lib/tooling/api/job_spec.rb new file mode 100644 index 00000000000..dfb047f0e6d --- /dev/null +++ b/spec/tooling/lib/tooling/api/job_spec.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require_relative '../../../../../tooling/lib/tooling/api/job' +require_relative '../../../../../tooling/lib/tooling/api/request' + +RSpec.describe Tooling::API::Job, feature_category: :tooling do + describe '#rspec_failed_files' do + let(:job) { described_class.new('api_token', 'project_id', 'job_id') } + let(:failures) { '' } + let(:log) do + <<~LOG + lots of content at the top of the file + #{failures} + some content at the bottom of the file + LOG + end + + subject(:rspec_failed_files) { job.rspec_failed_files } + + shared_context 'with stubbed API request' do + before do + # Stub the API request. + allow(job).to receive(:get_job_log).and_return(log) + end + end + + it 'will fetch job logs' do + uri = URI("https://gitlab.com/api/v4/projects/project_id/jobs/job_id/trace") + + response_double = instance_double(Net::HTTPOK, body: log) + expect(Tooling::API::Request).to receive(:get).with('api_token', uri).and_return(response_double) + + rspec_failed_files + end + + context 'when there are no failures' do + include_context 'with stubbed API request' + + let(:failures) { '' } + + it { is_expected.to be_empty } + end + + context 'when a spec fails on a specified line' do + include_context 'with stubbed API request' + + let(:failures) { 'rspec ./spec/foo_spec.rb:123' } + + it { is_expected.to eq(%w[spec/foo_spec.rb]) } + end + + context 'when a nested spec fails' do + include_context 'with stubbed API request' + + let(:failures) { %(rspec './spec/foo_spec.rb[123:456]') } + + it { is_expected.to eq(%w[spec/foo_spec.rb]) } + end + + context 'when there are multiple spec failures' do + include_context 'with stubbed API request' + + let(:failures) do + <<~LOG + rspec spec/foo_spec.rb:123 + rspec spec/bar_spec.rb:456 + rspec 'spec/ro_spec.rb[1:2]' + rspec 'spec/sham_spec.rb[3:4]' + rspec 'spec/bo_spec.rb[5:6]' + LOG + end + + it do + is_expected.to match_array( + %w[spec/bar_spec.rb spec/bo_spec.rb spec/foo_spec.rb spec/ro_spec.rb spec/sham_spec.rb] + ) + end + end + + context 'when there are multiple spec failures in the same file' do + include_context 'with stubbed API request' + + let(:failures) do + <<~LOG + rspec ./spec/foo_spec.rb:123 + rspec ./spec/foo_spec.rb:456 + rspec './spec/bar_spec.rb[1:2]' + rspec './spec/bar_spec.rb[3:4]' + LOG + end + + it { is_expected.to eq(%w[spec/foo_spec.rb spec/bar_spec.rb]) } + end + end +end diff --git a/spec/tooling/lib/tooling/api/pipeline_spec.rb b/spec/tooling/lib/tooling/api/pipeline_spec.rb new file mode 100644 index 00000000000..1c641328796 --- /dev/null +++ b/spec/tooling/lib/tooling/api/pipeline_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require_relative '../../../../../tooling/lib/tooling/api/pipeline' +require_relative '../../../../../tooling/lib/tooling/api/job' + +RSpec.describe Tooling::API::Pipeline, feature_category: :tooling do + let(:pipeline) { described_class.new('api_token', 'project_id', 'pipeline_id') } + + describe '#failed_jobs' do + subject { pipeline.failed_jobs } + + context 'when there are failed jobs' do + let(:jobs) { [{ 'id' => '123' }, { 'id' => '456' }] } + let(:response) { instance_double(Net::HTTPOK, body: jobs.to_json, '[]' => nil) } + + it 'returns the jobs' do + allow(Tooling::API::Request).to receive(:get).and_yield(response) + + expect(pipeline.failed_jobs).to eq(jobs) + end + end + end + + describe '#failed_spec_files' do + let(:job1) { { 'id' => 1 } } + let(:job2) { { 'id' => 2 } } + let(:failed_jobs) { [job1, job2] } + let(:job1_failed_files) { %w[spec/foo_spec.rb spec/bar_spec.rb] } + let(:job2_failed_files) { %w[spec/baz_spec.rb spec/qux_spec.rb] } + let(:failed_files) { job1_failed_files + job2_failed_files } + + subject { pipeline.failed_spec_files } + + before do + allow(pipeline).to receive(:failed_jobs).and_return(failed_jobs) + + allow(Tooling::API::Job).to receive(:new).with(anything, anything, job1['id']).and_return(job1) + allow(job1).to receive(:rspec_failed_files).and_return(job1_failed_files) + + allow(Tooling::API::Job).to receive(:new).with(anything, anything, job2['id']).and_return(job2) + allow(job2).to receive(:rspec_failed_files).and_return(job2_failed_files) + end + + it 'returns the failed spec files' do + expect(pipeline.failed_spec_files).to match_array(failed_files) + end + + context 'when Tooling::Debug is enabled' do + around do |example| + Tooling::Debug.debug = true + example.run + ensure + Tooling::Debug.debug = false + end + + it 'outputs the job logs' do + expect { pipeline.failed_spec_files }.to output(/Fetching failed jobs... found 2/).to_stdout + expect { pipeline.failed_spec_files }.to output(/Fetching job logs for #1/).to_stdout + expect { pipeline.failed_spec_files }.to output(/Fetching job logs for #2/).to_stdout + end + end + end +end diff --git a/spec/tooling/lib/tooling/api/request_spec.rb b/spec/tooling/lib/tooling/api/request_spec.rb new file mode 100644 index 00000000000..091bd860cbd --- /dev/null +++ b/spec/tooling/lib/tooling/api/request_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require_relative '../../../../../tooling/lib/tooling/api/request' + +require 'webmock/rspec' + +RSpec.describe Tooling::API::Request, feature_category: :tooling do + let(:base_url) { 'https://gitlab.com/api/v4/projects/project_id/pipelines/pipeline_id/jobs' } + + describe '.get' do + let(:body) { 'body' } + + subject(:response) { described_class.get('api_token', URI(base_url)) } + + context 'when the response is successful' do + before do + stub_request(:get, base_url).to_return(status: 200, body: body) + end + + it { expect(response.body).to eq(body) } + end + + context 'when the response is not successful' do + before do + stub_request(:get, base_url).to_return(status: 500) + end + + it { expect(response.body).to be_empty } + end + + context 'when there are multiple pages' do + let(:body1) { 'body1' } + let(:body2) { 'body2' } + + before do + stub_request(:get, base_url).to_return( + status: 200, body: body1, headers: { 'Link' => %(<#{base_url}&page=2>; rel="next") } + ) + stub_request(:get, "#{base_url}&page=2").to_return(status: 200, body: body2, headers: { 'Link' => '' }) + end + + it 'yields each page' do + expected = [body1, body2] + + expected_yield = proc do |response| + expect(response.body).to eq(expected.shift) + end + + described_class.get('api_token', URI(base_url), &expected_yield) + end + end + end +end diff --git a/spec/tooling/lib/tooling/debug_spec.rb b/spec/tooling/lib/tooling/debug_spec.rb new file mode 100644 index 00000000000..c506a93a24a --- /dev/null +++ b/spec/tooling/lib/tooling/debug_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require_relative '../../../../tooling/lib/tooling/debug' + +RSpec.describe Tooling::Debug, feature_category: :tooling do + let(:some_class) do + Class.new do + include Tooling::Debug + + def print_hello_world + print 'hello world' + end + + def puts_hello_world + puts 'hello world' + end + end + end + + after do + # Ensure that debug mode is default disabled at start of specs. + described_class.debug = false + end + + shared_context 'when debug is enabled' do + before do + described_class.debug = true + end + end + + shared_context 'when debug is disabled' do + before do + described_class.debug = false + end + end + + shared_examples 'writes to stdout' do |str| + it 'writes to stdout' do + expect { subject }.to output(str).to_stdout + end + end + + shared_examples 'does not write to stdout' do + it 'does not write to stdout' do + expect { subject }.not_to output.to_stdout + end + end + + describe '#print' do + subject { some_class.new.print_hello_world } + + context 'when debug is enabled' do + include_context 'when debug is enabled' + include_examples 'writes to stdout', 'hello world' + end + + context 'when debug is disabled' do + include_context 'when debug is disabled' + include_examples 'does not write to stdout' + end + end + + describe '#puts' do + subject { some_class.new.puts_hello_world } + + context 'when debug is enabled' do + include_context 'when debug is enabled' + include_examples 'writes to stdout', "hello world\n" + end + + context 'when debug is disabled' do + include_context 'when debug is disabled' + include_examples 'does not write to stdout' + end + end +end |