gitlab-org--gitlab-foss/spec/lib/gitlab/audit/type/definition_spec.rb

220 lines
6.7 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Audit::Type::Definition do
let(:attributes) do
{ name: 'group_deploy_token_destroyed',
description: 'Group deploy token is deleted',
introduced_by_issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/1',
introduced_by_mr: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1',
group: 'govern::compliance',
milestone: '15.4',
saved_to_database: true,
streamed: true }
end
let(:path) { File.join('types', 'group_deploy_token_destroyed.yml') }
let(:definition) { described_class.new(path, attributes) }
let(:yaml_content) { attributes.deep_stringify_keys.to_yaml }
describe '#key' do
subject { definition.key }
it 'returns a symbol from name' do
is_expected.to eq(:group_deploy_token_destroyed)
end
end
describe '#validate!', :aggregate_failures do
using RSpec::Parameterized::TableSyntax
# rubocop:disable Layout/LineLength
where(:param, :value, :result) do
:path | 'audit_event/types/invalid.yml' | /Audit event type 'group_deploy_token_destroyed' has an invalid path/
:name | nil | %r{property '/name' is not of type: string}
:description | nil | %r{property '/description' is not of type: string}
:introduced_by_issue | nil | %r{property '/introduced_by_issue' is not of type: string}
:introduced_by_mr | nil | %r{property '/introduced_by_mr' is not of type: string}
:group | nil | %r{property '/group' is not of type: string}
:milestone | nil | %r{property '/milestone' is not of type: string}
end
# rubocop:enable Layout/LineLength
with_them do
let(:params) { attributes.merge(path: path) }
before do
params[param] = value
end
it do
expect do
described_class.new(
params[:path], params.except(:path)
).validate!
end.to raise_error(result)
end
end
context 'when both saved_to_database and streamed are false' do
let(:params) { attributes.merge({ path: path, saved_to_database: false, streamed: false }) }
it 'raises an exception' do
expect do
described_class.new(
params[:path], params.except(:path)
).validate!
end.to raise_error(/root is invalid: error_type=not/)
end
end
end
describe '.paths' do
it 'returns at least one path' do
expect(described_class.paths).not_to be_empty
end
end
describe '.get' do
before do
allow(described_class).to receive(:definitions) do
{ definition.key => definition }
end
end
context 'when audit event type is not defined' do
let(:undefined_audit_event_type) { 'undefined_audit_event_type' }
it 'returns nil' do
expect(described_class.get(undefined_audit_event_type)).to be nil
end
end
context 'when audit event type is defined' do
let(:audit_event_type) { 'group_deploy_token_destroyed' }
it 'returns an instance of Gitlab::Audit::Type::Definition' do
expect(described_class.get(audit_event_type)).to be_an_instance_of(described_class)
end
it 'returns the properties as defined for that audit event type', :aggregate_failures do
audit_event_type_definition = described_class.get(audit_event_type)
expect(audit_event_type_definition.name).to eq "group_deploy_token_destroyed"
expect(audit_event_type_definition.description).to eq "Group deploy token is deleted"
expect(audit_event_type_definition.group).to eq "govern::compliance"
expect(audit_event_type_definition.milestone).to eq "15.4"
expect(audit_event_type_definition.saved_to_database).to be true
expect(audit_event_type_definition.streamed).to be true
end
end
end
describe '.load_from_file' do
it 'properly loads a definition from file' do
expect_file_read(path, content: yaml_content)
expect(described_class.send(:load_from_file, path).attributes)
.to eq(definition.attributes)
end
context 'for missing file' do
let(:path) { 'missing/audit_events/type/file.yml' }
it 'raises exception' do
expect do
described_class.send(:load_from_file, path)
end.to raise_error(/Invalid definition for/)
end
end
context 'for invalid definition' do
it 'raises exception' do
expect_file_read(path, content: '{}')
expect do
described_class.send(:load_from_file, path)
end.to raise_error(%r{property '/name' is not of type: string})
end
end
end
describe '.load_all!' do
let(:store1) { Dir.mktmpdir('path1') }
let(:store2) { Dir.mktmpdir('path2') }
let(:definitions) { {} }
before do
allow(described_class).to receive(:paths).and_return(
[
File.join(store1, '**', '*.yml'),
File.join(store2, '**', '*.yml')
]
)
end
subject { described_class.send(:load_all!) }
after do
FileUtils.rm_rf(store1)
FileUtils.rm_rf(store2)
end
it "when there are no audit event types a list of definitions is empty" do
is_expected.to be_empty
end
it "when there's a single audit event type it properly loads them" do
write_audit_event_type(store1, path, yaml_content)
is_expected.to be_one
end
it "when the same audit event type is stored multiple times raises exception" do
write_audit_event_type(store1, path, yaml_content)
write_audit_event_type(store2, path, yaml_content)
expect { subject }
.to raise_error(/Audit event type 'group_deploy_token_destroyed' is already defined/)
end
it "when one of the YAMLs is invalid it does raise exception" do
write_audit_event_type(store1, path, '{}')
expect { subject }.to raise_error(/Invalid definition for .* '' must match the filename/)
end
end
describe '.definitions' do
let(:store1) { Dir.mktmpdir('path1') }
before do
allow(described_class).to receive(:paths).and_return(
[
File.join(store1, '**', '*.yml')
]
)
end
subject { described_class.definitions }
after do
FileUtils.rm_rf(store1)
end
it "loads the definitions for all the audit event types" do
write_audit_event_type(store1, path, yaml_content)
is_expected.to be_one
end
end
def write_audit_event_type(store, path, content)
path = File.join(store, path)
dir = File.dirname(path)
FileUtils.mkdir_p(dir)
File.write(path, content)
end
end