220 lines
6.7 KiB
Ruby
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
|