geo_node_status_spec.rb 33 KB
Newer Older
1 2
require 'spec_helper'

3
describe GeoNodeStatus, :geo do
4 5
  include ::EE::GeoHelpers

6 7
  let!(:primary)  { create(:geo_node, :primary) }
  let!(:secondary) { create(:geo_node) }
8

9 10 11 12 13
  let!(:group)     { create(:group) }
  let!(:project_1) { create(:project, group: group) }
  let!(:project_2) { create(:project, group: group) }
  let!(:project_3) { create(:project) }
  let!(:project_4) { create(:project) }
14

15
  subject(:status) { described_class.current_node_status }
16 17 18 19

  before do
    stub_current_geo_node(secondary)
  end
20

21 22
  describe '#fast_current_node_status' do
    it 'reads the cache and spawns the worker' do
23 24
      expect(described_class).to receive(:spawn_worker).once

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
      rails_cache = double
      expect(rails_cache).to receive(:read).with(described_class.cache_key)
      expect(Rails).to receive(:cache).and_return(rails_cache)

      described_class.fast_current_node_status
    end
  end

  describe '#update_cache!' do
    it 'writes a cache' do
      rails_cache = double
      expect(rails_cache).to receive(:write).with(described_class.cache_key, kind_of(Hash))
      expect(Rails).to receive(:cache).and_return(rails_cache)

      described_class.new.update_cache!
    end
  end

43 44 45
  describe '#healthy?' do
    context 'when health is blank' do
      it 'returns true' do
46
        subject.status_message = ''
47

48
        expect(subject.healthy?).to be true
49 50 51 52
      end
    end

    context 'when health is present' do
53
      it 'returns true' do
54
        subject.status_message = GeoNodeStatus::HEALTHY_STATUS
55 56 57 58

        expect(subject.healthy?).to be true
      end

59
      it 'returns false' do
60
        subject.status_message = 'something went wrong'
61

62
        expect(subject.healthy?).to be false
63 64
      end
    end
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 91 92 93 94

    context 'takes outdated? into consideration' do
      it 'return false' do
        subject.status_message = GeoNodeStatus::HEALTHY_STATUS
        subject.updated_at = 10.minutes.ago

        expect(subject.healthy?).to be false
      end

      it 'return false' do
        subject.status_message = 'something went wrong'
        subject.updated_at = 1.minute.ago

        expect(subject.healthy?).to be false
      end
    end
  end

  describe '#outdated?' do
    it 'return true' do
      subject.updated_at = 10.minutes.ago

      expect(subject.outdated?).to be true
    end

    it 'return false' do
      subject.updated_at = 1.minute.ago

      expect(subject.outdated?).to be false
    end
95 96
  end

97
  describe '#status_message' do
98 99 100
    it 'delegates to the HealthCheck' do
      expect(HealthCheck::Utils).to receive(:process_checks).with(['geo']).once

101
      subject
102 103 104
    end
  end

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
  describe '#health' do
    context 'takes outdated? into consideration' do
      it 'returns expiration error' do
        subject.status_message = GeoNodeStatus::HEALTHY_STATUS
        subject.updated_at = 10.minutes.ago

        expect(subject.health).to eq "Status has not been updated in the past #{described_class::EXPIRATION_IN_MINUTES} minutes"
      end

      it 'returns original message' do
        subject.status_message = 'something went wrong'
        subject.updated_at = 1.minute.ago

        expect(subject.health).to eq 'something went wrong'
      end
    end
  end

123
  # Disable transactions via :delete method because a foreign table
124
  # can't see changes inside a transaction of a different connection.
125
  describe '#attachments_synced_count', :delete do
126
    it 'only counts successful syncs' do
Robert Speicher's avatar
Robert Speicher committed
127
      create_list(:user, 3, avatar: fixture_file_upload('spec/fixtures/dk.png', 'image/png'))
128 129 130 131 132 133 134 135 136
      uploads = Upload.all.pluck(:id)

      create(:geo_file_registry, :avatar, file_id: uploads[0])
      create(:geo_file_registry, :avatar, file_id: uploads[1])
      create(:geo_file_registry, :avatar, file_id: uploads[2], success: false)

      expect(subject.attachments_synced_count).to eq(2)
    end

137
    it 'does not count synced files that were replaced' do
Robert Speicher's avatar
Robert Speicher committed
138
      user = create(:user, avatar: fixture_file_upload('spec/fixtures/dk.png', 'image/png'))
139 140 141 142

      expect(subject.attachments_count).to eq(1)
      expect(subject.attachments_synced_count).to eq(0)

143
      upload = Upload.find_by(model: user, uploader: 'AvatarUploader')
144
      create(:geo_file_registry, :avatar, file_id: upload.id)
145

146 147
      subject = described_class.current_node_status

148 149 150
      expect(subject.attachments_count).to eq(1)
      expect(subject.attachments_synced_count).to eq(1)

Robert Speicher's avatar
Robert Speicher committed
151
      user.update(avatar: fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg'))
152

153 154
      subject = described_class.current_node_status

155 156 157
      expect(subject.attachments_count).to eq(1)
      expect(subject.attachments_synced_count).to eq(0)

158
      upload = Upload.find_by(model: user, uploader: 'AvatarUploader')
159
      create(:geo_file_registry, :avatar, file_id: upload.id)
160

161 162
      subject = described_class.current_node_status

163
      expect(subject.attachments_count).to eq(1)
164 165 166 167
      expect(subject.attachments_synced_count).to eq(1)
    end
  end

168 169 170 171
  # Disable transactions via :delete method because a foreign table
  # can't see changes inside a transaction of a different connection.
  describe '#attachments_synced_missing_on_primary_count', :delete do
    it 'only counts successful syncs' do
Robert Speicher's avatar
Robert Speicher committed
172
      create_list(:user, 3, avatar: fixture_file_upload('spec/fixtures/dk.png', 'image/png'))
173 174 175 176 177 178 179 180 181 182
      uploads = Upload.all.pluck(:id)

      create(:geo_file_registry, :avatar, file_id: uploads[0], missing_on_primary: true)
      create(:geo_file_registry, :avatar, file_id: uploads[1])
      create(:geo_file_registry, :avatar, file_id: uploads[2], success: false)

      expect(subject.attachments_synced_missing_on_primary_count).to eq(1)
    end
  end

183
  describe '#attachments_failed_count', :delete do
184 185
    it 'counts failed avatars, attachment, personal snippets and files' do
      # These two should be ignored
186 187
      create(:geo_file_registry, :lfs, :with_file, success: false)
      create(:geo_file_registry, :with_file)
188

189 190 191 192
      create(:geo_file_registry, :with_file, file_type: :personal_file, success: false)
      create(:geo_file_registry, :with_file, file_type: :attachment, success: false)
      create(:geo_file_registry, :avatar, :with_file, success: false)
      create(:geo_file_registry, :with_file, success: false)
193 194 195 196 197

      expect(subject.attachments_failed_count).to eq(4)
    end
  end

198
  describe '#attachments_synced_in_percentage', :delete do
Robert Speicher's avatar
Robert Speicher committed
199
    let(:avatar) { fixture_file_upload('spec/fixtures/dk.png') }
200 201 202 203 204 205 206
    let(:upload_1) { create(:upload, model: group, path: avatar) }
    let(:upload_2) { create(:upload, model: project_1, path: avatar) }

    before do
      create(:upload, model: create(:group), path: avatar)
      create(:upload, model: project_3, path: avatar)
    end
207

208
    it 'returns 0 when no objects are available' do
209 210 211
      expect(subject.attachments_synced_in_percentage).to eq(0)
    end

212 213 214
    it 'returns the right percentage with no group restrictions' do
      create(:geo_file_registry, :avatar, file_id: upload_1.id)
      create(:geo_file_registry, :avatar, file_id: upload_2.id)
215

216 217 218 219
      expect(subject.attachments_synced_in_percentage).to be_within(0.0001).of(50)
    end

    it 'returns the right percentage with group restrictions' do
220
      secondary.update!(selective_sync_type: 'namespaces', namespaces: [group])
221 222 223 224
      create(:geo_file_registry, :avatar, file_id: upload_1.id)
      create(:geo_file_registry, :avatar, file_id: upload_2.id)

      expect(subject.attachments_synced_in_percentage).to be_within(0.0001).of(100)
225 226 227
    end
  end

228
  describe '#db_replication_lag_seconds' do
229 230
    it 'returns the set replication lag if secondary' do
      allow(Gitlab::Geo).to receive(:secondary?).and_return(true)
231
      allow(Gitlab::Geo::HealthCheck).to receive(:db_replication_lag_seconds).and_return(1000)
232

233
      expect(subject.db_replication_lag_seconds).to eq(1000)
234
    end
235 236

    it "doesn't attempt to set replication lag if primary" do
237
      stub_current_geo_node(primary)
238
      expect(Gitlab::Geo::HealthCheck).not_to receive(:db_replication_lag_seconds)
239

240
      expect(subject.db_replication_lag_seconds).to eq(nil)
241
    end
242 243
  end

244 245
  # Disable transactions via :delete method because a foreign table
  # can't see changes inside a transaction of a different connection.
246 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
  describe '#lfs_objects_synced_count', :delete do
    it 'counts synced LFS objects' do
      # These four should be ignored
      create(:geo_file_registry, success: false)
      create(:geo_file_registry, :avatar)
      create(:geo_file_registry, file_type: :attachment)
      create(:geo_file_registry, :lfs, :with_file, success: false)

      create(:geo_file_registry, :lfs, :with_file, success: true)

      expect(subject.lfs_objects_synced_count).to eq(1)
    end
  end

  # Disable transactions via :delete method because a foreign table
  # can't see changes inside a transaction of a different connection.
  describe '#lfs_objects_synced_missing_on_primary_count', :delete do
    it 'counts LFS objects marked as synced due to file missing on the primary' do
      # These four should be ignored
      create(:geo_file_registry, success: false)
      create(:geo_file_registry, :avatar, missing_on_primary: true)
      create(:geo_file_registry, file_type: :attachment, missing_on_primary: true)
      create(:geo_file_registry, :lfs, :with_file, success: false)

      create(:geo_file_registry, :lfs, :with_file, success: true, missing_on_primary: true)

      expect(subject.lfs_objects_synced_missing_on_primary_count).to eq(1)
    end
  end

  # Disable transactions via :delete method because a foreign table
  # can't see changes inside a transaction of a different connection.
  describe '#lfs_objects_failed_count', :delete do
279 280 281 282 283
    it 'counts failed LFS objects' do
      # These four should be ignored
      create(:geo_file_registry, success: false)
      create(:geo_file_registry, :avatar, success: false)
      create(:geo_file_registry, file_type: :attachment, success: false)
284
      create(:geo_file_registry, :lfs, :with_file)
285

286
      create(:geo_file_registry, :lfs, :with_file, success: false)
287 288 289 290 291

      expect(subject.lfs_objects_failed_count).to eq(1)
    end
  end

292 293 294
  # Disable transactions via :delete method because a foreign table
  # can't see changes inside a transaction of a different connection.
  describe '#lfs_objects_synced_in_percentage', :delete do
295
    let(:lfs_object_project) { create(:lfs_objects_project, project: project_1) }
296

297 298 299 300 301 302 303 304
    before do
      allow(ProjectCacheWorker).to receive(:perform_async).and_return(true)

      create(:lfs_objects_project, project: project_1)
      create_list(:lfs_objects_project, 2, project: project_3)
    end

    it 'returns 0 when no objects are available' do
305 306 307
      expect(subject.lfs_objects_synced_in_percentage).to eq(0)
    end

308
    it 'returns the right percentage with no group restrictions' do
309
      create(:geo_file_registry, :lfs, file_id: lfs_object_project.lfs_object_id, success: true)
310 311 312

      expect(subject.lfs_objects_synced_in_percentage).to be_within(0.0001).of(25)
    end
313 314

    it 'returns the right percentage with group restrictions' do
315
      secondary.update!(selective_sync_type: 'namespaces', namespaces: [group])
316
      create(:geo_file_registry, :lfs, file_id: lfs_object_project.lfs_object_id, success: true)
317 318 319

      expect(subject.lfs_objects_synced_in_percentage).to be_within(0.0001).of(50)
    end
320 321
  end

322 323 324
  # Disable transactions via :delete method because a foreign table
  # can't see changes inside a transaction of a different connection.
  describe '#job_artifacts_synced_count', :delete do
325 326
    it 'counts synced job artifacts' do
      # These should be ignored
327
      create(:geo_file_registry, success: true)
328
      create(:geo_job_artifact_registry, :with_artifact, success: false)
329

330
      create(:geo_job_artifact_registry, :with_artifact, success: true)
331 332 333 334 335

      expect(subject.job_artifacts_synced_count).to eq(1)
    end
  end

336 337 338 339 340 341 342 343 344 345 346 347 348 349
  # Disable transactions via :delete method because a foreign table
  # can't see changes inside a transaction of a different connection.
  describe '#job_artifacts_synced_missing_on_primary_count', :delete do
    it 'counts job artifacts marked as synced due to file missing on the primary' do
      # These should be ignored
      create(:geo_file_registry, success: true, missing_on_primary: true)
      create(:geo_job_artifact_registry, :with_artifact, success: true)

      create(:geo_job_artifact_registry, :with_artifact, success: true, missing_on_primary: true)

      expect(subject.job_artifacts_synced_missing_on_primary_count).to eq(1)
    end
  end

350 351 352
  # Disable transactions via :delete method because a foreign table
  # can't see changes inside a transaction of a different connection.
  describe '#job_artifacts_failed_count', :delete do
353 354 355 356 357
    it 'counts failed job artifacts' do
      # These should be ignored
      create(:geo_file_registry, success: false)
      create(:geo_file_registry, :avatar, success: false)
      create(:geo_file_registry, file_type: :attachment, success: false)
358
      create(:geo_job_artifact_registry, :with_artifact, success: true)
359

360
      create(:geo_job_artifact_registry, :with_artifact, success: false)
361 362 363 364 365

      expect(subject.job_artifacts_failed_count).to eq(1)
    end
  end

366 367 368 369
  # Disable transactions via :delete method because a foreign table
  # can't see changes inside a transaction of a different connection.
  describe '#job_artifacts_synced_in_percentage', :delete do
    context 'when artifacts are available' do
370 371 372 373
      before do
        [project_1, project_2, project_3, project_4].each_with_index do |project, index|
          build = create(:ci_build, project: project)
          job_artifact = create(:ci_job_artifact, job: build)
374 375

          create(:geo_job_artifact_registry, success: index.even?, artifact_id: job_artifact.id)
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
        end
      end

      it 'returns the right percentage with no group restrictions' do
        expect(subject.job_artifacts_synced_in_percentage).to be_within(0.0001).of(50)
      end

      it 'returns the right percentage with group restrictions' do
        secondary.update_attribute(:namespaces, [group])

        expect(subject.job_artifacts_synced_in_percentage).to be_within(0.0001).of(50)
      end
    end

    it 'returns 0 when no artifacts are available' do
      expect(subject.job_artifacts_synced_in_percentage).to eq(0)
    end
  end

395 396 397 398
  describe '#repositories_failed_count' do
    before do
      create(:geo_project_registry, :sync_failed, project: project_1)
      create(:geo_project_registry, :sync_failed, project: project_3)
399 400
      create(:geo_project_registry, :repository_syncing, project: project_4)
      create(:geo_project_registry, :wiki_syncing)
401 402 403 404 405 406 407
    end

    it 'returns the right number of failed repos with no group restrictions' do
      expect(subject.repositories_failed_count).to eq(2)
    end

    it 'returns the right number of failed repos with group restrictions' do
408
      secondary.update!(selective_sync_type: 'namespaces', namespaces: [group])
409 410 411 412 413

      expect(subject.repositories_failed_count).to eq(1)
    end
  end

414 415 416 417 418 419 420 421 422 423 424 425 426
  describe '#wikis_failed_count' do
    before do
      create(:geo_project_registry, :sync_failed, project: project_1)
      create(:geo_project_registry, :sync_failed, project: project_3)
      create(:geo_project_registry, :repository_syncing, project: project_4)
      create(:geo_project_registry, :wiki_syncing)
    end

    it 'returns the right number of failed repos with no group restrictions' do
      expect(subject.wikis_failed_count).to eq(2)
    end

    it 'returns the right number of failed repos with group restrictions' do
427
      secondary.update!(selective_sync_type: 'namespaces', namespaces: [group])
428 429 430 431 432

      expect(subject.wikis_failed_count).to eq(1)
    end
  end

433
  describe '#repositories_synced_in_percentage' do
434
    it 'returns 0 when no projects are available' do
435 436 437
      expect(subject.repositories_synced_in_percentage).to eq(0)
    end

438 439 440 441 442 443
    it 'returns 0 when project count is unknown' do
      allow(subject).to receive(:repositories_count).and_return(nil)

      expect(subject.repositories_synced_in_percentage).to eq(0)
    end

444 445
    it 'returns the right percentage with no group restrictions' do
      create(:geo_project_registry, :synced, project: project_1)
446 447 448

      expect(subject.repositories_synced_in_percentage).to be_within(0.0001).of(25)
    end
449 450

    it 'returns the right percentage with group restrictions' do
451
      secondary.update!(selective_sync_type: 'namespaces', namespaces: [group])
452 453 454 455
      create(:geo_project_registry, :synced, project: project_1)

      expect(subject.repositories_synced_in_percentage).to be_within(0.0001).of(50)
    end
456 457
  end

458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
  # Disable transactions via :delete method because a foreign table
  # can't see changes inside a transaction of a different connection.
  describe '#wikis_synced_in_percentage', :delete do
    it 'returns 0 when no projects are available' do
      expect(subject.wikis_synced_in_percentage).to eq(0)
    end

    it 'returns 0 when project count is unknown' do
      allow(subject).to receive(:wikis_count).and_return(nil)

      expect(subject.wikis_synced_in_percentage).to eq(0)
    end

    it 'returns the right percentage with no group restrictions' do
      create(:geo_project_registry, :synced, project: project_1)

      expect(subject.wikis_synced_in_percentage).to be_within(0.0001).of(25)
    end

    it 'returns the right percentage with group restrictions' do
478
      secondary.update!(selective_sync_type: 'namespaces', namespaces: [group])
479 480 481 482 483 484
      create(:geo_project_registry, :synced, project: project_1)

      expect(subject.wikis_synced_in_percentage).to be_within(0.0001).of(50)
    end
  end

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
  describe '#replication_slots_used_count' do
    it 'returns the right number of used replication slots' do
      stub_current_geo_node(primary)
      allow(primary).to receive(:replication_slots_used_count).and_return(1)

      expect(subject.replication_slots_used_count).to eq(1)
    end
  end

  describe '#replication_slots_used_in_percentage' do
    it 'returns 0 when no replication slots are available' do
      expect(subject.replication_slots_used_in_percentage).to eq(0)
    end

    it 'returns 0 when replication slot count is unknown' do
      allow(subject).to receive(:replication_slot_count).and_return(nil)

      expect(subject.replication_slots_used_in_percentage).to eq(0)
    end

    it 'returns the right percentage' do
      stub_current_geo_node(primary)
      allow(subject).to receive(:replication_slots_count).and_return(2)
      allow(subject).to receive(:replication_slots_used_count).and_return(1)

      expect(subject.replication_slots_used_in_percentage).to be_within(0.0001).of(50)
    end
  end

  describe '#replication_slots_max_retained_wal_bytes' do
    it 'returns the number of bytes replication slots are using' do
      stub_current_geo_node(primary)
      allow(primary).to receive(:replication_slots_max_retained_wal_bytes).and_return(2.megabytes)

      expect(subject.replication_slots_max_retained_wal_bytes).to eq(2.megabytes)
    end
521 522 523 524 525 526 527

    it 'handles large values' do
      stub_current_geo_node(primary)
      allow(primary).to receive(:replication_slots_max_retained_wal_bytes).and_return(900.gigabytes)

      expect(subject.replication_slots_max_retained_wal_bytes).to eq(900.gigabytes)
    end
528 529
  end

530 531 532 533
  describe '#repositories_checksummed_count' do
    before do
      stub_current_geo_node(primary)
    end
534

535 536 537
    it 'returns the right number of checksummed repositories' do
      create(:repository_state, :repository_verified)
      create(:repository_state, :repository_verified)
538

539 540
      expect(subject.repositories_checksummed_count).to eq(2)
    end
541

542
    it 'returns existing value when feature flag is off' do
543
      allow(Gitlab::Geo).to receive(:repository_verification_enabled?).and_return(false)
544
      create(:geo_node_status, :healthy, geo_node: primary)
545

546
      expect(subject.repositories_checksummed_count).to eq(600)
547
    end
548
  end
549

550 551 552 553
  describe '#repositories_checksum_failed_count' do
    before do
      stub_current_geo_node(primary)
    end
554

555 556 557
    it 'returns the right number of failed repositories' do
      create(:repository_state, :repository_failed)
      create(:repository_state, :repository_failed)
558

559 560
      expect(subject.repositories_checksum_failed_count).to eq(2)
    end
561

562
    it 'returns existing value when feature flag if off' do
563
      allow(Gitlab::Geo).to receive(:repository_verification_enabled?).and_return(false)
564
      create(:geo_node_status, :healthy, geo_node: primary)
565

566
      expect(subject.repositories_checksum_failed_count).to eq(120)
567 568 569
    end
  end

570 571 572 573
  describe '#repositories_checksummed_in_percentage' do
    before do
      stub_current_geo_node(primary)
    end
574

575 576 577
    it 'returns 0 when no projects are available' do
      expect(subject.repositories_checksummed_in_percentage).to eq(0)
    end
578

579 580
    it 'returns 0 when project count is unknown' do
      allow(subject).to receive(:repositories_count).and_return(nil)
581

582 583
      expect(subject.repositories_checksummed_in_percentage).to eq(0)
    end
584

585 586 587 588
    it 'returns the right percentage' do
      create(:repository_state, :repository_verified, project: project_1)

      expect(subject.repositories_checksummed_in_percentage).to be_within(0.0001).of(25)
589
    end
590
  end
591

592 593 594 595
  describe '#wikis_checksummed_count' do
    before do
      stub_current_geo_node(primary)
    end
596

597 598 599
    it 'returns the right number of checksummed wikis' do
      create(:repository_state, :wiki_verified)
      create(:repository_state, :wiki_verified)
600

601 602
      expect(subject.wikis_checksummed_count).to eq(2)
    end
603

604
    it 'returns existing value when feature flag if off' do
605
      allow(Gitlab::Geo).to receive(:repository_verification_enabled?).and_return(false)
606
      create(:geo_node_status, :healthy, geo_node: primary)
607

608
      expect(subject.wikis_checksummed_count).to eq(585)
609 610 611
    end
  end

612 613 614 615
  describe '#wikis_checksum_failed_count' do
    before do
      stub_current_geo_node(primary)
    end
616

617 618 619
    it 'returns the right number of failed wikis' do
      create(:repository_state, :wiki_failed)
      create(:repository_state, :wiki_failed)
620

621 622
      expect(subject.wikis_checksum_failed_count).to eq(2)
    end
623

624
    it 'returns existing value when feature flag if off' do
625
      allow(Gitlab::Geo).to receive(:repository_verification_enabled?).and_return(false)
626
      create(:geo_node_status, :healthy, geo_node: primary)
627

628
      expect(subject.wikis_checksum_failed_count).to eq(55)
629
    end
630
  end
631

632 633 634 635
  describe '#wikis_checksummed_in_percentage', :delete do
    before do
      stub_current_geo_node(primary)
    end
636

637 638 639
    it 'returns 0 when no projects are available' do
      expect(subject.wikis_checksummed_in_percentage).to eq(0)
    end
640

641 642 643 644 645
    it 'returns 0 when project count is unknown' do
      allow(subject).to receive(:wikis_count).and_return(nil)

      expect(subject.wikis_checksummed_in_percentage).to eq(0)
    end
646

647 648
    it 'returns the right percentage' do
      create(:repository_state, :wiki_verified, project: project_1)
649

650
      expect(subject.wikis_checksummed_in_percentage).to be_within(0.0001).of(25)
651 652 653
    end
  end

654 655 656 657
  describe '#repositories_verified_count' do
    before do
      stub_current_geo_node(secondary)
    end
658

659 660 661
    it 'returns the right number of verified repositories' do
      create(:geo_project_registry, :repository_verified)
      create(:geo_project_registry, :repository_verified)
662

663 664
      expect(subject.repositories_verified_count).to eq(2)
    end
665

666
    it 'returns existing value when feature flag if off' do
667
      allow(Gitlab::Geo).to receive(:repository_verification_enabled?).and_return(false)
668
      create(:geo_node_status, :healthy, geo_node: secondary)
669

670
      expect(subject.repositories_verified_count).to eq(501)
671
    end
672
  end
673

674 675 676 677
  describe '#repositories_checksum_mismatch_count' do
    before do
      stub_current_geo_node(secondary)
    end
678

679 680 681 682
    it 'returns the right number of repositories that checksum mismatch' do
      create(:geo_project_registry, :repository_checksum_mismatch)
      create(:geo_project_registry, :repository_verification_failed)
      create(:geo_project_registry, :repository_verified)
683

684 685
      expect(subject.repositories_checksum_mismatch_count).to eq(1)
    end
686

687
    it 'returns existing value when feature flag if off' do
688
      allow(Gitlab::Geo).to receive(:repository_verification_enabled?).and_return(false)
689
      create(:geo_node_status, :healthy, geo_node: secondary)
690

691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
      expect(subject.repositories_checksum_mismatch_count).to eq(15)
    end
  end

  describe '#repositories_verification_failed_count' do
    before do
      stub_current_geo_node(secondary)
    end

    it 'returns the right number of failed repositories' do
      create(:geo_project_registry, :repository_verification_failed)
      create(:geo_project_registry, :repository_verification_failed)

      expect(subject.repositories_verification_failed_count).to eq(2)
    end

    it 'returns existing value when feature flag if off' do
708
      allow(Gitlab::Geo).to receive(:repository_verification_enabled?).and_return(false)
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
      create(:geo_node_status, :healthy, geo_node: secondary)

      expect(subject.repositories_verification_failed_count).to eq(100)
    end
  end

  describe '#wikis_verified_count' do
    before do
      stub_current_geo_node(secondary)
    end

    it 'returns the right number of verified wikis' do
      create(:geo_project_registry, :wiki_verified)
      create(:geo_project_registry, :wiki_verified)

      expect(subject.wikis_verified_count).to eq(2)
    end

    it 'returns existing value when feature flag if off' do
728
      allow(Gitlab::Geo).to receive(:repository_verification_enabled?).and_return(false)
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
      create(:geo_node_status, :healthy, geo_node: secondary)

      expect(subject.wikis_verified_count).to eq(499)
    end
  end

  describe '#wikis_checksum_mismatch_count' do
    before do
      stub_current_geo_node(secondary)
    end

    it 'returns the right number of wikis that checksum mismatch' do
      create(:geo_project_registry, :wiki_checksum_mismatch)
      create(:geo_project_registry, :wiki_verification_failed)
      create(:geo_project_registry, :wiki_verified)

      expect(subject.wikis_checksum_mismatch_count).to eq(1)
    end

    it 'returns existing value when feature flag if off' do
749
      allow(Gitlab::Geo).to receive(:repository_verification_enabled?).and_return(false)
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
      create(:geo_node_status, :healthy, geo_node: secondary)

      expect(subject.wikis_checksum_mismatch_count).to eq(10)
    end
  end

  describe '#wikis_verification_failed_count' do
    before do
      stub_current_geo_node(secondary)
    end

    it 'returns the right number of failed wikis' do
      create(:geo_project_registry, :wiki_verification_failed)
      create(:geo_project_registry, :wiki_verification_failed)

      expect(subject.wikis_verification_failed_count).to eq(2)
    end

    it 'returns existing value when feature flag if off' do
769
      allow(Gitlab::Geo).to receive(:repository_verification_enabled?).and_return(false)
770 771 772
      create(:geo_node_status, :healthy, geo_node: secondary)

      expect(subject.wikis_verification_failed_count).to eq(99)
773 774 775
    end
  end

776
  describe '#last_event_id and #last_event_date' do
777 778
    it 'returns nil when no events are available' do
      expect(subject.last_event_id).to be_nil
779
      expect(subject.last_event_date).to be_nil
780 781 782
    end

    it 'returns the latest event' do
783
      created_at = Date.today.to_time(:utc)
784 785 786
      event = create(:geo_event_log, created_at: created_at)

      expect(subject.last_event_id).to eq(event.id)
787
      expect(subject.last_event_date).to eq(created_at)
788 789 790
    end
  end

791
  describe '#cursor_last_event_id and #cursor_last_event_date' do
792 793
    it 'returns nil when no events are available' do
      expect(subject.cursor_last_event_id).to be_nil
794
      expect(subject.cursor_last_event_date).to be_nil
795 796
    end

797 798
    it 'returns the latest event ID if secondary' do
      allow(Gitlab::Geo).to receive(:secondary?).and_return(true)
799 800 801 802
      event = create(:geo_event_log_state)

      expect(subject.cursor_last_event_id).to eq(event.event_id)
    end
803 804

    it "doesn't attempt to retrieve cursor if primary" do
805
      stub_current_geo_node(primary)
806
      create(:geo_event_log_state)
807

808
      expect(subject.cursor_last_event_date).to eq(nil)
809 810
      expect(subject.cursor_last_event_id).to eq(nil)
    end
811 812
  end

813
  describe '#version' do
814
    it { expect(status.version).to eq(Gitlab::VERSION) }
815 816 817
  end

  describe '#revision' do
818
    it {  expect(status.revision).to eq(Gitlab.revision) }
819 820
  end

821 822 823 824 825 826 827 828 829 830 831
  describe '#[]' do
    it 'returns values for each attribute' do
      expect(subject[:repositories_count]).to eq(4)
      expect(subject[:repositories_synced_count]).to eq(0)
    end

    it 'raises an error for invalid attributes' do
      expect { subject[:testme] }.to raise_error(NoMethodError)
    end
  end

832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
  shared_examples 'timestamp parameters' do |timestamp_column, date_column|
    it 'returns the value it was assigned via UNIX timestamp' do
      now = Time.now.beginning_of_day.utc
      subject.update_attribute(timestamp_column, now.to_i)

      expect(subject.public_send(date_column)).to eq(now)
      expect(subject.public_send(timestamp_column)).to eq(now.to_i)
    end
  end

  describe '#last_successful_status_check_timestamp' do
    it_behaves_like 'timestamp parameters', :last_successful_status_check_timestamp, :last_successful_status_check_at
  end

  describe '#last_event_timestamp' do
    it_behaves_like 'timestamp parameters', :last_event_timestamp, :last_event_date
  end

  describe '#cursor_last_event_timestamp' do
    it_behaves_like 'timestamp parameters', :cursor_last_event_timestamp, :cursor_last_event_date
  end

854 855
  describe '#storage_shards' do
    it "returns the current node's shard config" do
856
      expect(subject[:storage_shards].as_json).to eq(StorageShard.all.as_json)
857 858 859
    end
  end

860 861 862 863
  describe '#from_json' do
    it 'returns a new GeoNodeStatus excluding parameters' do
      status = create(:geo_node_status)

864
      data = GeoNodeStatusSerializer.new.represent(status).as_json
865
      data['id'] = 10000
866

Stan Hu's avatar
Stan Hu committed
867
      result = described_class.from_json(data)
868 869 870

      expect(result.id).to be_nil
      expect(result.attachments_count).to eq(status.attachments_count)
871
      expect(result.cursor_last_event_date).to eq(Time.at(status.cursor_last_event_timestamp))
872
      expect(result.storage_shards.count).to eq(Settings.repositories.storages.count)
873 874
    end
  end
875 876

  describe '#storage_shards_match?' do
877
    it 'returns false if no shard data is available for secondary' do
878
      stub_primary_node
879 880 881 882 883
      stub_current_geo_node(secondary)

      status = create(:geo_node_status, geo_node: secondary, storage_configuration_digest: 'bc11119c101846c20367fff34ce9fffa9b05aab8')

      expect(status.storage_shards_match?).to be false
884
    end
885

886 887 888
    it 'returns true even if no shard data is available for secondary' do
      stub_secondary_node
      stub_current_geo_node(primary)
889

890
      status = create(:geo_node_status, geo_node: primary, storage_configuration_digest: 'bc11119c101846c20367fff34ce9fffa9b05aab8')
891

892
      expect(status.storage_shards_match?).to be true
893 894
    end

895
    it 'returns false if the storage shards do not match' do
896 897 898
      stub_primary_node
      stub_current_geo_node(secondary)
      create(:geo_node_status, geo_node: primary, storage_configuration_digest: 'aea7849c10b886c202676ff34ce9fdf0940567b8')
899

900
      status = create(:geo_node_status, geo_node: secondary, storage_configuration_digest: 'bc11119c101846c20367fff34ce9fffa9b05aab8')
901

902
      expect(status.storage_shards_match?).to be false
903 904
    end
  end
905 906 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 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970

  describe '#repositories_checked_count' do
    before do
      stub_application_setting(repository_checks_enabled: true)
    end

    context 'current is a Geo primary' do
      before do
        stub_current_geo_node(primary)
      end

      it 'counts the number of repo checked projects' do
        project_1.update!(last_repository_check_at: 2.minutes.ago)
        project_2.update!(last_repository_check_at: 7.minutes.ago)

        expect(status.repositories_checked_count).to eq(2)
      end
    end

    context 'current is a Geo secondary' do
      before do
        stub_current_geo_node(secondary)
      end

      it 'counts the number of repo checked projects' do
        create(:geo_project_registry, project: project_1, last_repository_check_at: 2.minutes.ago)
        create(:geo_project_registry, project: project_2, last_repository_check_at: 7.minutes.ago)
        create(:geo_project_registry, project: project_3)

        expect(status.repositories_checked_count).to eq(2)
      end
    end
  end

  describe '#repositories_checked_failed_count' do
    before do
      stub_application_setting(repository_checks_enabled: true)
    end

    context 'current is a Geo primary' do
      before do
        stub_current_geo_node(primary)
      end

      it 'counts the number of repo check failed projects' do
        project_1.update!(last_repository_check_at: 2.minutes.ago, last_repository_check_failed: true)
        project_2.update!(last_repository_check_at: 7.minutes.ago, last_repository_check_failed: false)

        expect(status.repositories_checked_failed_count).to eq(1)
      end
    end

    context 'current is a Geo secondary' do
      before do
        stub_current_geo_node(secondary)
      end

      it 'counts the number of repo check failed projects' do
        create(:geo_project_registry, project: project_1, last_repository_check_at: 2.minutes.ago, last_repository_check_failed: true)
        create(:geo_project_registry, project: project_2, last_repository_check_at: 7.minutes.ago, last_repository_check_failed: false)
        create(:geo_project_registry, project: project_3)

        expect(status.repositories_checked_failed_count).to eq(1)
      end
    end
  end
971
end