diff options
Diffstat (limited to 'spec/lib/gitlab/background_migration/populate_finding_uuid_for_vulnerability_feedback_spec.rb')
-rw-r--r-- | spec/lib/gitlab/background_migration/populate_finding_uuid_for_vulnerability_feedback_spec.rb | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/spec/lib/gitlab/background_migration/populate_finding_uuid_for_vulnerability_feedback_spec.rb b/spec/lib/gitlab/background_migration/populate_finding_uuid_for_vulnerability_feedback_spec.rb new file mode 100644 index 00000000000..8e74935e127 --- /dev/null +++ b/spec/lib/gitlab/background_migration/populate_finding_uuid_for_vulnerability_feedback_spec.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::PopulateFindingUuidForVulnerabilityFeedback, schema: 20201211090634 do + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:users) { table(:users) } + let(:scanners) { table(:vulnerability_scanners) } + let(:identifiers) { table(:vulnerability_identifiers) } + let(:findings) { table(:vulnerability_occurrences) } + let(:vulnerability_feedback) { table(:vulnerability_feedback) } + + let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') } + let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo') } + let(:user) { users.create!(username: 'john.doe', projects_limit: 5) } + let(:scanner) { scanners.create!(project_id: project.id, external_id: 'foo', name: 'bar') } + let(:identifier) { identifiers.create!(project_id: project.id, fingerprint: 'foo', external_type: 'bar', external_id: 'zoo', name: 'baz') } + let(:sast_report) { 0 } + let(:dependency_scanning_report) { 1 } + let(:dast_report) { 3 } + let(:secret_detection_report) { 4 } + let(:project_fingerprint) { Digest::SHA1.hexdigest(SecureRandom.uuid) } + let(:location_fingerprint_1) { Digest::SHA1.hexdigest(SecureRandom.uuid) } + let(:location_fingerprint_2) { Digest::SHA1.hexdigest(SecureRandom.uuid) } + let(:location_fingerprint_3) { Digest::SHA1.hexdigest(SecureRandom.uuid) } + let(:finding_1) { finding_creator.call(sast_report, location_fingerprint_1) } + let(:finding_2) { finding_creator.call(dast_report, location_fingerprint_2) } + let(:finding_3) { finding_creator.call(secret_detection_report, location_fingerprint_3) } + let(:uuid_1_components) { ['sast', identifier.fingerprint, location_fingerprint_1, project.id].join('-') } + let(:uuid_2_components) { ['dast', identifier.fingerprint, location_fingerprint_2, project.id].join('-') } + let(:uuid_3_components) { ['secret_detection', identifier.fingerprint, location_fingerprint_3, project.id].join('-') } + let(:expected_uuid_1) { Gitlab::UUID.v5(uuid_1_components) } + let(:expected_uuid_2) { Gitlab::UUID.v5(uuid_2_components) } + let(:expected_uuid_3) { Gitlab::UUID.v5(uuid_3_components) } + let(:finding_creator) do + -> (report_type, location_fingerprint) do + findings.create!( + project_id: project.id, + primary_identifier_id: identifier.id, + scanner_id: scanner.id, + report_type: report_type, + uuid: SecureRandom.uuid, + name: 'Foo', + location_fingerprint: Gitlab::Database::ShaAttribute.serialize(location_fingerprint), + project_fingerprint: Gitlab::Database::ShaAttribute.serialize(project_fingerprint), + metadata_version: '1', + severity: 0, + confidence: 5, + raw_metadata: '{}' + ) + end + end + + let(:feedback_creator) do + -> (category, project_fingerprint) do + vulnerability_feedback.create!( + project_id: project.id, + author_id: user.id, + feedback_type: 0, + category: category, + project_fingerprint: project_fingerprint + ) + end + end + + let!(:feedback_1) { feedback_creator.call(finding_1.report_type, project_fingerprint) } + let!(:feedback_2) { feedback_creator.call(finding_2.report_type, project_fingerprint) } + let!(:feedback_3) { feedback_creator.call(finding_3.report_type, project_fingerprint) } + let!(:feedback_4) { feedback_creator.call(finding_1.report_type, 'foo') } + let!(:feedback_5) { feedback_creator.call(dependency_scanning_report, project_fingerprint) } + + subject(:populate_finding_uuids) { described_class.new.perform(feedback_1.id, feedback_5.id) } + + before do + allow(Gitlab::BackgroundMigration::Logger).to receive(:info) + end + + describe '#perform' do + it 'updates the `finding_uuid` attributes of the feedback records' do + expect { populate_finding_uuids }.to change { feedback_1.reload.finding_uuid }.from(nil).to(expected_uuid_1) + .and change { feedback_2.reload.finding_uuid }.from(nil).to(expected_uuid_2) + .and change { feedback_3.reload.finding_uuid }.from(nil).to(expected_uuid_3) + .and not_change { feedback_4.reload.finding_uuid } + .and not_change { feedback_5.reload.finding_uuid } + + expect(Gitlab::BackgroundMigration::Logger).to have_received(:info).once + end + + it 'preloads the finding and identifier records to prevent N+1 queries' do + # Load feedback records(1), load findings(2), load identifiers(3) and finally update feedback records one by one(6) + expect { populate_finding_uuids }.not_to exceed_query_limit(6) + end + + context 'when setting the `finding_uuid` attribute of a feedback record fails' do + let(:expected_error) { RuntimeError.new } + + before do + allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception) + + allow_next_found_instance_of(described_class::VulnerabilityFeedback) do |feedback| + allow(feedback).to receive(:update_column).and_raise(expected_error) + end + end + + it 'captures the errors and does not crash entirely' do + expect { populate_finding_uuids }.not_to raise_error + + expect(Gitlab::ErrorTracking).to have_received(:track_and_raise_for_dev_exception).with(expected_error).exactly(3).times + end + end + end +end |