1# frozen_string_literal: true
2
3require 'spec_helper'
4
5RSpec.describe Gitlab::Database::BackgroundMigrationJob do
6  it_behaves_like 'having unique enum values'
7
8  describe '.for_migration_execution' do
9    let!(:job1) { create(:background_migration_job) }
10    let!(:job2) { create(:background_migration_job, arguments: ['hi', 2]) }
11    let!(:job3) { create(:background_migration_job, class_name: 'OtherJob', arguments: ['hi', 2]) }
12
13    it 'returns jobs matching class_name and arguments' do
14      relation = described_class.for_migration_execution('TestJob', ['hi', 2])
15
16      expect(relation.count).to eq(1)
17      expect(relation.first).to have_attributes(class_name: 'TestJob', arguments: ['hi', 2])
18    end
19
20    it 'normalizes class names by removing leading ::' do
21      relation = described_class.for_migration_execution('::TestJob', ['hi', 2])
22
23      expect(relation.count).to eq(1)
24      expect(relation.first).to have_attributes(class_name: 'TestJob', arguments: ['hi', 2])
25    end
26  end
27
28  describe '.for_partitioning_migration' do
29    let!(:job1) { create(:background_migration_job, arguments: [1, 100, 'other_table']) }
30    let!(:job2) { create(:background_migration_job, arguments: [1, 100, 'audit_events']) }
31    let!(:job3) { create(:background_migration_job, class_name: 'OtherJob', arguments: [1, 100, 'audit_events']) }
32
33    it 'returns jobs matching class_name and the table_name job argument' do
34      relation = described_class.for_partitioning_migration('TestJob', 'audit_events')
35
36      expect(relation.count).to eq(1)
37      expect(relation.first).to have_attributes(class_name: 'TestJob', arguments: [1, 100, 'audit_events'])
38    end
39
40    it 'normalizes class names by removing leading ::' do
41      relation = described_class.for_partitioning_migration('::TestJob', 'audit_events')
42
43      expect(relation.count).to eq(1)
44      expect(relation.first).to have_attributes(class_name: 'TestJob', arguments: [1, 100, 'audit_events'])
45    end
46  end
47
48  describe '.mark_all_as_succeeded' do
49    let!(:job1) { create(:background_migration_job, arguments: [1, 100]) }
50    let!(:job2) { create(:background_migration_job, arguments: [1, 100]) }
51    let!(:job3) { create(:background_migration_job, arguments: [101, 200]) }
52    let!(:job4) { create(:background_migration_job, class_name: 'OtherJob', arguments: [1, 100]) }
53
54    it 'marks all matching jobs as succeeded' do
55      expect { described_class.mark_all_as_succeeded('TestJob', [1, 100]) }
56        .to change { described_class.succeeded.count }.from(0).to(2)
57
58      expect(job1.reload).to be_succeeded
59      expect(job2.reload).to be_succeeded
60      expect(job3.reload).to be_pending
61      expect(job4.reload).to be_pending
62    end
63
64    it 'normalizes class_names by removing leading ::' do
65      expect { described_class.mark_all_as_succeeded('::TestJob', [1, 100]) }
66        .to change { described_class.succeeded.count }.from(0).to(2)
67
68      expect(job1.reload).to be_succeeded
69      expect(job2.reload).to be_succeeded
70      expect(job3.reload).to be_pending
71      expect(job4.reload).to be_pending
72    end
73
74    it 'returns the number of jobs updated' do
75      expect(described_class.succeeded.count).to eq(0)
76
77      jobs_updated = described_class.mark_all_as_succeeded('::TestJob', [1, 100])
78
79      expect(jobs_updated).to eq(2)
80      expect(described_class.succeeded.count).to eq(2)
81    end
82
83    context 'when previous matching jobs have already succeeded' do
84      let(:initial_time) { Time.now.round }
85      let!(:job1) { create(:background_migration_job, :succeeded, created_at: initial_time, updated_at: initial_time) }
86
87      it 'does not update non-pending jobs' do
88        travel_to(initial_time + 1.day) do
89          expect { described_class.mark_all_as_succeeded('TestJob', [1, 100]) }
90            .to change { described_class.succeeded.count }.from(1).to(2)
91        end
92
93        expect(job1.reload.updated_at).to eq(initial_time)
94        expect(job2.reload).to be_succeeded
95        expect(job3.reload).to be_pending
96        expect(job4.reload).to be_pending
97      end
98    end
99  end
100
101  describe '#class_name=' do
102    context 'when the class_name is given without the leading ::' do
103      it 'sets the class_name to the given value' do
104        job = described_class.new(class_name: 'TestJob')
105
106        expect(job.class_name).to eq('TestJob')
107      end
108    end
109
110    context 'when the class_name is given with the leading ::' do
111      it 'removes the leading :: when setting the class_name' do
112        job = described_class.new(class_name: '::TestJob')
113
114        expect(job.class_name).to eq('TestJob')
115      end
116    end
117
118    context 'when the value is nil' do
119      it 'sets the class_name to nil' do
120        job = described_class.new(class_name: nil)
121
122        expect(job.class_name).to be_nil
123      end
124    end
125
126    context 'when the values is blank' do
127      it 'sets the class_name to the given value' do
128        job = described_class.new(class_name: '')
129
130        expect(job.class_name).to eq('')
131      end
132    end
133  end
134end
135