2019-10-21 15:05:58 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
2020-06-24 06:09:01 +00:00
|
|
|
RSpec.describe Ci::FindExposedArtifactsService do
|
2019-10-21 15:05:58 +00:00
|
|
|
include Gitlab::Routing
|
|
|
|
|
|
|
|
let(:metadata) do
|
|
|
|
Gitlab::Ci::Build::Artifacts::Metadata
|
|
|
|
.new(metadata_file_stream, path, { recursive: true })
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:metadata_file_stream) do
|
|
|
|
File.open(Rails.root + 'spec/fixtures/ci_build_artifacts_metadata.gz')
|
|
|
|
end
|
|
|
|
|
|
|
|
let_it_be(:project) { create(:project) }
|
2021-04-21 09:09:15 +00:00
|
|
|
|
2019-10-21 15:05:58 +00:00
|
|
|
let(:user) { nil }
|
|
|
|
|
|
|
|
after do
|
|
|
|
metadata_file_stream&.close
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_job_with_artifacts(options)
|
|
|
|
create(:ci_build, pipeline: pipeline, options: options).tap do |job|
|
|
|
|
create(:ci_job_artifact, :metadata, job: job)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#for_pipeline' do
|
|
|
|
shared_examples 'finds a single match' do
|
|
|
|
it 'returns the artifact with exact location' do
|
|
|
|
expect(subject).to eq([{
|
|
|
|
text: 'Exposed artifact',
|
|
|
|
url: file_project_job_artifacts_path(project, job, 'other_artifacts_0.1.2/doc_sample.txt'),
|
|
|
|
job_name: job.name,
|
|
|
|
job_path: project_job_path(project, job)
|
|
|
|
}])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples 'finds multiple matches' do
|
|
|
|
it 'returns the path to the artifacts browser' do
|
|
|
|
expect(subject).to eq([{
|
|
|
|
text: 'Exposed artifact',
|
|
|
|
url: browse_project_job_artifacts_path(project, job),
|
|
|
|
job_name: job.name,
|
|
|
|
job_path: project_job_path(project, job)
|
|
|
|
}])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-01-03 15:08:33 +00:00
|
|
|
shared_examples 'does not find any matches' do
|
|
|
|
it 'returns empty array' do
|
|
|
|
expect(subject).to eq []
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-10-21 15:05:58 +00:00
|
|
|
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
|
|
|
|
|
|
|
|
subject { described_class.new(project, user).for_pipeline(pipeline) }
|
|
|
|
|
2020-01-03 15:08:33 +00:00
|
|
|
context 'with jobs having no exposed artifacts' do
|
|
|
|
let!(:job) do
|
|
|
|
create_job_with_artifacts(artifacts: {
|
|
|
|
paths: ['other_artifacts_0.1.2/doc_sample.txt', 'something-else.html']
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'does not find any matches'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with jobs having no artifacts (metadata)' do
|
|
|
|
let!(:job) do
|
|
|
|
create(:ci_build, pipeline: pipeline, options: {
|
|
|
|
artifacts: {
|
|
|
|
expose_as: 'Exposed artifact',
|
|
|
|
paths: ['other_artifacts_0.1.2/doc_sample.txt', 'something-else.html']
|
|
|
|
}
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'does not find any matches'
|
|
|
|
end
|
|
|
|
|
2019-10-21 15:05:58 +00:00
|
|
|
context 'with jobs having at most 1 matching exposed artifact' do
|
|
|
|
let!(:job) do
|
|
|
|
create_job_with_artifacts(artifacts: {
|
|
|
|
expose_as: 'Exposed artifact',
|
|
|
|
paths: ['other_artifacts_0.1.2/doc_sample.txt', 'something-else.html']
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'finds a single match'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with jobs having more than 1 matching exposed artifacts' do
|
|
|
|
let!(:job) do
|
|
|
|
create_job_with_artifacts(artifacts: {
|
|
|
|
expose_as: 'Exposed artifact',
|
|
|
|
paths: [
|
|
|
|
'ci_artifacts.txt',
|
|
|
|
'other_artifacts_0.1.2/doc_sample.txt',
|
|
|
|
'something-else.html'
|
|
|
|
]
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'finds multiple matches'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with jobs having more than 1 matching exposed artifacts inside a directory' do
|
|
|
|
let!(:job) do
|
|
|
|
create_job_with_artifacts(artifacts: {
|
|
|
|
expose_as: 'Exposed artifact',
|
|
|
|
paths: ['tests_encoding/']
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'finds multiple matches'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with jobs having paths with glob expression' do
|
|
|
|
let!(:job) do
|
|
|
|
create_job_with_artifacts(artifacts: {
|
|
|
|
expose_as: 'Exposed artifact',
|
|
|
|
paths: ['other_artifacts_0.1.2/doc_sample.txt', 'tests_encoding/*.*']
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'finds a single match' # because those with * are ignored
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'limiting results' do
|
|
|
|
let!(:job1) do
|
|
|
|
create_job_with_artifacts(artifacts: {
|
|
|
|
expose_as: 'artifact 1',
|
|
|
|
paths: ['ci_artifacts.txt']
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:job2) do
|
|
|
|
create_job_with_artifacts(artifacts: {
|
|
|
|
expose_as: 'artifact 2',
|
|
|
|
paths: ['tests_encoding/']
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:job3) do
|
|
|
|
create_job_with_artifacts(artifacts: {
|
|
|
|
expose_as: 'should not be exposed',
|
|
|
|
paths: ['other_artifacts_0.1.2/doc_sample.txt']
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
subject { described_class.new(project, user).for_pipeline(pipeline, limit: 2) }
|
|
|
|
|
|
|
|
it 'returns first 2 results' do
|
|
|
|
expect(subject).to eq([
|
|
|
|
{
|
|
|
|
text: 'artifact 1',
|
|
|
|
url: file_project_job_artifacts_path(project, job1, 'ci_artifacts.txt'),
|
|
|
|
job_name: job1.name,
|
|
|
|
job_path: project_job_path(project, job1)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text: 'artifact 2',
|
|
|
|
url: browse_project_job_artifacts_path(project, job2),
|
|
|
|
job_name: job2.name,
|
|
|
|
job_path: project_job_path(project, job2)
|
|
|
|
}
|
|
|
|
])
|
|
|
|
end
|
|
|
|
end
|
2020-03-05 15:07:52 +00:00
|
|
|
|
|
|
|
context 'cross-project MR' do
|
|
|
|
let!(:foreign_project) { create(:project) }
|
|
|
|
let!(:pipeline) { create(:ci_pipeline, project: foreign_project) }
|
|
|
|
|
|
|
|
let!(:job_show) do
|
|
|
|
create_job_with_artifacts({
|
|
|
|
artifacts: {
|
|
|
|
expose_as: 'file artifact',
|
|
|
|
paths: ['ci_artifacts.txt']
|
|
|
|
}
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:job_browse) do
|
|
|
|
create_job_with_artifacts({
|
|
|
|
artifacts: {
|
|
|
|
expose_as: 'directory artifact',
|
|
|
|
paths: ['tests_encoding/']
|
|
|
|
}
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
subject { described_class.new(project, user).for_pipeline(pipeline, limit: 2) }
|
|
|
|
|
|
|
|
it 'returns the correct path for cross-project MRs' do
|
|
|
|
expect(subject).to eq([
|
|
|
|
{
|
|
|
|
text: 'file artifact',
|
|
|
|
url: file_project_job_artifacts_path(foreign_project, job_show, 'ci_artifacts.txt'),
|
|
|
|
job_name: job_show.name,
|
|
|
|
job_path: project_job_path(foreign_project, job_show)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text: 'directory artifact',
|
|
|
|
url: browse_project_job_artifacts_path(foreign_project, job_browse),
|
|
|
|
job_name: job_browse.name,
|
|
|
|
job_path: project_job_path(foreign_project, job_browse)
|
|
|
|
}
|
|
|
|
])
|
|
|
|
end
|
|
|
|
end
|
2019-10-21 15:05:58 +00:00
|
|
|
end
|
|
|
|
end
|