usage_data_spec.rb 41.7 KB
Newer Older
1 2
# frozen_string_literal: true

3 4
require 'spec_helper'

5
RSpec.describe Gitlab::UsageData, :aggregate_failures do
6
  include UsageDataHelpers
7

8
  before do
9
    stub_usage_data_connections
10
    stub_object_store_settings
11
  end
12

13 14 15
  describe '.uncached_data' do
    describe '.usage_activity_by_stage' do
      it 'includes usage_activity_by_stage data' do
16 17 18 19 20 21 22 23
        uncached_data = described_class.uncached_data

        expect(uncached_data).to include(:usage_activity_by_stage)
        expect(uncached_data).to include(:usage_activity_by_stage_monthly)
        expect(uncached_data[:usage_activity_by_stage])
          .to include(:configure, :create, :manage, :monitor, :plan, :release, :verify)
        expect(uncached_data[:usage_activity_by_stage_monthly])
          .to include(:configure, :create, :manage, :monitor, :plan, :release, :verify)
24 25
      end

26
      it 'clears memoized values' do
27 28
        values = %i(issue_minimum_id issue_maximum_id
                    user_minimum_id user_maximum_id unique_visit_service
29 30 31
                    deployment_minimum_id deployment_maximum_id
                    approval_merge_request_rule_minimum_id
                    approval_merge_request_rule_maximum_id)
32
        values.each do |key|
33 34 35 36 37 38
          expect(described_class).to receive(:clear_memoization).with(key)
        end

        described_class.uncached_data
      end

39 40 41 42 43 44 45
      it 'merge_requests_users is included only in montly counters' do
        uncached_data = described_class.uncached_data

        expect(uncached_data[:usage_activity_by_stage][:create])
          .not_to include(:merge_requests_users)
        expect(uncached_data[:usage_activity_by_stage_monthly][:create])
          .to include(:merge_requests_users)
46
      end
47
    end
48

49 50 51 52 53
    it 'ensures recorded_at is set before any other usage data calculation' do
      %i(alt_usage_data redis_usage_data distinct_count count).each do |method|
        expect(described_class).not_to receive(method)
      end
      expect(described_class).to receive(:recorded_at).and_raise(Exception.new('Stopped calculating recorded_at'))
54

55 56 57
      expect { described_class.uncached_data }.to raise_error('Stopped calculating recorded_at')
    end
  end
58

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
  describe '.usage_activity_by_stage_configure' do
    it 'includes accurate usage_activity_by_stage data' do
      for_defined_days_back do
        user = create(:user)
        cluster = create(:cluster, user: user)
        create(:clusters_applications_cert_manager, :installed, cluster: cluster)
        create(:clusters_applications_helm, :installed, cluster: cluster)
        create(:clusters_applications_ingress, :installed, cluster: cluster)
        create(:clusters_applications_knative, :installed, cluster: cluster)
        create(:cluster, :disabled, user: user)
        create(:cluster_provider_gcp, :created)
        create(:cluster_provider_aws, :created)
        create(:cluster_platform_kubernetes)
        create(:cluster, :group, :disabled, user: user)
        create(:cluster, :group, user: user)
        create(:cluster, :instance, :disabled, :production_environment)
        create(:cluster, :instance, :production_environment)
        create(:cluster, :management_project)
77 78
      end

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
      expect(described_class.usage_activity_by_stage_configure({})).to include(
        clusters_applications_cert_managers: 2,
        clusters_applications_helm: 2,
        clusters_applications_ingress: 2,
        clusters_applications_knative: 2,
        clusters_management_project: 2,
        clusters_disabled: 4,
        clusters_enabled: 12,
        clusters_platforms_gke: 2,
        clusters_platforms_eks: 2,
        clusters_platforms_user: 2,
        instance_clusters_disabled: 2,
        instance_clusters_enabled: 2,
        group_clusters_disabled: 2,
        group_clusters_enabled: 2,
        project_clusters_disabled: 2,
        project_clusters_enabled: 10
      )
      expect(described_class.usage_activity_by_stage_configure(described_class.last_28_days_time_period)).to include(
        clusters_applications_cert_managers: 1,
        clusters_applications_helm: 1,
        clusters_applications_ingress: 1,
        clusters_applications_knative: 1,
        clusters_management_project: 1,
        clusters_disabled: 2,
        clusters_enabled: 6,
        clusters_platforms_gke: 1,
        clusters_platforms_eks: 1,
        clusters_platforms_user: 1,
        instance_clusters_disabled: 1,
        instance_clusters_enabled: 1,
        group_clusters_disabled: 1,
        group_clusters_enabled: 1,
        project_clusters_disabled: 1,
        project_clusters_enabled: 5
      )
    end
  end
117

118 119 120 121 122 123 124 125 126 127 128 129 130
  describe 'usage_activity_by_stage_create' do
    it 'includes accurate usage_activity_by_stage data' do
      for_defined_days_back do
        user = create(:user)
        project = create(:project, :repository_private,
                         :test_repo, :remote_mirror, creator: user)
        create(:merge_request, source_project: project)
        create(:deploy_key, user: user)
        create(:key, user: user)
        create(:project, creator: user, disable_overriding_approvers_per_merge_request: true)
        create(:project, creator: user, disable_overriding_approvers_per_merge_request: false)
        create(:remote_mirror, project: project)
        create(:snippet, author: user)
131
      end
132

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
      expect(described_class.usage_activity_by_stage_create({})).to include(
        deploy_keys: 2,
        keys: 2,
        merge_requests: 2,
        projects_with_disable_overriding_approvers_per_merge_request: 2,
        projects_without_disable_overriding_approvers_per_merge_request: 4,
        remote_mirrors: 2,
        snippets: 2
      )
      expect(described_class.usage_activity_by_stage_create(described_class.last_28_days_time_period)).to include(
        deploy_keys: 1,
        keys: 1,
        merge_requests: 1,
        projects_with_disable_overriding_approvers_per_merge_request: 1,
        projects_without_disable_overriding_approvers_per_merge_request: 2,
        remote_mirrors: 1,
        snippets: 1
      )
    end
  end

  describe 'usage_activity_by_stage_manage' do
    it 'includes accurate usage_activity_by_stage data' do
      stub_config(
        omniauth:
          { providers: omniauth_providers }
      )

      for_defined_days_back do
        user = create(:user)
        create(:event, author: user)
        create(:group_member, user: user)
165 166
      end

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
      expect(described_class.usage_activity_by_stage_manage({})).to include(
        events: 2,
        groups: 2,
        users_created: 4,
        omniauth_providers: ['google_oauth2']
      )
      expect(described_class.usage_activity_by_stage_manage(described_class.last_28_days_time_period)).to include(
        events: 1,
        groups: 1,
        users_created: 2,
        omniauth_providers: ['google_oauth2']
      )
    end

    def omniauth_providers
      [
        OpenStruct.new(name: 'google_oauth2'),
        OpenStruct.new(name: 'ldapmain'),
        OpenStruct.new(name: 'group_saml')
      ]
    end
  end

  describe 'usage_activity_by_stage_monitor' do
    it 'includes accurate usage_activity_by_stage data' do
      for_defined_days_back do
        user    = create(:user, dashboard: 'operations')
        cluster = create(:cluster, user: user)
        create(:project, creator: user)
        create(:clusters_applications_prometheus, :installed, cluster: cluster)
197 198
      end

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
      expect(described_class.usage_activity_by_stage_monitor({})).to include(
        clusters: 2,
        clusters_applications_prometheus: 2,
        operations_dashboard_default_dashboard: 2
      )
      expect(described_class.usage_activity_by_stage_monitor(described_class.last_28_days_time_period)).to include(
        clusters: 1,
        clusters_applications_prometheus: 1,
        operations_dashboard_default_dashboard: 1
      )
    end
  end

  describe 'usage_activity_by_stage_plan' do
    it 'includes accurate usage_activity_by_stage data' do
      for_defined_days_back do
        user = create(:user)
        project = create(:project, creator: user)
        issue = create(:issue, project: project, author: user)
        create(:issue, project: project, author: User.support_bot)
        create(:note, project: project, noteable: issue, author: user)
        create(:todo, project: project, target: issue, author: user)
221 222
      end

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
      expect(described_class.usage_activity_by_stage_plan({})).to include(
        issues: 3,
        notes: 2,
        projects: 2,
        todos: 2,
        service_desk_enabled_projects: 2,
        service_desk_issues: 2
      )
      expect(described_class.usage_activity_by_stage_plan(described_class.last_28_days_time_period)).to include(
        issues: 2,
        notes: 1,
        projects: 1,
        todos: 1,
        service_desk_enabled_projects: 1,
        service_desk_issues: 1
      )
    end
  end

  describe 'usage_activity_by_stage_release' do
    it 'includes accurate usage_activity_by_stage data' do
      for_defined_days_back do
        user = create(:user)
        create(:deployment, :failed, user: user)
        create(:release, author: user)
        create(:deployment, :success, user: user)
249
      end
250 251 252 253 254 255 256 257 258 259 260 261 262

      expect(described_class.usage_activity_by_stage_release({})).to include(
        deployments: 2,
        failed_deployments: 2,
        releases: 2,
        successful_deployments: 2
      )
      expect(described_class.usage_activity_by_stage_release(described_class.last_28_days_time_period)).to include(
        deployments: 1,
        failed_deployments: 1,
        releases: 1,
        successful_deployments: 1
      )
263
    end
264
  end
265

266 267 268 269 270 271 272 273 274 275 276 277
  describe 'usage_activity_by_stage_verify' do
    it 'includes accurate usage_activity_by_stage data' do
      for_defined_days_back do
        user = create(:user)
        create(:ci_build, user: user)
        create(:ci_empty_pipeline, source: :external, user: user)
        create(:ci_empty_pipeline, user: user)
        create(:ci_pipeline, :auto_devops_source, user: user)
        create(:ci_pipeline, :repository_source, user: user)
        create(:ci_pipeline_schedule, owner: user)
        create(:ci_trigger, owner: user)
        create(:clusters_applications_runner, :installed)
278 279
      end

280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
      expect(described_class.usage_activity_by_stage_verify({})).to include(
        ci_builds: 2,
        ci_external_pipelines: 2,
        ci_internal_pipelines: 2,
        ci_pipeline_config_auto_devops: 2,
        ci_pipeline_config_repository: 2,
        ci_pipeline_schedules: 2,
        ci_pipelines: 2,
        ci_triggers: 2,
        clusters_applications_runner: 2
      )
      expect(described_class.usage_activity_by_stage_verify(described_class.last_28_days_time_period)).to include(
        ci_builds: 1,
        ci_external_pipelines: 1,
        ci_internal_pipelines: 1,
        ci_pipeline_config_auto_devops: 1,
        ci_pipeline_config_repository: 1,
        ci_pipeline_schedules: 1,
        ci_pipelines: 1,
        ci_triggers: 1,
        clusters_applications_runner: 1
      )
302 303 304
    end
  end

305
  describe '.data' do
306
    let!(:ud) { build(:usage_data) }
307

308 309 310
    before do
      allow(described_class).to receive(:grafana_embed_usage_data).and_return(2)
    end
311

312
    subject { described_class.data }
313

314 315 316
    it 'gathers usage data' do
      expect(subject.keys).to include(*UsageDataHelpers::USAGE_DATA_KEYS)
    end
317

318 319
    it 'gathers usage counts' do
      count_data = subject[:counts]
320

321 322 323 324 325 326
      expect(count_data[:boards]).to eq(1)
      expect(count_data[:projects]).to eq(4)
      expect(count_data.values_at(*UsageDataHelpers::SMAU_KEYS)).to all(be_an(Integer))
      expect(count_data.keys).to include(*UsageDataHelpers::COUNTS_KEYS)
      expect(UsageDataHelpers::COUNTS_KEYS - count_data.keys).to be_empty
    end
327

328 329 330 331
    it 'gathers usage counts monthly hash' do
      expect(subject[:counts_monthly]).to be_an(Hash)
    end

332
    it 'gathers usage counts correctly' do
333 334 335 336 337 338 339 340 341 342 343 344 345 346
      count_data = subject[:counts]

      expect(count_data[:projects]).to eq(4)
      expect(count_data[:projects_asana_active]).to eq(0)
      expect(count_data[:projects_prometheus_active]).to eq(1)
      expect(count_data[:projects_jira_active]).to eq(4)
      expect(count_data[:projects_jira_server_active]).to eq(2)
      expect(count_data[:projects_jira_cloud_active]).to eq(2)
      expect(count_data[:jira_imports_projects_count]).to eq(2)
      expect(count_data[:jira_imports_total_imported_count]).to eq(3)
      expect(count_data[:jira_imports_total_imported_issues_count]).to eq(13)
      expect(count_data[:projects_slack_active]).to eq(2)
      expect(count_data[:projects_slack_slash_commands_active]).to eq(1)
      expect(count_data[:projects_custom_issue_tracker_active]).to eq(1)
347
      expect(count_data[:projects_mattermost_active]).to eq(1)
348 349
      expect(count_data[:templates_mattermost_active]).to eq(1)
      expect(count_data[:instances_mattermost_active]).to eq(1)
350
      expect(count_data[:projects_inheriting_instance_mattermost_active]).to eq(1)
351 352 353 354 355 356
      expect(count_data[:projects_with_repositories_enabled]).to eq(3)
      expect(count_data[:projects_with_error_tracking_enabled]).to eq(1)
      expect(count_data[:projects_with_alerts_service_enabled]).to eq(1)
      expect(count_data[:projects_with_prometheus_alerts]).to eq(2)
      expect(count_data[:projects_with_terraform_reports]).to eq(2)
      expect(count_data[:projects_with_terraform_states]).to eq(2)
357
      expect(count_data[:terraform_reports]).to eq(6)
358 359 360 361 362 363 364
      expect(count_data[:terraform_states]).to eq(3)
      expect(count_data[:issues_created_from_gitlab_error_tracking_ui]).to eq(1)
      expect(count_data[:issues_with_associated_zoom_link]).to eq(2)
      expect(count_data[:issues_using_zoom_quick_actions]).to eq(3)
      expect(count_data[:issues_with_embedded_grafana_charts_approx]).to eq(2)
      expect(count_data[:incident_issues]).to eq(4)
      expect(count_data[:issues_created_gitlab_alerts]).to eq(1)
365 366
      expect(count_data[:issues_created_from_alerts]).to eq(3)
      expect(count_data[:issues_created_manually_from_alerts]).to eq(1)
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
      expect(count_data[:alert_bot_incident_issues]).to eq(4)
      expect(count_data[:incident_labeled_issues]).to eq(3)

      expect(count_data[:clusters_enabled]).to eq(6)
      expect(count_data[:project_clusters_enabled]).to eq(4)
      expect(count_data[:group_clusters_enabled]).to eq(1)
      expect(count_data[:instance_clusters_enabled]).to eq(1)
      expect(count_data[:clusters_disabled]).to eq(3)
      expect(count_data[:project_clusters_disabled]).to eq(1)
      expect(count_data[:group_clusters_disabled]).to eq(1)
      expect(count_data[:instance_clusters_disabled]).to eq(1)
      expect(count_data[:clusters_platforms_eks]).to eq(1)
      expect(count_data[:clusters_platforms_gke]).to eq(1)
      expect(count_data[:clusters_platforms_user]).to eq(1)
      expect(count_data[:clusters_applications_helm]).to eq(1)
      expect(count_data[:clusters_applications_ingress]).to eq(1)
      expect(count_data[:clusters_applications_cert_managers]).to eq(1)
      expect(count_data[:clusters_applications_crossplane]).to eq(1)
      expect(count_data[:clusters_applications_prometheus]).to eq(1)
      expect(count_data[:clusters_applications_runner]).to eq(1)
      expect(count_data[:clusters_applications_knative]).to eq(1)
      expect(count_data[:clusters_applications_elastic_stack]).to eq(1)
      expect(count_data[:grafana_integrated_projects]).to eq(2)
      expect(count_data[:clusters_applications_jupyter]).to eq(1)
391
      expect(count_data[:clusters_applications_cilium]).to eq(1)
392
      expect(count_data[:clusters_management_project]).to eq(1)
393

394 395 396
      expect(count_data[:deployments]).to eq(4)
      expect(count_data[:successful_deployments]).to eq(2)
      expect(count_data[:failed_deployments]).to eq(2)
397 398 399
      expect(count_data[:snippets]).to eq(6)
      expect(count_data[:personal_snippets]).to eq(2)
      expect(count_data[:project_snippets]).to eq(4)
400
    end
401

402 403 404 405 406 407 408 409 410
    it 'gathers object store usage correctly' do
      expect(subject[:object_store]).to eq(
        { artifacts: { enabled: true, object_store: { enabled: true, direct_upload: true, background_upload: false, provider: "AWS" } },
         external_diffs: { enabled: false },
         lfs: { enabled: true, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } },
         uploads: { enabled: nil, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } },
         packages: { enabled: true, object_store: { enabled: false, direct_upload: false, background_upload: true, provider: "AWS" } } }
      )
    end
411

412 413 414 415
    it 'gathers topology data' do
      expect(subject.keys).to include(:topology)
    end

416 417 418
    context 'with existing container expiration policies' do
      let_it_be(:disabled) { create(:container_expiration_policy, enabled: false) }
      let_it_be(:enabled) { create(:container_expiration_policy, enabled: true) }
419

420 421 422
      %i[keep_n cadence older_than].each do |attribute|
        ContainerExpirationPolicy.send("#{attribute}_options").keys.each do |value|
          let_it_be("container_expiration_policy_with_#{attribute}_set_to_#{value}") { create(:container_expiration_policy, attribute => value) }
423
        end
424
      end
425

426 427
      let_it_be('container_expiration_policy_with_keep_n_set_to_null') { create(:container_expiration_policy, keep_n: nil) }
      let_it_be('container_expiration_policy_with_older_than_set_to_null') { create(:container_expiration_policy, older_than: nil) }
428

429 430
      let(:inactive_policies) { ::ContainerExpirationPolicy.where(enabled: false) }
      let(:active_policies) { ::ContainerExpirationPolicy.active }
431

432
      subject { described_class.data[:counts] }
433

434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
      it 'gathers usage data' do
        expect(subject[:projects_with_expiration_policy_enabled]).to eq 22
        expect(subject[:projects_with_expiration_policy_disabled]).to eq 1

        expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_unset]).to eq 1
        expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_1]).to eq 1
        expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_5]).to eq 1
        expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_10]).to eq 16
        expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_25]).to eq 1
        expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_50]).to eq 1

        expect(subject[:projects_with_expiration_policy_enabled_with_older_than_unset]).to eq 1
        expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_7d]).to eq 1
        expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_14d]).to eq 1
        expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_30d]).to eq 1
        expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_90d]).to eq 18

        expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_1d]).to eq 18
        expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_7d]).to eq 1
        expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_14d]).to eq 1
        expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_1month]).to eq 1
        expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_3month]).to eq 1
456
      end
457
    end
458

459 460 461
    it 'works when queries time out' do
      allow_any_instance_of(ActiveRecord::Relation)
        .to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
462

463 464
      expect { subject }.not_to raise_error
    end
465

466 467 468 469
    it 'includes a recording_ce_finished_at timestamp' do
      expect(subject[:recording_ce_finished_at]).to be_a(Time)
    end

470 471 472
    it 'jira usage works when queries time out' do
      allow_any_instance_of(ActiveRecord::Relation)
        .to receive(:find_in_batches).and_raise(ActiveRecord::StatementInvalid.new(''))
473

474
      expect { described_class.jira_usage }.not_to raise_error
475
    end
476
  end
477

478 479 480 481 482
  describe '.system_usage_data_monthly' do
    let!(:ud) { build(:usage_data) }

    subject { described_class.system_usage_data_monthly }

483
    it 'gathers monthly usage counts correctly' do
484 485
      counts_monthly = subject[:counts_monthly]

486 487 488
      expect(counts_monthly[:deployments]).to eq(2)
      expect(counts_monthly[:successful_deployments]).to eq(1)
      expect(counts_monthly[:failed_deployments]).to eq(1)
489 490 491
      expect(counts_monthly[:snippets]).to eq(3)
      expect(counts_monthly[:personal_snippets]).to eq(1)
      expect(counts_monthly[:project_snippets]).to eq(2)
492 493 494
    end
  end

495
  describe '.usage_data_counters' do
496
    subject { described_class.usage_data_counters }
497

498 499
    it { is_expected.to all(respond_to :totals) }
    it { is_expected.to all(respond_to :fallback_totals) }
A
Alex Kalderimis 已提交
500

501 502
    describe 'the results of calling #totals on all objects in the array' do
      subject { described_class.usage_data_counters.map(&:totals) }
A
Alex Kalderimis 已提交
503

504 505 506
      it { is_expected.to all(be_a Hash) }
      it { is_expected.to all(have_attributes(keys: all(be_a Symbol), values: all(be_a Integer))) }
    end
A
Alex Kalderimis 已提交
507

508 509
    describe 'the results of calling #fallback_totals on all objects in the array' do
      subject { described_class.usage_data_counters.map(&:fallback_totals) }
510

511 512 513
      it { is_expected.to all(be_a Hash) }
      it { is_expected.to all(have_attributes(keys: all(be_a Symbol), values: all(eq(-1)))) }
    end
514

515 516
    it 'does not have any conflicts' do
      all_keys = subject.flat_map { |counter| counter.totals.keys }
A
Alex Kalderimis 已提交
517

518
      expect(all_keys.size).to eq all_keys.to_set.size
519
    end
520
  end
A
Alex Kalderimis 已提交
521

522
  describe '.license_usage_data' do
523
    subject { described_class.license_usage_data }
524

525 526 527 528 529 530
    it 'gathers license data' do
      expect(subject[:uuid]).to eq(Gitlab::CurrentSettings.uuid)
      expect(subject[:version]).to eq(Gitlab::VERSION)
      expect(subject[:installation_type]).to eq('gitlab-development-kit')
      expect(subject[:active_user_count]).to eq(User.active.size)
      expect(subject[:recorded_at]).to be_a(Time)
531
    end
532
  end
533

534
  context 'when not relying on database records' do
535
    describe '.features_usage_data_ce' do
536 537
      subject { described_class.features_usage_data_ce }

538 539
      it 'gathers feature usage data', :aggregate_failures do
        expect(subject[:instance_auto_devops_enabled]).to eq(Gitlab::CurrentSettings.auto_devops_enabled?)
540 541 542 543 544 545 546 547 548 549 550 551
        expect(subject[:mattermost_enabled]).to eq(Gitlab.config.mattermost.enabled)
        expect(subject[:signup_enabled]).to eq(Gitlab::CurrentSettings.allow_signup?)
        expect(subject[:ldap_enabled]).to eq(Gitlab.config.ldap.enabled)
        expect(subject[:gravatar_enabled]).to eq(Gitlab::CurrentSettings.gravatar_enabled?)
        expect(subject[:omniauth_enabled]).to eq(Gitlab::Auth.omniauth_enabled?)
        expect(subject[:reply_by_email_enabled]).to eq(Gitlab::IncomingEmail.enabled?)
        expect(subject[:container_registry_enabled]).to eq(Gitlab.config.registry.enabled)
        expect(subject[:dependency_proxy_enabled]).to eq(Gitlab.config.dependency_proxy.enabled)
        expect(subject[:gitlab_shared_runners_enabled]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled)
        expect(subject[:web_ide_clientside_preview_enabled]).to eq(Gitlab::CurrentSettings.web_ide_clientside_preview_enabled?)
        expect(subject[:grafana_link_enabled]).to eq(Gitlab::CurrentSettings.grafana_enabled?)
      end
552

553 554 555 556 557 558 559 560 561 562 563 564 565 566
      context 'with embedded Prometheus' do
        it 'returns true when embedded Prometheus is enabled' do
          allow(Gitlab::Prometheus::Internal).to receive(:prometheus_enabled?).and_return(true)

          expect(subject[:prometheus_enabled]).to eq(true)
        end

        it 'returns false when embedded Prometheus is disabled' do
          allow(Gitlab::Prometheus::Internal).to receive(:prometheus_enabled?).and_return(false)

          expect(subject[:prometheus_enabled]).to eq(false)
        end
      end

567 568 569
      context 'with embedded grafana' do
        it 'returns true when embedded grafana is enabled' do
          stub_application_setting(grafana_enabled: true)
570

571
          expect(subject[:grafana_link_enabled]).to eq(true)
572 573
        end

574 575
        it 'returns false when embedded grafana is disabled' do
          stub_application_setting(grafana_enabled: false)
576

577
          expect(subject[:grafana_link_enabled]).to eq(false)
578
        end
579 580
      end
    end
581

582
    describe '.components_usage_data' do
583 584 585
      subject { described_class.components_usage_data }

      it 'gathers basic components usage data' do
586
        stub_application_setting(container_registry_vendor: 'gitlab', container_registry_version: 'x.y.z')
587 588 589 590 591 592 593 594 595 596 597

        expect(subject[:gitlab_pages][:enabled]).to eq(Gitlab.config.pages.enabled)
        expect(subject[:gitlab_pages][:version]).to eq(Gitlab::Pages::VERSION)
        expect(subject[:git][:version]).to eq(Gitlab::Git.version)
        expect(subject[:database][:adapter]).to eq(Gitlab::Database.adapter_name)
        expect(subject[:database][:version]).to eq(Gitlab::Database.version)
        expect(subject[:gitaly][:version]).to be_present
        expect(subject[:gitaly][:servers]).to be >= 1
        expect(subject[:gitaly][:clusters]).to be >= 0
        expect(subject[:gitaly][:filesystems]).to be_an(Array)
        expect(subject[:gitaly][:filesystems].first).to be_a(String)
598 599
        expect(subject[:container_registry_server][:vendor]).to eq('gitlab')
        expect(subject[:container_registry_server][:version]).to eq('x.y.z')
600
      end
601
    end
602

603
    describe '.object_store_config' do
604
      let(:component) { 'lfs' }
605

606
      subject { described_class.object_store_config(component) }
607

608 609 610
      context 'when object_store is not configured' do
        it 'returns component enable status only' do
          allow(Settings).to receive(:[]).with(component).and_return({ 'enabled' => false })
611

612
          expect(subject).to eq({ enabled: false })
613
        end
614
      end
615

616 617 618 619 620 621
      context 'when object_store is configured' do
        it 'returns filtered object store config' do
          allow(Settings).to receive(:[]).with(component)
            .and_return(
              { 'enabled' => true,
                'object_store' =>
622
                { 'enabled' => true,
623 624 625 626 627 628 629 630 631
                  'remote_directory' => component,
                  'direct_upload' => true,
                  'connection' =>
                { 'provider' => 'AWS', 'aws_access_key_id' => 'minio', 'aws_secret_access_key' => 'gdk-minio', 'region' => 'gdk', 'endpoint' => 'http://127.0.0.1:9000', 'path_style' => true },
                  'background_upload' => false,
                  'proxy_download' => false } })

          expect(subject).to eq(
            { enabled: true, object_store: { enabled: true, direct_upload: true, background_upload: false, provider: "AWS" } })
632
        end
633
      end
634

635 636 637
      context 'when retrieve component setting meets exception' do
        it 'returns -1 for component enable status' do
          allow(Settings).to receive(:[]).with(component).and_raise(StandardError)
638

639
          expect(subject).to eq({ enabled: -1 })
640 641
        end
      end
642
    end
643

644
    describe '.object_store_usage_data' do
645
      subject { described_class.object_store_usage_data }
646

647 648 649
      it 'fetches object store config of five components' do
        %w(artifacts external_diffs lfs uploads packages).each do |component|
          expect(described_class).to receive(:object_store_config).with(component).and_return("#{component}_object_store_config")
650
        end
651 652 653 654 655 656 657 658 659

        expect(subject).to eq(
          object_store: {
            artifacts: 'artifacts_object_store_config',
            external_diffs: 'external_diffs_object_store_config',
            lfs: 'lfs_object_store_config',
            uploads: 'uploads_object_store_config',
            packages: 'packages_object_store_config'
          })
660
      end
661
    end
662

663
    describe '.cycle_analytics_usage_data' do
664
      subject { described_class.cycle_analytics_usage_data }
665

666 667 668
      it 'works when queries time out in new' do
        allow(Gitlab::CycleAnalytics::UsageData)
          .to receive(:new).and_raise(ActiveRecord::StatementInvalid.new(''))
669

670 671 672 673 674 675
        expect { subject }.not_to raise_error
      end

      it 'works when queries time out in to_json' do
        allow_any_instance_of(Gitlab::CycleAnalytics::UsageData)
          .to receive(:to_json).and_raise(ActiveRecord::StatementInvalid.new(''))
676

677 678 679
        expect { subject }.not_to raise_error
      end
    end
680

681
    describe '.ingress_modsecurity_usage' do
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
      subject { described_class.ingress_modsecurity_usage }

      let(:environment) { create(:environment) }
      let(:project) { environment.project }
      let(:environment_scope) { '*' }
      let(:deployment) { create(:deployment, :success, environment: environment, project: project, cluster: cluster) }
      let(:cluster) { create(:cluster, environment_scope: environment_scope, projects: [project]) }
      let(:ingress_mode) { :modsecurity_blocking }
      let!(:ingress) { create(:clusters_applications_ingress, ingress_mode, cluster: cluster) }

      context 'when cluster is disabled' do
        let(:cluster) { create(:cluster, :disabled, projects: [project]) }

        it 'gathers ingress data' do
          expect(subject[:ingress_modsecurity_logging]).to eq(0)
          expect(subject[:ingress_modsecurity_blocking]).to eq(0)
          expect(subject[:ingress_modsecurity_disabled]).to eq(0)
          expect(subject[:ingress_modsecurity_not_installed]).to eq(0)
700 701 702
        end
      end

703 704
      context 'when deployment is unsuccessful' do
        let!(:deployment) { create(:deployment, :failed, environment: environment, project: project, cluster: cluster) }
705

706 707 708 709 710 711 712
        it 'gathers ingress data' do
          expect(subject[:ingress_modsecurity_logging]).to eq(0)
          expect(subject[:ingress_modsecurity_blocking]).to eq(0)
          expect(subject[:ingress_modsecurity_disabled]).to eq(0)
          expect(subject[:ingress_modsecurity_not_installed]).to eq(0)
        end
      end
713

714 715
      context 'when deployment is successful' do
        let!(:deployment) { create(:deployment, :success, environment: environment, project: project, cluster: cluster) }
716

717
        context 'when modsecurity is in blocking mode' do
718 719
          it 'gathers ingress data' do
            expect(subject[:ingress_modsecurity_logging]).to eq(0)
720
            expect(subject[:ingress_modsecurity_blocking]).to eq(1)
721 722 723 724 725
            expect(subject[:ingress_modsecurity_disabled]).to eq(0)
            expect(subject[:ingress_modsecurity_not_installed]).to eq(0)
          end
        end

726 727
        context 'when modsecurity is in logging mode' do
          let(:ingress_mode) { :modsecurity_logging }
728 729

          it 'gathers ingress data' do
730
            expect(subject[:ingress_modsecurity_logging]).to eq(1)
731 732 733 734 735 736
            expect(subject[:ingress_modsecurity_blocking]).to eq(0)
            expect(subject[:ingress_modsecurity_disabled]).to eq(0)
            expect(subject[:ingress_modsecurity_not_installed]).to eq(0)
          end
        end

737 738
        context 'when modsecurity is disabled' do
          let(:ingress_mode) { :modsecurity_disabled }
739

740 741 742 743 744
          it 'gathers ingress data' do
            expect(subject[:ingress_modsecurity_logging]).to eq(0)
            expect(subject[:ingress_modsecurity_blocking]).to eq(0)
            expect(subject[:ingress_modsecurity_disabled]).to eq(1)
            expect(subject[:ingress_modsecurity_not_installed]).to eq(0)
745
          end
746
        end
747

748 749
        context 'when modsecurity is not installed' do
          let(:ingress_mode) { :modsecurity_not_installed }
750

751 752 753 754 755
          it 'gathers ingress data' do
            expect(subject[:ingress_modsecurity_logging]).to eq(0)
            expect(subject[:ingress_modsecurity_blocking]).to eq(0)
            expect(subject[:ingress_modsecurity_disabled]).to eq(0)
            expect(subject[:ingress_modsecurity_not_installed]).to eq(1)
756
          end
757
        end
758

759 760 761 762 763 764
        context 'with multiple projects' do
          let(:environment_2) { create(:environment) }
          let(:project_2) { environment_2.project }
          let(:cluster_2) { create(:cluster, environment_scope: environment_scope, projects: [project_2]) }
          let!(:ingress_2) { create(:clusters_applications_ingress, :modsecurity_logging, cluster: cluster_2) }
          let!(:deployment_2) { create(:deployment, :success, environment: environment_2, project: project_2, cluster: cluster_2) }
765

766 767 768 769 770
          it 'gathers non-duplicated ingress data' do
            expect(subject[:ingress_modsecurity_logging]).to eq(1)
            expect(subject[:ingress_modsecurity_blocking]).to eq(1)
            expect(subject[:ingress_modsecurity_disabled]).to eq(0)
            expect(subject[:ingress_modsecurity_not_installed]).to eq(0)
771
          end
772
        end
773

774 775
        context 'with multiple deployments' do
          let!(:deployment_2) { create(:deployment, :success, environment: environment, project: project, cluster: cluster) }
776

777 778 779 780 781
          it 'gathers non-duplicated ingress data' do
            expect(subject[:ingress_modsecurity_logging]).to eq(0)
            expect(subject[:ingress_modsecurity_blocking]).to eq(1)
            expect(subject[:ingress_modsecurity_disabled]).to eq(0)
            expect(subject[:ingress_modsecurity_not_installed]).to eq(0)
782
          end
783
        end
784

785 786 787 788 789
        context 'with multiple projects' do
          let(:environment_2) { create(:environment) }
          let(:project_2) { environment_2.project }
          let!(:deployment_2) { create(:deployment, :success, environment: environment_2, project: project_2, cluster: cluster) }
          let(:cluster) { create(:cluster, environment_scope: environment_scope, projects: [project, project_2]) }
790

791 792 793 794 795
          it 'gathers ingress data' do
            expect(subject[:ingress_modsecurity_logging]).to eq(0)
            expect(subject[:ingress_modsecurity_blocking]).to eq(2)
            expect(subject[:ingress_modsecurity_disabled]).to eq(0)
            expect(subject[:ingress_modsecurity_not_installed]).to eq(0)
796
          end
797
        end
798

799 800 801
        context 'with multiple environments' do
          let!(:environment_2) { create(:environment, project: project) }
          let!(:deployment_2) { create(:deployment, :success, environment: environment_2, project: project, cluster: cluster) }
802

803 804 805 806 807
          it 'gathers ingress data' do
            expect(subject[:ingress_modsecurity_logging]).to eq(0)
            expect(subject[:ingress_modsecurity_blocking]).to eq(2)
            expect(subject[:ingress_modsecurity_disabled]).to eq(0)
            expect(subject[:ingress_modsecurity_not_installed]).to eq(0)
808
          end
809 810
        end
      end
811
    end
812

813
    describe '.grafana_embed_usage_data' do
814
      subject { described_class.grafana_embed_usage_data }
815

816 817 818 819
      let(:project) { create(:project) }
      let(:description_with_embed) { "Some comment\n\nhttps://grafana.example.com/d/xvAk4q0Wk/go-processes?orgId=1&from=1573238522762&to=1573240322762&var-job=prometheus&var-interval=10m&panelId=1&fullscreen" }
      let(:description_with_unintegrated_embed) { "Some comment\n\nhttps://grafana.exp.com/d/xvAk4q0Wk/go-processes?orgId=1&from=1573238522762&to=1573240322762&var-job=prometheus&var-interval=10m&panelId=1&fullscreen" }
      let(:description_with_non_grafana_inline_metric) { "Some comment\n\n#{Gitlab::Routing.url_helpers.metrics_namespace_project_environment_url(*['foo', 'bar', 12])}" }
820

821 822 823
      shared_examples "zero count" do
        it "does not count the issue" do
          expect(subject).to eq(0)
824
        end
825
      end
826

827 828 829
      context 'with project grafana integration enabled' do
        before do
          create(:grafana_integration, project: project, enabled: true)
830 831
        end

832
        context 'with valid and invalid embeds' do
833
          before do
834 835 836 837 838 839 840 841 842
            # Valid
            create(:issue, project: project, description: description_with_embed)
            create(:issue, project: project, description: description_with_embed)
            # In-Valid
            create(:issue, project: project, description: description_with_unintegrated_embed)
            create(:issue, project: project, description: description_with_non_grafana_inline_metric)
            create(:issue, project: project, description: nil)
            create(:issue, project: project, description: '')
            create(:issue, project: project)
843 844
          end

845 846
          it 'counts only the issues with embeds' do
            expect(subject).to eq(2)
847 848
          end
        end
849
      end
850

851 852 853 854
      context 'with project grafana integration disabled' do
        before do
          create(:grafana_integration, project: project, enabled: false)
        end
855

856 857 858 859
        context 'with one issue having a grafana link in the description and one without' do
          before do
            create(:issue, project: project, description: description_with_embed)
            create(:issue, project: project)
860
          end
861 862

          it_behaves_like('zero count')
863 864
        end
      end
865

866 867 868 869 870 871
      context 'with an un-integrated project' do
        context 'with one issue having a grafana link in the description and one without' do
          before do
            create(:issue, project: project, description: description_with_embed)
            create(:issue, project: project)
          end
872

873 874 875
          it_behaves_like('zero count')
        end
      end
876 877
    end
  end
878

879
  describe '.merge_requests_users' do
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
    let(:time_period) { { created_at: 2.days.ago..Time.current } }
    let(:merge_request) { create(:merge_request) }
    let(:other_user) { create(:user) }
    let(:another_user) { create(:user) }

    before do
      create(:event, target: merge_request, author: merge_request.author, created_at: 1.day.ago)
      create(:event, target: merge_request, author: merge_request.author, created_at: 1.hour.ago)
      create(:event, target: merge_request, author: merge_request.author, created_at: 3.days.ago)
      create(:event, target: merge_request, author: other_user, created_at: 1.day.ago)
      create(:event, target: merge_request, author: other_user, created_at: 1.hour.ago)
      create(:event, target: merge_request, author: other_user, created_at: 3.days.ago)
      create(:event, target: merge_request, author: another_user, created_at: 4.days.ago)
    end

    it 'returns the distinct count of users using merge requests (via events table) within the specified time period' do
896
      expect(described_class.merge_requests_users(time_period)).to eq(2)
897 898
    end
  end
899 900 901 902 903 904 905 906

  def for_defined_days_back(days: [29, 2])
    days.each do |n|
      Timecop.travel(n.days.ago) do
        yield
      end
    end
  end
907

908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
  describe '#action_monthly_active_users', :clean_gitlab_redis_shared_state do
    let(:time_period) { { created_at: 2.days.ago..time } }
    let(:time) { Time.zone.now }

    before do
      stub_feature_flags(Gitlab::UsageDataCounters::TrackUniqueActions::FEATURE_FLAG => feature_flag)
    end

    context 'when the feature flag is enabled' do
      let(:feature_flag) { true }

      before do
        counter = Gitlab::UsageDataCounters::TrackUniqueActions
        project = Event::TARGET_TYPES[:project]
        wiki = Event::TARGET_TYPES[:wiki]
        design = Event::TARGET_TYPES[:design]

        counter.track_action(event_action: :pushed, event_target: project, author_id: 1)
        counter.track_action(event_action: :pushed, event_target: project, author_id: 1)
        counter.track_action(event_action: :pushed, event_target: project, author_id: 2)
        counter.track_action(event_action: :pushed, event_target: project, author_id: 3)
        counter.track_action(event_action: :pushed, event_target: project, author_id: 4, time: time - 3.days)
        counter.track_action(event_action: :created, event_target: project, author_id: 5, time: time - 3.days)
        counter.track_action(event_action: :created, event_target: wiki, author_id: 3)
        counter.track_action(event_action: :created, event_target: design, author_id: 3)
      end

      it 'returns the distinct count of user actions within the specified time period' do
        expect(described_class.action_monthly_active_users(time_period)).to eq(
          {
            action_monthly_active_users_design_management: 1,
            action_monthly_active_users_project_repo: 3,
            action_monthly_active_users_wiki_repo: 1
          }
        )
      end
    end

    context 'when the feature flag is disabled' do
      let(:feature_flag) { false }

      it 'returns an empty hash' do
        expect(described_class.action_monthly_active_users(time_period)).to eq({})
      end
    end
  end

955 956 957 958 959
  describe '.analytics_unique_visits_data' do
    subject { described_class.analytics_unique_visits_data }

    it 'returns the number of unique visits to pages with analytics features' do
      ::Gitlab::Analytics::UniqueVisits::TARGET_IDS.each do |target_id|
960
        expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: target_id).and_return(123)
961 962
      end

963 964
      expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: :any).and_return(543)
      expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: :any, weeks: 4).and_return(987)
965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980

      expect(subject).to eq({
        analytics_unique_visits: {
          'g_analytics_contribution' => 123,
          'g_analytics_insights' => 123,
          'g_analytics_issues' => 123,
          'g_analytics_productivity' => 123,
          'g_analytics_valuestream' => 123,
          'p_analytics_pipelines' => 123,
          'p_analytics_code_reviews' => 123,
          'p_analytics_valuestream' => 123,
          'p_analytics_insights' => 123,
          'p_analytics_issues' => 123,
          'p_analytics_repo' => 123,
          'i_analytics_cohorts' => 123,
          'i_analytics_dev_ops_score' => 123,
981 982
          'analytics_unique_visits_for_any_target' => 543,
          'analytics_unique_visits_for_any_target_monthly' => 987
983 984 985 986
        }
      })
    end
  end
987 988 989 990 991 992 993 994 995 996 997 998 999

  describe '.service_desk_counts' do
    subject { described_class.send(:service_desk_counts) }

    let(:project) { create(:project, :service_desk_enabled) }

    it 'gathers Service Desk data' do
      create_list(:issue, 2, :confidential, author: User.support_bot, project: project)

      expect(subject).to eq(service_desk_enabled_projects: 1,
                            service_desk_issues: 2)
    end
  end
1000
end