summaryrefslogtreecommitdiff
path: root/spec/models/concerns/safely_change_column_default_spec.rb
blob: 36782170eafca48107bac86a0feabc44e04b9cd8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe SafelyChangeColumnDefault, feature_category: :database do
  include Gitlab::Database::DynamicModelHelpers
  before do
    ApplicationRecord.connection.execute(<<~SQL)
      CREATE TABLE _test_gitlab_main_data(
        id bigserial primary key not null,
        value bigint default 1
      );
    SQL
  end

  let!(:model) do
    define_batchable_model('_test_gitlab_main_data', connection: ApplicationRecord.connection).tap do |model|
      model.include(described_class)
      model.columns_changing_default(:value)
      model.columns # Force the schema cache to populate
    end
  end

  def alter_default(new_default)
    ApplicationRecord.connection.execute(<<~SQL)
      ALTER TABLE _test_gitlab_main_data ALTER COLUMN value SET DEFAULT #{new_default}
    SQL
  end

  def recorded_insert_queries(&block)
    recorder = ActiveRecord::QueryRecorder.new
    recorder.record(&block)

    recorder.log.select { |q| q.include?('INSERT INTO') }
  end

  def query_includes_value_column?(query)
    parsed = PgQuery.parse(query)
    parsed.tree.stmts.first.stmt.insert_stmt.cols.any? { |node| node.res_target.name == 'value' }
  end

  it 'forces the column to be written on a change' do
    queries = recorded_insert_queries do
      model.create!(value: 1)
    end

    expect(queries.length).to eq(1)

    expect(query_includes_value_column?(queries.first)).to be_truthy
  end

  it 'does not write the column without a change' do
    queries = recorded_insert_queries do
      model.create!
    end

    expect(queries.length).to eq(1)
    expect(query_includes_value_column?(queries.first)).to be_falsey
  end

  it 'does not send the old column value if the default has changed' do
    alter_default(2)
    model.create!

    expect(model.pluck(:value)).to contain_exactly(2)
  end

  it 'prevents writing new default in place of the old default' do
    alter_default(2)

    model.create!(value: 1)

    expect(model.pluck(:value)).to contain_exactly(1)
  end
end