Commit 28d76dd5 authored by can eldem's avatar can eldem

Merge security report compare services

Instead of using different class for different analyzers
combine them into one since they all utilize same classes
parent 25c266df
......@@ -1374,9 +1374,9 @@ class MergeRequest < ApplicationRecord
# TODO: consider renaming this as with exposed artifacts we generate reports,
# not always compare
# issue: https://gitlab.com/gitlab-org/gitlab/issues/34224
def compare_reports(service_class, current_user = nil)
with_reactive_cache(service_class.name, current_user&.id) do |data|
unless service_class.new(project, current_user, id: id)
def compare_reports(service_class, current_user = nil, report_type = nil )
with_reactive_cache(service_class.name, current_user&.id, report_type) do |data|
unless service_class.new(project, current_user, id: id, report_type: report_type)
.latest?(base_pipeline, actual_head_pipeline, data)
raise InvalidateReactiveCache
end
......@@ -1385,7 +1385,7 @@ class MergeRequest < ApplicationRecord
end || { status: :parsing }
end
def calculate_reactive_cache(identifier, current_user_id = nil, *args)
def calculate_reactive_cache(identifier, current_user_id = nil, report_type = nil, *args)
service_class = identifier.constantize
# TODO: the type check should change to something that includes exposed artifacts service
......@@ -1393,7 +1393,7 @@ class MergeRequest < ApplicationRecord
raise NameError, service_class unless service_class < Ci::CompareReportsBaseService
current_user = User.find_by(id: current_user_id)
service_class.new(project, current_user, id: id).execute(base_pipeline, actual_head_pipeline)
service_class.new(project, current_user, id: id, report_type: report_type).execute(base_pipeline, actual_head_pipeline)
end
def all_commits
......
......@@ -161,7 +161,7 @@ module EE
def compare_dependency_scanning_reports(current_user)
return missing_report_error("dependency scanning") unless has_dependency_scanning_reports?
compare_reports(::Ci::CompareDependencyScanningReportsService, current_user)
compare_reports(::Ci::CompareSecurityReportsService, current_user, 'dependency_scanning')
end
def has_license_scanning_reports?
......@@ -175,7 +175,7 @@ module EE
def compare_container_scanning_reports(current_user)
return missing_report_error("container scanning") unless has_container_scanning_reports?
compare_reports(::Ci::CompareContainerScanningReportsService, current_user)
compare_reports(::Ci::CompareSecurityReportsService, current_user, 'container_scanning')
end
def has_sast_reports?
......@@ -189,13 +189,13 @@ module EE
def compare_sast_reports(current_user)
return missing_report_error("SAST") unless has_sast_reports?
compare_reports(::Ci::CompareSastReportsService, current_user)
compare_reports(::Ci::CompareSecurityReportsService, current_user, 'sast')
end
def compare_secret_detection_reports(current_user)
return missing_report_error("secret detection") unless has_secret_detection_reports?
compare_reports(::Ci::CompareSecretDetectionReportsService, current_user)
compare_reports(::Ci::CompareSecurityReportsService, current_user, 'secret_detection')
end
def has_dast_reports?
......@@ -205,7 +205,7 @@ module EE
def compare_dast_reports(current_user)
return missing_report_error("DAST") unless has_dast_reports?
compare_reports(::Ci::CompareDastReportsService, current_user)
compare_reports(::Ci::CompareSecurityReportsService, current_user, 'dast')
end
def compare_license_scanning_reports(current_user)
......
# frozen_string_literal: true
module Ci
class CompareDastReportsService < ::Ci::CompareReportsBaseService
def comparer_class
Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer
end
def serializer_class
Vulnerabilities::FindingDiffSerializer
end
def get_report(pipeline)
Security::PipelineVulnerabilitiesFinder.new(pipeline: pipeline, params: { report_type: %w[dast] }).execute
end
def build_comparer(base_pipeline, head_pipeline)
head_scans = head_pipeline&.security_scans || Security::Scan.none
comparer_class.new(get_report(base_pipeline), get_report(head_pipeline), head_security_scans: head_scans)
end
end
end
# frozen_string_literal: true
module Ci
class CompareDependencyScanningReportsService < ::Ci::CompareReportsBaseService
def comparer_class
Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer
end
def serializer_class
Vulnerabilities::FindingDiffSerializer
end
def get_report(pipeline)
Security::PipelineVulnerabilitiesFinder.new(pipeline: pipeline, params: { report_type: %w[dependency_scanning], scope: 'all' }).execute
end
def build_comparer(base_pipeline, head_pipeline)
head_scans = head_pipeline&.security_scans || Security::Scan.none
comparer_class.new(get_report(base_pipeline), get_report(head_pipeline), head_security_scans: head_scans)
end
end
end
# frozen_string_literal: true
module Ci
class CompareSastReportsService < ::Ci::CompareReportsBaseService
def comparer_class
Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer
end
def serializer_class
Vulnerabilities::FindingDiffSerializer
end
def get_report(pipeline)
Security::PipelineVulnerabilitiesFinder.new(pipeline: pipeline, params: { report_type: %w[sast], scope: 'all' }).execute
end
def build_comparer(base_pipeline, head_pipeline)
head_scans = head_pipeline&.security_scans || Security::Scan.none
comparer_class.new(get_report(base_pipeline), get_report(head_pipeline), head_security_scans: head_scans)
end
end
end
# frozen_string_literal: true
module Ci
class CompareSecretDetectionReportsService < ::Ci::CompareReportsBaseService
def comparer_class
Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer
end
def serializer_class
Vulnerabilities::FindingDiffSerializer
end
def get_report(pipeline)
Security::PipelineVulnerabilitiesFinder.new(pipeline: pipeline, params: { report_type: %w[secret_detection], scope: 'all' }).execute
end
def build_comparer(base_pipeline, head_pipeline)
head_scans = head_pipeline&.security_scans || Security::Scan.none
comparer_class.new(get_report(base_pipeline), get_report(head_pipeline), head_security_scans: head_scans)
end
end
end
# frozen_string_literal: true
module Ci
class CompareContainerScanningReportsService < ::Ci::CompareReportsBaseService
class CompareSecurityReportsService < ::Ci::CompareReportsBaseService
def comparer_class
Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer
end
......@@ -11,7 +11,7 @@ module Ci
end
def get_report(pipeline)
Security::PipelineVulnerabilitiesFinder.new(pipeline: pipeline, params: { report_type: %w[container_scanning], scope: 'all' }).execute
Security::PipelineVulnerabilitiesFinder.new(pipeline: pipeline, params: { report_type: [params[:report_type]], scope: 'all' }).execute
end
def build_comparer(base_pipeline, head_pipeline)
......
......@@ -432,7 +432,7 @@ RSpec.describe Projects::MergeRequestsController do
before do
allow_any_instance_of(::MergeRequest).to receive(:compare_reports)
.with(::Ci::CompareDependencyScanningReportsService, project.users.first).and_return(comparison_status)
.with(::Ci::CompareSecurityReportsService, project.users.first, 'dependency_scanning').and_return(comparison_status)
end
context 'when comparison is being processed' do
......@@ -502,7 +502,7 @@ RSpec.describe Projects::MergeRequestsController do
before do
allow_any_instance_of(::MergeRequest).to receive(:compare_reports)
.with(::Ci::CompareContainerScanningReportsService, project.users.first).and_return(comparison_status)
.with(::Ci::CompareSecurityReportsService, project.users.first, 'container_scanning').and_return(comparison_status)
end
context 'when comparison is being processed' do
......@@ -572,7 +572,7 @@ RSpec.describe Projects::MergeRequestsController do
before do
allow_any_instance_of(::MergeRequest).to receive(:compare_reports)
.with(::Ci::CompareSastReportsService, project.users.first).and_return(comparison_status)
.with(::Ci::CompareSecurityReportsService, project.users.first, 'sast').and_return(comparison_status)
end
context 'when comparison is being processed' do
......@@ -643,7 +643,7 @@ RSpec.describe Projects::MergeRequestsController do
before do
allow_any_instance_of(::MergeRequest).to receive(:compare_reports)
.with(::Ci::CompareSecretDetectionReportsService, project.users.first).and_return(comparison_status)
.with(::Ci::CompareSecurityReportsService, project.users.first, 'secret_detection').and_return(comparison_status)
end
context 'when comparison is being processed' do
......@@ -713,7 +713,7 @@ RSpec.describe Projects::MergeRequestsController do
before do
allow_any_instance_of(::MergeRequest).to receive(:compare_reports)
.with(::Ci::CompareDastReportsService, project.users.first).and_return(comparison_status)
.with(::Ci::CompareSecurityReportsService, project.users.first, 'dast').and_return(comparison_status)
end
context 'when comparison is being processed' do
......
......@@ -288,7 +288,7 @@ RSpec.describe MergeRequest do
subject { merge_request.calculate_reactive_cache(service_class_name, current_user&.id) }
context 'when given a known service class name' do
let(:service_class_name) { 'Ci::CompareDependencyScanningReportsService' }
let(:service_class_name) { 'Ci::CompareSecurityReportsService' }
it 'does not raises a NameError exception' do
allow_any_instance_of(service_class_name.constantize).to receive(:execute).and_return(nil)
......@@ -338,7 +338,7 @@ RSpec.describe MergeRequest do
end
it 'returns status and data' do
expect_any_instance_of(Ci::CompareContainerScanningReportsService)
expect_any_instance_of(Ci::CompareSecurityReportsService)
.to receive(:execute).with(base_pipeline, head_pipeline).and_call_original
subject
......@@ -346,7 +346,7 @@ RSpec.describe MergeRequest do
context 'when cached results is not latest' do
before do
allow_any_instance_of(Ci::CompareContainerScanningReportsService)
allow_any_instance_of(Ci::CompareSecurityReportsService)
.to receive(:latest?).and_return(false)
end
......@@ -398,7 +398,7 @@ RSpec.describe MergeRequest do
end
it 'returns status and data' do
expect_any_instance_of(Ci::CompareSecretDetectionReportsService)
expect_any_instance_of(Ci::CompareSecurityReportsService)
.to receive(:execute).with(base_pipeline, head_pipeline).and_call_original
subject
......@@ -406,7 +406,7 @@ RSpec.describe MergeRequest do
context 'when cached results is not latest' do
before do
allow_any_instance_of(Ci::CompareSecretDetectionReportsService)
allow_any_instance_of(Ci::CompareSecurityReportsService)
.to receive(:latest?).and_return(false)
end
......@@ -458,7 +458,7 @@ RSpec.describe MergeRequest do
end
it 'returns status and data' do
expect_any_instance_of(Ci::CompareSastReportsService)
expect_any_instance_of(Ci::CompareSecurityReportsService)
.to receive(:execute).with(base_pipeline, head_pipeline).and_call_original
subject
......@@ -466,7 +466,7 @@ RSpec.describe MergeRequest do
context 'when cached results is not latest' do
before do
allow_any_instance_of(Ci::CompareSastReportsService)
allow_any_instance_of(Ci::CompareSecurityReportsService)
.to receive(:latest?).and_return(false)
end
......
# frozen_string_literal: true
require 'spec_helper'
describe Ci::CompareContainerScanningReportsService do
let_it_be(:project) { create(:project, :repository) }
let(:current_user) { build(:user, :admin) }
let(:service) { described_class.new(project, current_user) }
before do
stub_licensed_features(container_scanning: true)
end
describe '#execute' do
subject { service.execute(base_pipeline, head_pipeline) }
context 'when head pipeline has container scanning reports' do
let!(:base_pipeline) { create(:ee_ci_pipeline) }
let!(:head_pipeline) { create(:ee_ci_pipeline, :with_container_scanning_report, project: project) }
it 'reports new, existing and fixed vulnerabilities' do
expect(subject[:status]).to eq(:parsed)
expect(subject[:data]['added'].count).to eq(8)
expect(subject[:data]['existing'].count).to eq(0)
expect(subject[:data]['fixed'].count).to eq(0)
end
end
context 'when base and head pipelines have container scanning reports' do
let!(:base_pipeline) { create(:ee_ci_pipeline, :with_container_scanning_report, project: project) }
let!(:head_pipeline) { create(:ee_ci_pipeline, :with_container_scanning_feature_branch, project: project) }
it 'reports status as parsed' do
expect(subject[:status]).to eq(:parsed)
end
it 'populates fields based on current_user' do
payload = subject[:data]['added'].first
expect(payload['create_vulnerability_feedback_issue_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_merge_request_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_dismissal_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_issue_path']).not_to be_empty
expect(service.current_user).to eq(current_user)
end
it 'reports new vulnerability' do
expect(subject[:data]['added'].count).to eq(1)
expect(subject[:data]['added'].first['identifiers']).to include(a_hash_including('external_id' => 'CVE-2017-15650'))
end
it 'reports existing container vulnerabilities' do
expect(subject[:data]['existing'].count).to eq(0)
end
it 'reports fixed container scanning vulnerabilities' do
expect(subject[:data]['fixed'].count).to eq(8)
compare_keys = subject[:data]['fixed'].map { |t| t['identifiers'].first['external_id'] }
expected_keys = %w(CVE-2017-16997 CVE-2017-18269 CVE-2018-1000001 CVE-2016-10228 CVE-2010-4052 CVE-2018-18520 CVE-2018-16869 CVE-2018-18311)
expect(compare_keys - expected_keys).to eq([])
end
end
end
describe "#build_comparer" do
context "when the head_pipeline is nil" do
subject { service.build_comparer(base_pipeline, nil) }
let(:base_pipeline) { create(:ee_ci_pipeline) }
specify { expect { subject }.not_to raise_error }
specify { expect(subject.scans).to be_empty }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Ci::CompareDastReportsService do
let_it_be(:project) { create(:project, :repository) }
let(:current_user) { build(:user, :admin) }
let(:service) { described_class.new(project, current_user) }
before do
stub_licensed_features(container_scanning: true, dast: true)
end
describe '#execute' do
subject { service.execute(base_pipeline, head_pipeline) }
context 'when head pipeline has DAST reports containing some vulnerabilities' do
let!(:base_pipeline) { create(:ee_ci_pipeline) }
let!(:head_pipeline) { create(:ee_ci_pipeline, :with_dast_report, project: project) }
it 'reports the new vulnerabilities, while not changing the counts of existing and fixed vulnerabilities' do
expect(subject[:status]).to eq(:parsed)
expect(subject[:data]['added'].count).to eq(20)
expect(subject[:data]['existing'].count).to eq(0)
expect(subject[:data]['fixed'].count).to eq(0)
end
end
context 'when base and head pipelines have DAST reports containing vulnerabilities' do
let!(:base_pipeline) { create(:ee_ci_pipeline, :with_dast_report, project: project) }
let!(:head_pipeline) { create(:ee_ci_pipeline, :with_dast_feature_branch, project: project) }
it 'reports status as parsed' do
expect(subject[:status]).to eq(:parsed)
end
it 'populates fields based on current_user' do
payload = subject[:data]['fixed'].first
expect(payload).not_to be_empty
expect(payload['create_vulnerability_feedback_issue_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_merge_request_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_dismissal_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_issue_path']).not_to be_empty
expect(service.current_user).to eq(current_user)
end
it 'reports new vulnerability' do
expect(subject[:data]['added'].count).to eq(1)
expect(subject[:data]['added'].last['identifiers']).to include(a_hash_including('name' => 'CWE-201'))
end
it 'reports existing DAST vulnerabilities' do
expect(subject[:data]['existing'].count).to eq(1)
expect(subject[:data]['existing'].last['identifiers']).to include(a_hash_including('name' => 'CWE-120'))
end
it 'reports fixed DAST vulnerabilities' do
expect(subject[:data]['fixed'].count).to eq(19)
expect(subject[:data]['fixed']).to include(
a_hash_including(
{
'identifiers' => a_collection_including(
a_hash_including(
"name" => "CWE-352"
)
)
})
)
end
end
describe "#build_comparer" do
context "when the head_pipeline is nil" do
subject { service.build_comparer(base_pipeline, nil) }
let(:base_pipeline) { create(:ee_ci_pipeline) }
specify { expect { subject }.not_to raise_error }
specify { expect(subject.scans).to be_empty }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Ci::CompareDependencyScanningReportsService do
let_it_be(:project) { create(:project, :repository) }
let(:current_user) { build(:user, :admin) }
let(:service) { described_class.new(project, current_user) }
before do
stub_licensed_features(dependency_scanning: true)
end
describe '#execute' do
subject { service.execute(base_pipeline, head_pipeline) }
context 'when head pipeline has dependency scanning reports' do
let!(:base_pipeline) { create(:ee_ci_pipeline) }
let!(:head_pipeline) { create(:ee_ci_pipeline, :with_dependency_scanning_report, project: project) }
it 'reports new vulnerabilities' do
expect(subject[:status]).to eq(:parsed)
expect(subject[:data]['added'].count).to eq(4)
expect(subject[:data]['existing'].count).to eq(0)
expect(subject[:data]['fixed'].count).to eq(0)
end
end
context 'when base and head pipelines have dependency scanning reports' do
let!(:base_pipeline) { create(:ee_ci_pipeline, :with_dependency_scanning_report, project: project) }
let!(:head_pipeline) { create(:ee_ci_pipeline, :with_dependency_scanning_feature_branch, project: project) }
it 'reports status as parsed' do
expect(subject[:status]).to eq(:parsed)
end
it 'populates fields based on current_user' do
payload = subject[:data]['existing'].first
expect(payload['create_vulnerability_feedback_issue_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_merge_request_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_dismissal_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_issue_path']).not_to be_empty
expect(service.current_user).to eq(current_user)
end
it 'reports fixed vulnerability' do
expect(subject[:data]['added'].count).to eq(1)
expect(subject[:data]['added'].first['identifiers']).to include(a_hash_including('external_id' => 'CVE-2017-5946'))
end
it 'reports existing dependency vulenerabilities' do
expect(subject[:data]['existing'].count).to eq(3)
end
it 'reports fixed dependency scanning vulnerabilities' do
expect(subject[:data]['fixed'].count).to eq(1)
compare_keys = subject[:data]['fixed'].map { |t| t['identifiers'].first['external_id'] }
expected_keys = %w(06565b64-486d-4326-b906-890d9915804d)
expect(compare_keys - expected_keys).to eq([])
end
end
context 'when head pipeline has corrupted dependency scanning vulnerability reports' do
let!(:base_pipeline) { build(:ee_ci_pipeline, :with_corrupted_dependency_scanning_report, project: project) }
let!(:head_pipeline) { build(:ee_ci_pipeline, :with_corrupted_dependency_scanning_report, project: project) }
before do
error = Gitlab::Ci::Parsers::ParserError.new('Exception: JSON parsing failed')
allow(base_pipeline).to receive(:license_scanning_report).and_raise(error)
allow(head_pipeline).to receive(:license_scanning_report).and_raise(error)
end
it 'returns status and error message' do
expect(subject[:status]).to eq(:error)
expect(subject[:status_reason]).to include('JSON parsing failed')
end
it 'returns status and error message when pipeline is nil' do
result = service.execute(nil, head_pipeline)
expect(result[:status]).to eq(:error)
expect(result[:status_reason]).to include('JSON parsing failed')
end
end
describe "#build_comparer" do
context "when the head_pipeline is nil" do
subject { service.build_comparer(base_pipeline, nil) }
let(:base_pipeline) { create(:ee_ci_pipeline) }
specify { expect { subject }.not_to raise_error }
specify { expect(subject.scans).to be_empty }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Ci::CompareSastReportsService do
let_it_be(:project) { create(:project, :repository) }
let(:current_user) { build(:user, :admin) }
let(:service) { described_class.new(project, current_user) }
before do
stub_licensed_features(container_scanning: true)
stub_licensed_features(sast: true)
end
describe '#execute' do
subject { service.execute(base_pipeline, head_pipeline) }
context 'when head pipeline has sast reports' do
let!(:base_pipeline) { create(:ee_ci_pipeline) }
let!(:head_pipeline) { create(:ee_ci_pipeline, :with_sast_report, project: project) }
it 'reports new vulnerabilities' do
expect(subject[:status]).to eq(:parsed)
expect(subject[:data]['added'].count).to eq(33)
expect(subject[:data]['existing'].count).to eq(0)
expect(subject[:data]['fixed'].count).to eq(0)
end
end
context 'when base and head pipelines have sast reports' do
let!(:base_pipeline) { create(:ee_ci_pipeline, :with_sast_report, project: project) }
let!(:head_pipeline) { create(:ee_ci_pipeline, :with_sast_feature_branch, project: project) }
it 'reports status as parsed' do
expect(subject[:status]).to eq(:parsed)
end
it 'populates fields based on current_user' do
payload = subject[:data]['existing'].first
expect(payload['create_vulnerability_feedback_issue_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_merge_request_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_dismissal_path']).not_to be_empty
expect(payload['create_vulnerability_feedback_issue_path']).not_to be_empty
expect(service.current_user).to eq(current_user)
end
it 'reports new vulnerability' do
expect(subject[:data]['added'].count).to eq(1)
expect(subject[:data]['added'].first['identifiers']).to include(a_hash_including('name' => 'CWE-120'))
end
it 'reports existing sast vulnerabilities' do
expect(subject[:data]['existing'].count).to eq(29)
end
it 'reports fixed sast vulnerabilities' do
expect(subject[:data]['fixed'].count).to eq(4)
compare_keys = subject[:data]['fixed'].map { |t| t['identifiers'].first['external_id'] }
expected_keys = %w(char fopen strcpy char)
expect(compare_keys - expected_keys).to eq([])
end
end
describe "#build_comparer" do
context "when the head_pipeline is nil" do
subject { service.build_comparer(base_pipeline, nil) }
let(:base_pipeline) { create(:ee_ci_pipeline) }
specify { expect { subject }.not_to raise_error }
specify { expect(subject.scans).to be_empty }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::CompareSecretDetectionReportsService do
let(:current_user) { build(:user, :admin) }
let(:service) { described_class.new(project, current_user) }
let_it_be(:project) { create(:project, :repository) }
before do
stub_licensed_features(container_scanning: true)
stub_licensed_features(secret_detection: true)
end
describe '#execute' do
subject { service.execute(base_pipeline, head_pipeline) }
context 'when head pipeline has secret_detection reports' do
let!(:base_pipeline) { create(:ee_ci_pipeline) }
let!(:head_pipeline) { create(:ee_ci_pipeline, :with_secret_detection_report, project: project) }
it 'reports new vulnerabilities' do
expect(subject[:status]).to eq(:parsed)
expect(subject[:data]['added'].count).to eq(1)
expect(subject[:data]['existing'].count).to eq(0)
expect(subject[:data]['fixed'].count).to eq(0)
end
end
context 'when base and head pipelines have secret_detection reports' do
let!(:base_pipeline) { create(:ee_ci_pipeline, :with_secret_detection_report, project: project) }
let!(:head_pipeline) { create(:ee_ci_pipeline, :with_secret_detection_feature_branch, project: project) }
it 'reports status as parsed' do
expect(subject[:status]).to eq(:parsed)
end
it 'populates fields based on current_user' do
payload = subject[:data]['existing'].first
expect(payload).to be_nil
expect(service.current_user).to eq(current_user)
end
it 'reports new vulnerability' do
expect(subject[:data]['added'].count).to eq(0)
end
it 'reports existing secret_detection vulnerabilities' do
expect(subject[:data]['existing'].count).to eq(0)
end
it 'reports fixed secret_detection vulnerabilities' do
expect(subject[:data]['fixed'].count).to eq(1)
compare_keys = subject[:data]['fixed'].map { |t| t['identifiers'].first['external_id'] }
expected_keys = %w(AWS)
expect(compare_keys - expected_keys).to eq([])
end
end
describe '#build_comparer' do
context 'when the head_pipeline is nil' do
subject { service.build_comparer(base_pipeline, nil) }
let(:base_pipeline) { create(:ee_ci_pipeline) }
specify { expect { subject }.not_to raise_error }
specify { expect(subject.scans).to be_empty }
end
end
end
end
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment