usage_data_spec.rb 42.0 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 16 17 18 19
  describe '.uncached_data' do
    describe '.usage_activity_by_stage' do
      it 'includes usage_activity_by_stage data' do
        expect(described_class.uncached_data).to include(:usage_activity_by_stage)
        expect(described_class.uncached_data).to include(:usage_activity_by_stage_monthly)
      end

20
      it 'clears memoized values' do
21 22
        values = %i(issue_minimum_id issue_maximum_id
                    user_minimum_id user_maximum_id unique_visit_service
23 24 25
                    deployment_minimum_id deployment_maximum_id
                    approval_merge_request_rule_minimum_id
                    approval_merge_request_rule_maximum_id)
26
        values.each do |key|
27 28 29 30 31 32
          expect(described_class).to receive(:clear_memoization).with(key)
        end

        described_class.uncached_data
      end

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
      context 'for 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)
          end

          expect(described_class.uncached_data[: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.uncached_data[:usage_activity_by_stage_monthly][:configure]).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
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
      context 'for create' do
        it 'include usage_activity_by_stage data' do
          expect(described_class.uncached_data[:usage_activity_by_stage][:create])
            .not_to include(
              :merge_requests_users
            )
        end

        it 'includes monthly usage_activity_by_stage data' do
          expect(described_class.uncached_data[:usage_activity_by_stage_monthly][:create])
            .to include(
              :merge_requests_users
            )
        end

        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)
          end

          expect(described_class.uncached_data[: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.uncached_data[:usage_activity_by_stage_monthly][:create]).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

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
      context 'for 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)
          end

          expect(described_class.uncached_data[:usage_activity_by_stage][:manage]).to include(
            events: 2,
            groups: 2,
            users_created: Gitlab.ee? ? 6 : 5,
            omniauth_providers: ['google_oauth2']
          )
          expect(described_class.uncached_data[:usage_activity_by_stage_monthly][:manage]).to include(
            events: 1,
            groups: 1,
            users_created: Gitlab.ee? ? 4 : 3,
            omniauth_providers: ['google_oauth2']
          )
        end

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

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 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
      context 'for 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)
          end

          expect(described_class.uncached_data[:usage_activity_by_stage][:monitor]).to include(
            clusters: 2,
            clusters_applications_prometheus: 2,
            operations_dashboard_default_dashboard: 2
          )
          expect(described_class.uncached_data[:usage_activity_by_stage_monthly][:monitor]).to include(
            clusters: 1,
            clusters_applications_prometheus: 1,
            operations_dashboard_default_dashboard: 1
          )
        end
      end

      context 'for 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(:note, project: project, noteable: issue, author: user)
            create(:todo, project: project, target: issue, author: user)
          end

          expect(described_class.uncached_data[:usage_activity_by_stage][:plan]).to include(
            issues: 2,
            notes: 2,
            projects: 2,
            todos: 2
          )
          expect(described_class.uncached_data[:usage_activity_by_stage_monthly][:plan]).to include(
            issues: 1,
            notes: 1,
            projects: 1,
            todos: 1
          )
        end
      end

      context 'for 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)
          end

          expect(described_class.uncached_data[:usage_activity_by_stage][:release]).to include(
            deployments: 2,
            failed_deployments: 2,
            releases: 2,
            successful_deployments: 2
          )
          expect(described_class.uncached_data[:usage_activity_by_stage_monthly][:release]).to include(
            deployments: 1,
            failed_deployments: 1,
            releases: 1,
            successful_deployments: 1
          )
        end
      end

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
      context 'for 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)
          end

          expect(described_class.uncached_data[: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.uncached_data[:usage_activity_by_stage_monthly][:verify]).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
          )
        end
      end
287 288
    end

289 290 291 292 293 294 295 296 297 298
    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'))

      expect { described_class.uncached_data }.to raise_error('Stopped calculating recorded_at')
    end
  end

299
  describe '.data' do
300
    let!(:ud) { build(:usage_data) }
301

302 303 304
    before do
      allow(described_class).to receive(:grafana_embed_usage_data).and_return(2)
    end
305

306
    subject { described_class.data }
307

308 309 310
    it 'gathers usage data' do
      expect(subject.keys).to include(*UsageDataHelpers::USAGE_DATA_KEYS)
    end
311

312 313
    it 'gathers usage counts' do
      count_data = subject[:counts]
314

315 316 317 318 319 320
      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
321

322 323 324 325
    it 'gathers usage counts monthly hash' do
      expect(subject[:counts_monthly]).to be_an(Hash)
    end

326
    it 'gathers usage counts correctly' do
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
      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)
      expect(count_data[:projects_mattermost_active]).to eq(0)
      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)
348
      expect(count_data[:terraform_reports]).to eq(6)
349 350 351 352 353 354 355
      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)
356 357
      expect(count_data[:issues_created_from_alerts]).to eq(3)
      expect(count_data[:issues_created_manually_from_alerts]).to eq(1)
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
      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)
382
      expect(count_data[:clusters_applications_cilium]).to eq(1)
383
      expect(count_data[:clusters_management_project]).to eq(1)
384

385 386 387
      expect(count_data[:deployments]).to eq(4)
      expect(count_data[:successful_deployments]).to eq(2)
      expect(count_data[:failed_deployments]).to eq(2)
388 389 390
      expect(count_data[:snippets]).to eq(6)
      expect(count_data[:personal_snippets]).to eq(2)
      expect(count_data[:project_snippets]).to eq(4)
391
    end
392

393 394 395 396 397 398 399 400 401
    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
402

403 404 405 406
    it 'gathers topology data' do
      expect(subject.keys).to include(:topology)
    end

407 408 409
    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) }
410

411 412 413
      %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) }
414
        end
415
      end
416

417 418
      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) }
419

420 421
      let(:inactive_policies) { ::ContainerExpirationPolicy.where(enabled: false) }
      let(:active_policies) { ::ContainerExpirationPolicy.active }
422

423
      subject { described_class.data[:counts] }
424

425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
      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
447
      end
448
    end
449

450 451 452
    it 'works when queries time out' do
      allow_any_instance_of(ActiveRecord::Relation)
        .to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
453

454 455
      expect { subject }.not_to raise_error
    end
456

457 458 459 460
    it 'includes a recording_ce_finished_at timestamp' do
      expect(subject[:recording_ce_finished_at]).to be_a(Time)
    end

461 462 463
    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(''))
464

465
      expect { described_class.jira_usage }.not_to raise_error
466
    end
467
  end
468

469 470 471 472 473
  describe '.system_usage_data_monthly' do
    let!(:ud) { build(:usage_data) }

    subject { described_class.system_usage_data_monthly }

474
    it 'gathers monthly usage counts correctly' do
475 476
      counts_monthly = subject[:counts_monthly]

477 478 479
      expect(counts_monthly[:deployments]).to eq(2)
      expect(counts_monthly[:successful_deployments]).to eq(1)
      expect(counts_monthly[:failed_deployments]).to eq(1)
480 481 482
      expect(counts_monthly[:snippets]).to eq(3)
      expect(counts_monthly[:personal_snippets]).to eq(1)
      expect(counts_monthly[:project_snippets]).to eq(2)
483 484 485
    end
  end

486
  describe '.usage_data_counters' do
487
    subject { described_class.usage_data_counters }
488

489 490
    it { is_expected.to all(respond_to :totals) }
    it { is_expected.to all(respond_to :fallback_totals) }
A
Alex Kalderimis 已提交
491

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

495 496 497
      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 已提交
498

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

502 503 504
      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
505

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

509
      expect(all_keys.size).to eq all_keys.to_set.size
510
    end
511
  end
A
Alex Kalderimis 已提交
512

513
  describe '.license_usage_data' do
514
    subject { described_class.license_usage_data }
515

516 517 518 519 520 521
    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)
522
    end
523
  end
524

525
  context 'when not relying on database records' do
526
    describe '.features_usage_data_ce' do
527 528
      subject { described_class.features_usage_data_ce }

529 530
      it 'gathers feature usage data', :aggregate_failures do
        expect(subject[:instance_auto_devops_enabled]).to eq(Gitlab::CurrentSettings.auto_devops_enabled?)
531 532 533 534 535 536 537 538 539 540 541 542
        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
543

544 545 546 547 548 549 550 551 552 553 554 555 556 557
      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

558 559 560
      context 'with embedded grafana' do
        it 'returns true when embedded grafana is enabled' do
          stub_application_setting(grafana_enabled: true)
561

562
          expect(subject[:grafana_link_enabled]).to eq(true)
563 564
        end

565 566
        it 'returns false when embedded grafana is disabled' do
          stub_application_setting(grafana_enabled: false)
567

568
          expect(subject[:grafana_link_enabled]).to eq(false)
569
        end
570 571
      end
    end
572

573
    describe '.components_usage_data' do
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
      subject { described_class.components_usage_data }

      it 'gathers basic components usage data' do
        stub_runtime(:puma)

        expect(subject[:app_server][:type]).to eq('puma')
        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)
590 591
      end

592 593 594 595 596
      def stub_runtime(runtime)
        allow(Gitlab::Runtime).to receive(:identify).and_return(runtime)
      end
    end

597
    describe '.app_server_type' do
598
      subject { described_class.app_server_type }
599

600 601
      it 'successfully identifies runtime and returns the identifier' do
        expect(Gitlab::Runtime).to receive(:identify).and_return(:runtime_identifier)
602

603 604
        is_expected.to eq('runtime_identifier')
      end
605

606 607
      context 'when runtime is not identified' do
        let(:exception) { Gitlab::Runtime::IdentificationError.new('exception message from runtime identify') }
608

609 610 611 612 613 614
        it 'logs the exception and returns unknown app server type' do
          expect(Gitlab::Runtime).to receive(:identify).and_raise(exception)

          expect(Gitlab::AppLogger).to receive(:error).with(exception.message)
          expect(Gitlab::ErrorTracking).to receive(:track_exception).with(exception)
          expect(subject).to eq('unknown_app_server_type')
615 616
        end
      end
617
    end
618

619
    describe '.object_store_config' do
620
      let(:component) { 'lfs' }
621

622
      subject { described_class.object_store_config(component) }
623

624 625 626
      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 })
627

628
          expect(subject).to eq({ enabled: false })
629
        end
630
      end
631

632 633 634 635 636 637
      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' =>
638
                { 'enabled' => true,
639 640 641 642 643 644 645 646 647
                  '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" } })
648
        end
649
      end
650

651 652 653
      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)
654

655
          expect(subject).to eq({ enabled: -1 })
656 657
        end
      end
658
    end
659

660
    describe '.object_store_usage_data' do
661
      subject { described_class.object_store_usage_data }
662

663 664 665
      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")
666
        end
667 668 669 670 671 672 673 674 675

        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'
          })
676
      end
677
    end
678

679
    describe '.cycle_analytics_usage_data' do
680
      subject { described_class.cycle_analytics_usage_data }
681

682 683 684
      it 'works when queries time out in new' do
        allow(Gitlab::CycleAnalytics::UsageData)
          .to receive(:new).and_raise(ActiveRecord::StatementInvalid.new(''))
685

686 687 688 689 690 691
        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(''))
692

693 694 695
        expect { subject }.not_to raise_error
      end
    end
696

697
    describe '.ingress_modsecurity_usage' do
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
      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)
716 717 718
        end
      end

719 720
      context 'when deployment is unsuccessful' do
        let!(:deployment) { create(:deployment, :failed, environment: environment, project: project, cluster: cluster) }
721

722 723 724 725 726 727 728
        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
729

730 731
      context 'when deployment is successful' do
        let!(:deployment) { create(:deployment, :success, environment: environment, project: project, cluster: cluster) }
732

733
        context 'when modsecurity is in blocking mode' do
734 735
          it 'gathers ingress data' do
            expect(subject[:ingress_modsecurity_logging]).to eq(0)
736
            expect(subject[:ingress_modsecurity_blocking]).to eq(1)
737 738 739 740 741
            expect(subject[:ingress_modsecurity_disabled]).to eq(0)
            expect(subject[:ingress_modsecurity_not_installed]).to eq(0)
          end
        end

742 743
        context 'when modsecurity is in logging mode' do
          let(:ingress_mode) { :modsecurity_logging }
744 745

          it 'gathers ingress data' do
746
            expect(subject[:ingress_modsecurity_logging]).to eq(1)
747 748 749 750 751 752
            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

753 754
        context 'when modsecurity is disabled' do
          let(:ingress_mode) { :modsecurity_disabled }
755

756 757 758 759 760
          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)
761
          end
762
        end
763

764 765
        context 'when modsecurity is not installed' do
          let(:ingress_mode) { :modsecurity_not_installed }
766

767 768 769 770 771
          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)
772
          end
773
        end
774

775 776 777 778 779 780
        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) }
781

782 783 784 785 786
          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)
787
          end
788
        end
789

790 791
        context 'with multiple deployments' do
          let!(:deployment_2) { create(:deployment, :success, environment: environment, project: project, cluster: cluster) }
792

793 794 795 796 797
          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)
798
          end
799
        end
800

801 802 803 804 805
        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]) }
806

807 808 809 810 811
          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)
812
          end
813
        end
814

815 816 817
        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) }
818

819 820 821 822 823
          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)
824
          end
825 826
        end
      end
827
    end
828

829
    describe '.grafana_embed_usage_data' do
830
      subject { described_class.grafana_embed_usage_data }
831

832 833 834 835
      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])}" }
836

837 838 839
      shared_examples "zero count" do
        it "does not count the issue" do
          expect(subject).to eq(0)
840
        end
841
      end
842

843 844 845
      context 'with project grafana integration enabled' do
        before do
          create(:grafana_integration, project: project, enabled: true)
846 847
        end

848
        context 'with valid and invalid embeds' do
849
          before do
850 851 852 853 854 855 856 857 858
            # 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)
859 860
          end

861 862
          it 'counts only the issues with embeds' do
            expect(subject).to eq(2)
863 864
          end
        end
865
      end
866

867 868 869 870
      context 'with project grafana integration disabled' do
        before do
          create(:grafana_integration, project: project, enabled: false)
        end
871

872 873 874 875
        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)
876
          end
877 878

          it_behaves_like('zero count')
879 880
        end
      end
881

882 883 884 885 886 887
      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
888

889 890 891
          it_behaves_like('zero count')
        end
      end
892 893
    end
  end
894

895
  describe '.merge_requests_users' do
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
    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
912
      expect(described_class.merge_requests_users(time_period)).to eq(2)
913 914
    end
  end
915 916 917 918 919 920 921 922

  def for_defined_days_back(days: [29, 2])
    days.each do |n|
      Timecop.travel(n.days.ago) do
        yield
      end
    end
  end
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 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
  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

971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
  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|
        expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:weekly_unique_visits_for_target).with(target_id).and_return(123)
      end

      expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:weekly_unique_visits_for_any_target).and_return(543)

      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,
          'u_analytics_todos' => 123,
          'i_analytics_cohorts' => 123,
          'i_analytics_dev_ops_score' => 123,
          'analytics_unique_visits_for_any_target' => 543
        }
      })
    end
  end
1002
end