diff options
Diffstat (limited to 'spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb')
-rw-r--r-- | spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb | 228 |
1 files changed, 69 insertions, 159 deletions
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb index b8eddc0ca7f..b4894ec049f 100644 --- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb +++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb @@ -27,6 +27,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s 'deploy_token_packages', 'user_packages', 'compliance', + 'ecosystem', 'analytics', 'ide_edit', 'search', @@ -39,12 +40,16 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s 'snippets', 'code_review', 'terraform', - 'ci_templates' + 'ci_templates', + 'quickactions', + 'pipeline_authoring' ) end end describe 'known_events' do + let(:feature) { 'test_hll_redis_counter_ff_check' } + let(:weekly_event) { 'g_analytics_contribution' } let(:daily_event) { 'g_analytics_search' } let(:analytics_slot_event) { 'g_analytics_contribution' } @@ -64,7 +69,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s let(:known_events) do [ - { name: weekly_event, redis_slot: "analytics", category: analytics_category, expiry: 84, aggregation: "weekly" }, + { name: weekly_event, redis_slot: "analytics", category: analytics_category, expiry: 84, aggregation: "weekly", feature_flag: feature }, { name: daily_event, redis_slot: "analytics", category: analytics_category, expiry: 84, aggregation: "daily" }, { name: category_productivity_event, redis_slot: "analytics", category: productivity_category, aggregation: "weekly" }, { name: compliance_slot_event, redis_slot: "compliance", category: compliance_category, aggregation: "weekly" }, @@ -75,6 +80,8 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s end before do + skip_feature_flags_yaml_validation + skip_default_enabled_yaml_check allow(described_class).to receive(:known_events).and_return(known_events) end @@ -85,6 +92,32 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s end describe '.track_event' do + context 'with feature flag set' do + it 'tracks the event when feature enabled' do + stub_feature_flags(feature => true) + + expect(Gitlab::Redis::HLL).to receive(:add) + + described_class.track_event(weekly_event, values: 1) + end + + it 'does not track the event with feature flag disabled' do + stub_feature_flags(feature => false) + + expect(Gitlab::Redis::HLL).not_to receive(:add) + + described_class.track_event(weekly_event, values: 1) + end + end + + context 'with no feature flag set' do + it 'tracks the event' do + expect(Gitlab::Redis::HLL).to receive(:add) + + described_class.track_event(daily_event, values: 1) + end + end + context 'when usage_ping is disabled' do it 'does not track the event' do stub_application_setting(usage_ping_enabled: false) @@ -425,182 +458,59 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s end end - context 'aggregated_metrics_data' do + describe '.calculate_events_union' do + let(:time_range) { { start_date: 7.days.ago, end_date: DateTime.current } } let(:known_events) do [ { name: 'event1_slot', redis_slot: "slot", category: 'category1', aggregation: "weekly" }, { name: 'event2_slot', redis_slot: "slot", category: 'category2', aggregation: "weekly" }, { name: 'event3_slot', redis_slot: "slot", category: 'category3', aggregation: "weekly" }, - { name: 'event5_slot', redis_slot: "slot", category: 'category4', aggregation: "weekly" }, + { name: 'event5_slot', redis_slot: "slot", category: 'category4', aggregation: "daily" }, { name: 'event4', category: 'category2', aggregation: "weekly" } ].map(&:with_indifferent_access) end before do allow(described_class).to receive(:known_events).and_return(known_events) - end - - shared_examples 'aggregated_metrics_data' do - context 'no aggregated metrics is defined' do - it 'returns empty hash' do - allow(described_class).to receive(:aggregated_metrics).and_return([]) - - expect(aggregated_metrics_data).to eq({}) - end - end - - context 'there are aggregated metrics defined' do - before do - allow(described_class).to receive(:aggregated_metrics).and_return(aggregated_metrics) - end - - context 'with AND operator' do - let(:aggregated_metrics) do - [ - { name: 'gmau_1', events: %w[event1_slot event2_slot], operator: "AND" }, - { name: 'gmau_2', events: %w[event1_slot event2_slot event3_slot], operator: "AND" }, - { name: 'gmau_3', events: %w[event1_slot event2_slot event3_slot event5_slot], operator: "AND" }, - { name: 'gmau_4', events: %w[event4], operator: "AND" } - ].map(&:with_indifferent_access) - end - - it 'returns the number of unique events for all known events' do - results = { - 'gmau_1' => 3, - 'gmau_2' => 2, - 'gmau_3' => 1, - 'gmau_4' => 3 - } - - expect(aggregated_metrics_data).to eq(results) - end - end - - context 'with OR operator' do - let(:aggregated_metrics) do - [ - { name: 'gmau_1', events: %w[event3_slot event5_slot], operator: "OR" }, - { name: 'gmau_2', events: %w[event1_slot event2_slot event3_slot event5_slot], operator: "OR" }, - { name: 'gmau_3', events: %w[event4], operator: "OR" } - ].map(&:with_indifferent_access) - end - it 'returns the number of unique events for all known events' do - results = { - 'gmau_1' => 2, - 'gmau_2' => 3, - 'gmau_3' => 3 - } - - expect(aggregated_metrics_data).to eq(results) - end - end - - context 'hidden behind feature flag' do - let(:enabled_feature_flag) { 'test_ff_enabled' } - let(:disabled_feature_flag) { 'test_ff_disabled' } - let(:aggregated_metrics) do - [ - # represents stable aggregated metrics that has been fully released - { name: 'gmau_without_ff', events: %w[event3_slot event5_slot], operator: "OR" }, - # represents new aggregated metric that is under performance testing on gitlab.com - { name: 'gmau_enabled', events: %w[event4], operator: "AND", feature_flag: enabled_feature_flag }, - # represents aggregated metric that is under development and shouldn't be yet collected even on gitlab.com - { name: 'gmau_disabled', events: %w[event4], operator: "AND", feature_flag: disabled_feature_flag } - ].map(&:with_indifferent_access) - end - - it 'returns the number of unique events for all known events' do - skip_feature_flags_yaml_validation - stub_feature_flags(enabled_feature_flag => true, disabled_feature_flag => false) + described_class.track_event('event1_slot', values: entity1, time: 2.days.ago) + described_class.track_event('event1_slot', values: entity2, time: 2.days.ago) + described_class.track_event('event1_slot', values: entity3, time: 2.days.ago) + described_class.track_event('event2_slot', values: entity1, time: 2.days.ago) + described_class.track_event('event2_slot', values: entity2, time: 3.days.ago) + described_class.track_event('event2_slot', values: entity3, time: 3.days.ago) + described_class.track_event('event3_slot', values: entity1, time: 3.days.ago) + described_class.track_event('event3_slot', values: entity2, time: 3.days.ago) + described_class.track_event('event5_slot', values: entity2, time: 3.days.ago) + + # events out of time scope + described_class.track_event('event2_slot', values: entity4, time: 8.days.ago) - expect(aggregated_metrics_data).to eq('gmau_without_ff' => 2, 'gmau_enabled' => 3) - end - end - end + # events in different slots + described_class.track_event('event4', values: entity1, time: 2.days.ago) + described_class.track_event('event4', values: entity2, time: 2.days.ago) end - describe '.aggregated_metrics_weekly_data' do - subject(:aggregated_metrics_data) { described_class.aggregated_metrics_weekly_data } - - before do - described_class.track_event('event1_slot', values: entity1, time: 2.days.ago) - described_class.track_event('event1_slot', values: entity2, time: 2.days.ago) - described_class.track_event('event1_slot', values: entity3, time: 2.days.ago) - described_class.track_event('event2_slot', values: entity1, time: 2.days.ago) - described_class.track_event('event2_slot', values: entity2, time: 3.days.ago) - described_class.track_event('event2_slot', values: entity3, time: 3.days.ago) - described_class.track_event('event3_slot', values: entity1, time: 3.days.ago) - described_class.track_event('event3_slot', values: entity2, time: 3.days.ago) - described_class.track_event('event5_slot', values: entity2, time: 3.days.ago) - - # events out of time scope - described_class.track_event('event2_slot', values: entity3, time: 8.days.ago) - - # events in different slots - described_class.track_event('event4', values: entity1, time: 2.days.ago) - described_class.track_event('event4', values: entity2, time: 2.days.ago) - described_class.track_event('event4', values: entity4, time: 2.days.ago) - end - - it_behaves_like 'aggregated_metrics_data' + it 'calculates union of given events', :aggregate_failure do + expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[event4]))).to eq 2 + expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[event1_slot event2_slot event3_slot]))).to eq 3 end - describe '.aggregated_metrics_monthly_data' do - subject(:aggregated_metrics_data) { described_class.aggregated_metrics_monthly_data } - - it_behaves_like 'aggregated_metrics_data' do - before do - described_class.track_event('event1_slot', values: entity1, time: 2.days.ago) - described_class.track_event('event1_slot', values: entity2, time: 2.days.ago) - described_class.track_event('event1_slot', values: entity3, time: 2.days.ago) - described_class.track_event('event2_slot', values: entity1, time: 2.days.ago) - described_class.track_event('event2_slot', values: entity2, time: 3.days.ago) - described_class.track_event('event2_slot', values: entity3, time: 3.days.ago) - described_class.track_event('event3_slot', values: entity1, time: 3.days.ago) - described_class.track_event('event3_slot', values: entity2, time: 10.days.ago) - described_class.track_event('event5_slot', values: entity2, time: 4.weeks.ago.advance(days: 1)) - - # events out of time scope - described_class.track_event('event5_slot', values: entity1, time: 4.weeks.ago.advance(days: -1)) - - # events in different slots - described_class.track_event('event4', values: entity1, time: 2.days.ago) - described_class.track_event('event4', values: entity2, time: 2.days.ago) - described_class.track_event('event4', values: entity4, time: 2.days.ago) - end - end - - context 'Redis calls' do - let(:aggregated_metrics) do - [ - { name: 'gmau_3', events: %w[event1_slot event2_slot event3_slot event5_slot], operator: "AND" } - ].map(&:with_indifferent_access) - end - - let(:known_events) do - [ - { name: 'event1_slot', redis_slot: "slot", category: 'category1', aggregation: "weekly" }, - { name: 'event2_slot', redis_slot: "slot", category: 'category2', aggregation: "weekly" }, - { name: 'event3_slot', redis_slot: "slot", category: 'category3', aggregation: "weekly" }, - { name: 'event5_slot', redis_slot: "slot", category: 'category4', aggregation: "weekly" } - ].map(&:with_indifferent_access) - end - - it 'caches intermediate operations' do - allow(described_class).to receive(:known_events).and_return(known_events) - allow(described_class).to receive(:aggregated_metrics).and_return(aggregated_metrics) + it 'validates and raise exception if events has mismatched slot or aggregation', :aggregate_failure do + expect { described_class.calculate_events_union(**time_range.merge(event_names: %w[event1_slot event4])) }.to raise_error described_class::SlotMismatch + expect { described_class.calculate_events_union(**time_range.merge(event_names: %w[event5_slot event3_slot])) }.to raise_error described_class::AggregationMismatch + end + end - 4.downto(1) do |subset_size| - known_events.combination(subset_size).each do |events| - keys = described_class.send(:weekly_redis_keys, events: events, start_date: 4.weeks.ago.to_date, end_date: Date.current) - expect(Gitlab::Redis::HLL).to receive(:count).with(keys: keys).once.and_return(0) - end - end + describe '.weekly_time_range' do + it 'return hash with weekly time range boundaries' do + expect(described_class.weekly_time_range).to eq(start_date: 7.days.ago.to_date, end_date: Date.current) + end + end - subject - end - end + describe '.monthly_time_range' do + it 'return hash with monthly time range boundaries' do + expect(described_class.monthly_time_range).to eq(start_date: 4.weeks.ago.to_date, end_date: Date.current) end end end |