usage_data_spec.rb 39.9 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 23 24
        values = %i(issue_minimum_id issue_maximum_id
                    user_minimum_id user_maximum_id unique_visit_service
                    deployment_minimum_id deployment_maximum_id)
        values.each do |key|
25 26 27 28 29 30
          expect(described_class).to receive(:clear_memoization).with(key)
        end

        described_class.uncached_data
      end

31 32 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
      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
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
      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

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 165 166 167 168 169 170 171 172 173 174
      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
175

176 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
      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

247 248 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
      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
285 286
    end

287 288 289 290 291 292 293 294 295 296
    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

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

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

304
    subject { described_class.data }
305

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

310 311
    it 'gathers usage counts' do
      count_data = subject[:counts]
312

313 314 315 316 317 318
      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
319

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

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

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

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

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

405 406 407
    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) }
408

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

415 416
      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) }
417

418 419
      let(:inactive_policies) { ::ContainerExpirationPolicy.where(enabled: false) }
      let(:active_policies) { ::ContainerExpirationPolicy.active }
420

421
      subject { described_class.data[:counts] }
422

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

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

452 453
      expect { subject }.not_to raise_error
    end
454

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

459 460 461
    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(''))
462

463
      expect { described_class.jira_usage }.not_to raise_error
464
    end
465
  end
466

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

    subject { described_class.system_usage_data_monthly }

472
    it 'gathers monthly usage counts correctly' do
473 474
      counts_monthly = subject[:counts_monthly]

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

484
  describe '.usage_data_counters' do
485
    subject { described_class.usage_data_counters }
486

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

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

493 494 495
      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 已提交
496

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

500 501 502
      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
503

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

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

511
  describe '.license_usage_data' do
512
    subject { described_class.license_usage_data }
513

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

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

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

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

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

560
          expect(subject[:grafana_link_enabled]).to eq(true)
561 562
        end

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

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

571
    describe '.components_usage_data' do
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
      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)
588 589
      end

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

595
    describe '.app_server_type' do
596
      subject { described_class.app_server_type }
597

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

601 602
        is_expected.to eq('runtime_identifier')
      end
603

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

607 608 609 610 611 612
        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')
613 614
        end
      end
615
    end
616

617
    describe '.object_store_config' do
618
      let(:component) { 'lfs' }
619

620
      subject { described_class.object_store_config(component) }
621

622 623 624
      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 })
625

626
          expect(subject).to eq({ enabled: false })
627
        end
628
      end
629

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

649 650 651
      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)
652

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

658
    describe '.object_store_usage_data' do
659
      subject { described_class.object_store_usage_data }
660

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

        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'
          })
674
      end
675
    end
676

677
    describe '.cycle_analytics_usage_data' do
678
      subject { described_class.cycle_analytics_usage_data }
679

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

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

691 692 693
        expect { subject }.not_to raise_error
      end
    end
694

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

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

720 721 722 723 724 725 726
        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
727

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

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

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

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

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

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

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

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

773 774 775 776 777 778
        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) }
779

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

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

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

799 800 801 802 803
        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]) }
804

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

813 814 815
        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) }
816

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

827
    describe '.grafana_embed_usage_data' do
828
      subject { described_class.grafana_embed_usage_data }
829

830 831 832 833
      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])}" }
834

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

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

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

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

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

870 871 872 873
        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)
874
          end
875 876

          it_behaves_like('zero count')
877 878
        end
      end
879

880 881 882 883 884 885
      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
886

887 888 889
          it_behaves_like('zero count')
        end
      end
890 891
    end
  end
892

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

  def for_defined_days_back(days: [29, 2])
    days.each do |n|
      Timecop.travel(n.days.ago) do
        yield
      end
    end
  end
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

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