diff options
Diffstat (limited to 'spec/lib/gitlab/audit/type/definition_spec.rb')
-rw-r--r-- | spec/lib/gitlab/audit/type/definition_spec.rb | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/spec/lib/gitlab/audit/type/definition_spec.rb b/spec/lib/gitlab/audit/type/definition_spec.rb new file mode 100644 index 00000000000..9f4282a4ec0 --- /dev/null +++ b/spec/lib/gitlab/audit/type/definition_spec.rb @@ -0,0 +1,219 @@ +# 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 |