diff options
Diffstat (limited to 'spec/lib/gitlab/metrics/dashboard/validator')
4 files changed, 262 insertions, 0 deletions
diff --git a/spec/lib/gitlab/metrics/dashboard/validator/client_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator/client_spec.rb new file mode 100644 index 00000000000..4b07f9dbbab --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/validator/client_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Dashboard::Validator::Client do + include MetricsDashboardHelpers + + let_it_be(:schema_path) { 'lib/gitlab/metrics/dashboard/validator/schemas/dashboard.json' } + + subject { described_class.new(dashboard, schema_path) } + + describe '#execute' do + context 'with no validation errors' do + let(:dashboard) { load_sample_dashboard } + + it 'returns empty array' do + expect(subject.execute).to eq([]) + end + end + + context 'with validation errors' do + let(:dashboard) { load_dashboard_yaml(fixture_file('lib/gitlab/metrics/dashboard/invalid_dashboard.yml')) } + + it 'returns array of error objects' do + expect(subject.execute).to include(Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError) + end + end + end +end diff --git a/spec/lib/gitlab/metrics/dashboard/validator/custom_formats_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator/custom_formats_spec.rb new file mode 100644 index 00000000000..129fb631f3e --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/validator/custom_formats_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Dashboard::Validator::CustomFormats do + describe '#format_handlers' do + describe 'add_to_metric_id_cache' do + it 'adds data to metric id cache' do + subject.format_handlers['add_to_metric_id_cache'].call('metric_id', '_schema') + + expect(subject.metric_ids_cache).to eq(["metric_id"]) + end + end + end +end diff --git a/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb new file mode 100644 index 00000000000..f0db1bd0d33 --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb @@ -0,0 +1,140 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do + describe Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError do + context 'empty error hash' do + let(:error_hash) { {} } + + it 'uses default error message' do + expect(described_class.new(error_hash).message).to eq('Dashboard failed schema validation') + end + end + + context 'formatted message' do + subject { described_class.new(error_hash).message } + + let(:error_hash) do + { + 'data' => 'property_name', + 'data_pointer' => pointer, + 'type' => type, + 'schema' => 'schema', + 'details' => details + } + end + + context 'for root object' do + let(:pointer) { '' } + + context 'when required keys are missing' do + let(:type) { 'required' } + let(:details) { { 'missing_keys' => ['one'] } } + + it { is_expected.to eq 'root is missing required keys: one' } + end + end + + context 'for nested object' do + let(:pointer) { '/nested_objects/0' } + + context 'when required keys are missing' do + let(:type) { 'required' } + let(:details) { { 'missing_keys' => ['two'] } } + + it { is_expected.to eq '/nested_objects/0 is missing required keys: two' } + end + + context 'when there is type mismatch' do + %w(null string boolean integer number array object).each do |expected_type| + context "on type: #{expected_type}" do + let(:type) { expected_type } + let(:details) { nil } + + subject { described_class.new(error_hash).message } + + it { is_expected.to eq "'property_name' at /nested_objects/0 is not of type: #{expected_type}" } + end + end + end + + context 'when data does not match pattern' do + let(:type) { 'pattern' } + let(:error_hash) do + { + 'data' => 'property_name', + 'data_pointer' => pointer, + 'type' => type, + 'schema' => { 'pattern' => 'aa.*' } + } + end + + it { is_expected.to eq "'property_name' at /nested_objects/0 does not match pattern: aa.*" } + end + + context 'when data does not match format' do + let(:type) { 'format' } + let(:error_hash) do + { + 'data' => 'property_name', + 'data_pointer' => pointer, + 'type' => type, + 'schema' => { 'format' => 'date-time' } + } + end + + it { is_expected.to eq "'property_name' at /nested_objects/0 does not match format: date-time" } + end + + context 'when data is not const' do + let(:type) { 'const' } + let(:error_hash) do + { + 'data' => 'property_name', + 'data_pointer' => pointer, + 'type' => type, + 'schema' => { 'const' => 'one' } + } + end + + it { is_expected.to eq "'property_name' at /nested_objects/0 is not: \"one\"" } + end + + context 'when data is not included in enum' do + let(:type) { 'enum' } + let(:error_hash) do + { + 'data' => 'property_name', + 'data_pointer' => pointer, + 'type' => type, + 'schema' => { 'enum' => %w(one two) } + } + end + + it { is_expected.to eq "'property_name' at /nested_objects/0 is not one of: [\"one\", \"two\"]" } + end + + context 'when data is not included in enum' do + let(:type) { 'unknown' } + let(:error_hash) do + { + 'data' => 'property_name', + 'data_pointer' => pointer, + 'type' => type, + 'schema' => 'schema' + } + end + + it { is_expected.to eq "'property_name' at /nested_objects/0 is invalid: error_type=unknown" } + end + end + end + end + + describe Gitlab::Metrics::Dashboard::Validator::Errors::DuplicateMetricIds do + it 'has custom error message' do + expect(described_class.new.message).to eq('metric_id must be unique across a project') + end + end +end diff --git a/spec/lib/gitlab/metrics/dashboard/validator/post_schema_validator_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator/post_schema_validator_spec.rb new file mode 100644 index 00000000000..e7cb1429ca9 --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/validator/post_schema_validator_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Dashboard::Validator::PostSchemaValidator do + describe '#validate' do + context 'with no project and dashboard_path provided' do + context 'unique local metric_ids' do + it 'returns empty array' do + expect(described_class.new(metric_ids: [1, 2, 3]).validate).to eq([]) + end + end + + context 'duplicate local metrics_ids' do + it 'returns error' do + expect(described_class.new(metric_ids: [1, 1]).validate) + .to eq([Gitlab::Metrics::Dashboard::Validator::Errors::DuplicateMetricIds]) + end + end + end + + context 'with project and dashboard_path' do + let(:project) { create(:project) } + + subject do + described_class.new( + project: project, + metric_ids: ['some_identifier'], + dashboard_path: 'test/path.yml' + ).validate + end + + context 'with unique metric identifiers' do + before do + create(:prometheus_metric, + project: project, + identifier: 'some_other_identifier', + dashboard_path: 'test/path.yml' + ) + end + + it 'returns empty array' do + expect(subject).to eq([]) + end + end + + context 'duplicate metric identifiers in database' do + context 'with different dashboard_path' do + before do + create(:prometheus_metric, + project: project, + identifier: 'some_identifier', + dashboard_path: 'some/other/path.yml' + ) + end + + it 'returns error' do + expect(subject).to include(Gitlab::Metrics::Dashboard::Validator::Errors::DuplicateMetricIds) + end + end + + context 'with same dashboard_path' do + before do + create(:prometheus_metric, + project: project, + identifier: 'some_identifier', + dashboard_path: 'test/path.yml' + ) + end + + it 'returns empty array' do + expect(subject).to eq([]) + end + end + end + end + end +end |