diff options
Diffstat (limited to 'spec/lib/gitlab/metrics/dashboard')
7 files changed, 390 insertions, 24 deletions
diff --git a/spec/lib/gitlab/metrics/dashboard/importer_spec.rb b/spec/lib/gitlab/metrics/dashboard/importer_spec.rb new file mode 100644 index 00000000000..8b705395a2c --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/importer_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Dashboard::Importer do + include MetricsDashboardHelpers + + let_it_be(:dashboard_path) { '.gitlab/dashboards/sample_dashboard.yml' } + let_it_be(:project) { create(:project) } + + before do + allow(subject).to receive(:dashboard_hash).and_return(dashboard_hash) + end + + subject { described_class.new(dashboard_path, project) } + + describe '.execute' do + context 'valid dashboard hash' do + let(:dashboard_hash) { load_sample_dashboard } + + it 'imports metrics to database' do + expect { subject.execute } + .to change { PrometheusMetric.count }.from(0).to(3) + end + end + + context 'invalid dashboard hash' do + let(:dashboard_hash) { {} } + + it 'returns false' do + expect(subject.execute).to be(false) + end + end + end + + describe '.execute!' do + context 'valid dashboard hash' do + let(:dashboard_hash) { load_sample_dashboard } + + it 'imports metrics to database' do + expect { subject.execute } + .to change { PrometheusMetric.count }.from(0).to(3) + end + end + + context 'invalid dashboard hash' do + let(:dashboard_hash) { {} } + + it 'raises error' do + expect { subject.execute! }.to raise_error(Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError, + 'root is missing required keys: dashboard, panel_groups') + end + end + end +end diff --git a/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb new file mode 100644 index 00000000000..09d5e048f6a --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do + include MetricsDashboardHelpers + + describe '#execute' do + let(:project) { create(:project) } + let(:dashboard_path) { 'path/to/dashboard.yml' } + + subject { described_class.new(dashboard_hash, project: project, dashboard_path: dashboard_path) } + + context 'valid dashboard' do + let(:dashboard_hash) { load_sample_dashboard } + + context 'with all new metrics' do + it 'creates PrometheusMetrics' do + expect { subject.execute }.to change { PrometheusMetric.count }.by(3) + end + end + + context 'with existing metrics' do + let!(:existing_metric) do + create(:prometheus_metric, { + project: project, + identifier: 'metric_b', + title: 'overwrite', + y_label: 'overwrite', + query: 'overwrite', + unit: 'overwrite', + legend: 'overwrite' + }) + end + + it 'updates existing PrometheusMetrics' do + described_class.new(dashboard_hash, project: project, dashboard_path: dashboard_path).execute + + expect(existing_metric.reload.attributes.with_indifferent_access).to include({ + title: 'Super Chart B', + y_label: 'y_label', + query: 'query', + unit: 'unit', + legend: 'Legend Label' + }) + end + + it 'creates new PrometheusMetrics' do + expect { subject.execute }.to change { PrometheusMetric.count }.by(2) + end + + context 'with stale metrics' do + let!(:stale_metric) do + create(:prometheus_metric, + project: project, + identifier: 'stale_metric', + dashboard_path: dashboard_path, + group: 3 + ) + end + + it 'deletes stale metrics' do + subject.execute + + expect { stale_metric.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end + end + + context 'invalid dashboard' do + let(:dashboard_hash) { {} } + + it 'returns false' do + expect(subject.execute).to eq(false) + end + end + end +end diff --git a/spec/lib/gitlab/metrics/dashboard/stages/track_panel_type_spec.rb b/spec/lib/gitlab/metrics/dashboard/stages/track_panel_type_spec.rb index d9987b67127..60010b9f257 100644 --- a/spec/lib/gitlab/metrics/dashboard/stages/track_panel_type_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/stages/track_panel_type_spec.rb @@ -8,20 +8,19 @@ RSpec.describe Gitlab::Metrics::Dashboard::Stages::TrackPanelType do let(:project) { build_stubbed(:project) } let(:environment) { build_stubbed(:environment, project: project) } - describe '#transform!' do + describe '#transform!', :snowplow do subject { described_class.new(project, dashboard, environment: environment) } let(:dashboard) { load_sample_dashboard.deep_symbolize_keys } it 'creates tracking event' do - stub_application_setting(snowplow_enabled: true, snowplow_collector_hostname: 'localhost') - allow(Gitlab::Tracking).to receive(:event).and_call_original - subject.transform! - expect(Gitlab::Tracking).to have_received(:event) - .with('MetricsDashboard::Chart', 'chart_rendered', { label: 'area-chart' }) - .at_least(:once) + expect_snowplow_event( + category: 'MetricsDashboard::Chart', + action: 'chart_rendered', + label: 'area-chart' + ) end end end diff --git a/spec/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics_spec.rb b/spec/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics_spec.rb new file mode 100644 index 00000000000..3af8b51c889 --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics_spec.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Dashboard::Transformers::Yml::V1::PrometheusMetrics do + include MetricsDashboardHelpers + + describe '#execute' do + subject { described_class.new(dashboard_hash) } + + context 'valid dashboard' do + let_it_be(:dashboard_hash) do + { + panel_groups: [{ + panels: [ + { + title: 'Panel 1 title', + y_label: 'Panel 1 y_label', + metrics: [ + { + query_range: 'Panel 1 metric 1 query_range', + unit: 'Panel 1 metric 1 unit', + label: 'Panel 1 metric 1 label', + id: 'Panel 1 metric 1 id' + }, + { + query: 'Panel 1 metric 2 query', + unit: 'Panel 1 metric 2 unit', + label: 'Panel 1 metric 2 label', + id: 'Panel 1 metric 2 id' + } + ] + }, + { + title: 'Panel 2 title', + y_label: 'Panel 2 y_label', + metrics: [{ + query_range: 'Panel 2 metric 1 query_range', + unit: 'Panel 2 metric 1 unit', + label: 'Panel 2 metric 1 label', + id: 'Panel 2 metric 1 id' + }] + } + ] + }] + } + end + + let(:expected_metrics) do + [ + { + title: 'Panel 1 title', + y_label: 'Panel 1 y_label', + query: "Panel 1 metric 1 query_range", + unit: 'Panel 1 metric 1 unit', + legend: 'Panel 1 metric 1 label', + identifier: 'Panel 1 metric 1 id', + group: 3, + common: false + }, + { + title: 'Panel 1 title', + y_label: 'Panel 1 y_label', + query: 'Panel 1 metric 2 query', + unit: 'Panel 1 metric 2 unit', + legend: 'Panel 1 metric 2 label', + identifier: 'Panel 1 metric 2 id', + group: 3, + common: false + }, + { + title: 'Panel 2 title', + y_label: 'Panel 2 y_label', + query: 'Panel 2 metric 1 query_range', + unit: 'Panel 2 metric 1 unit', + legend: 'Panel 2 metric 1 label', + identifier: 'Panel 2 metric 1 id', + group: 3, + common: false + } + ] + end + + it 'returns collection of metrics with correct attributes' do + expect(subject.execute).to match_array(expected_metrics) + end + end + + context 'invalid dashboard' do + let(:dashboard_hash) { {} } + + it 'raises missing attribute error' do + expect { subject.execute }.to raise_error( + ::Gitlab::Metrics::Dashboard::Transformers::Errors::MissingAttribute, "Missing attribute: 'panel_groups'" + ) + end + end + end +end diff --git a/spec/lib/gitlab/metrics/dashboard/url_spec.rb b/spec/lib/gitlab/metrics/dashboard/url_spec.rb index 205e1000376..830d43169a9 100644 --- a/spec/lib/gitlab/metrics/dashboard/url_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/url_spec.rb @@ -6,11 +6,12 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do include Gitlab::Routing.url_helpers describe '#metrics_regex' do + let(:environment_id) { 1 } let(:url_params) do [ 'foo', 'bar', - 1, + environment_id, { start: '2019-08-02T05:43:09.000Z', dashboard: 'config/prometheus/common_metrics.yml', @@ -33,12 +34,42 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do subject { described_class.metrics_regex } - context 'for metrics route' do + context 'for /-/environments/:environment_id/metrics route' do let(:url) { metrics_namespace_project_environment_url(*url_params) } it_behaves_like 'regex which matches url when expected' end + context 'for /-/metrics?environment=:environment_id route' do + let(:url) { namespace_project_metrics_dashboard_url(*url_params) } + let(:url_params) do + [ + 'namespace1', + 'project1', + { + environment: environment_id, + start: '2019-08-02T05:43:09.000Z', + dashboard: 'config/prometheus/common_metrics.yml', + group: 'awesome group', + anchor: 'title' + } + ] + end + + let(:expected_params) do + { + 'url' => url, + 'namespace' => 'namespace1', + 'project' => 'project1', + 'environment' => "#{environment_id}", + 'query' => "?dashboard=config%2Fprometheus%2Fcommon_metrics.yml&environment=#{environment_id}&group=awesome+group&start=2019-08-02T05%3A43%3A09.000Z", + 'anchor' => '#title' + } + end + + it_behaves_like 'regex which matches url when expected' + end + context 'for metrics_dashboard route' do let(:url) { metrics_dashboard_namespace_project_environment_url(*url_params) } @@ -47,16 +78,19 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do end describe '#clusters_regex' do - let(:url) do - Gitlab::Routing.url_helpers.namespace_project_cluster_url( + let(:url) { Gitlab::Routing.url_helpers.namespace_project_cluster_url(*url_params) } + let(:url_params) do + [ 'foo', 'bar', '1', - group: 'Cluster Health', - title: 'Memory Usage', - y_label: 'Memory 20(GiB)', - anchor: 'title' - ) + { + group: 'Cluster Health', + title: 'Memory Usage', + y_label: 'Memory 20(GiB)', + anchor: 'title' + } + ] end let(:expected_params) do @@ -73,6 +107,27 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do subject { described_class.clusters_regex } it_behaves_like 'regex which matches url when expected' + + context 'for metrics_dashboard route' do + let(:url) do + metrics_dashboard_namespace_project_cluster_url( + *url_params, cluster_type: :project, embedded: true, format: :json + ) + end + + let(:expected_params) do + { + 'url' => url, + 'namespace' => 'foo', + 'project' => 'bar', + 'cluster_id' => '1', + 'query' => '?cluster_type=project&embedded=true', + 'anchor' => nil + } + end + + it_behaves_like 'regex which matches url when expected' + end end describe '#grafana_regex' do @@ -103,15 +158,18 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do end describe '#alert_regex' do - let(:url) do - Gitlab::Routing.url_helpers.metrics_dashboard_namespace_project_prometheus_alert_url( + let(:url) { Gitlab::Routing.url_helpers.metrics_dashboard_namespace_project_prometheus_alert_url(*url_params) } + let(:url_params) do + [ 'foo', 'bar', '1', - start: '2020-02-10T12:59:49.938Z', - end: '2020-02-10T20:59:49.938Z', - anchor: "anchor" - ) + { + start: '2020-02-10T12:59:49.938Z', + end: '2020-02-10T20:59:49.938Z', + anchor: "anchor" + } + ] end let(:expected_params) do @@ -128,6 +186,21 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do subject { described_class.alert_regex } it_behaves_like 'regex which matches url when expected' + + it_behaves_like 'regex which matches url when expected' do + let(:url) { Gitlab::Routing.url_helpers.metrics_dashboard_namespace_project_prometheus_alert_url(*url_params, format: :json) } + + let(:expected_params) do + { + 'url' => url, + 'namespace' => 'foo', + 'project' => 'bar', + 'alert' => '1', + 'query' => nil, + 'anchor' => nil + } + end + end end describe '#build_dashboard_url' do diff --git a/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb index f0db1bd0d33..fdbba6c31b5 100644 --- a/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb @@ -34,6 +34,17 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do it { is_expected.to eq 'root is missing required keys: one' } 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 } + + it { is_expected.to eq "'property_name' at root is not of type: #{expected_type}" } + end + end + end end context 'for nested object' do @@ -52,8 +63,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors 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 diff --git a/spec/lib/gitlab/metrics/dashboard/validator_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator_spec.rb index c4cda271408..eb67ea2b7da 100644 --- a/spec/lib/gitlab/metrics/dashboard/validator_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/validator_spec.rb @@ -143,4 +143,56 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do end end end + + describe '#errors' do + context 'valid dashboard schema' do + it 'returns no errors' do + expect(described_class.errors(valid_dashboard)).to eq [] + end + + context 'with duplicate metric_ids' do + it 'returns errors' do + expect(described_class.errors(duplicate_id_dashboard)).to eq [Gitlab::Metrics::Dashboard::Validator::Errors::DuplicateMetricIds.new] + end + end + + context 'with dashboard_path and project' do + subject { described_class.errors(valid_dashboard, dashboard_path: 'test/path.yml', project: project) } + + context 'with no conflicting metric identifiers in db' do + it { is_expected.to eq [] } + end + + context 'with metric identifier present in current dashboard' do + before do + create(:prometheus_metric, + identifier: 'metric_a1', + dashboard_path: 'test/path.yml', + project: project + ) + end + + it { is_expected.to eq [] } + end + + context 'with metric identifier present in another dashboard' do + before do + create(:prometheus_metric, + identifier: 'metric_a1', + dashboard_path: 'some/other/dashboard/path.yml', + project: project + ) + end + + it { is_expected.to eq [Gitlab::Metrics::Dashboard::Validator::Errors::DuplicateMetricIds.new] } + end + end + end + + context 'invalid dashboard schema' do + it 'returns collection of validation errors' do + expect(described_class.errors(invalid_dashboard)).to all be_kind_of(Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError) + end + end + end end |