BigW Consortium Gitlab

migration_helpers_spec.rb 4.88 KB
Newer Older
1 2 3 4
require 'spec_helper'

describe Gitlab::Database::MigrationHelpers, lib: true do
  let(:model) do
5 6 7
    ActiveRecord::Migration.new.extend(
      Gitlab::Database::MigrationHelpers
    )
8 9
  end

10 11
  before { allow(model).to receive(:puts) }

12 13 14 15
  describe '#add_concurrent_index' do
    context 'outside a transaction' do
      before do
        expect(model).to receive(:transaction_open?).and_return(false)
16 17 18 19

        unless Gitlab::Database.postgresql?
          allow_any_instance_of(Gitlab::Database::MigrationHelpers).to receive(:disable_statement_timeout)
        end
20 21 22
      end

      context 'using PostgreSQL' do
23
        before { expect(Gitlab::Database).to receive(:postgresql?).and_return(true) }
24

25
        it 'creates the index concurrently' do
26 27 28 29 30
          expect(model).to receive(:add_index).
            with(:users, :foo, algorithm: :concurrently)

          model.add_concurrent_index(:users, :foo)
        end
31 32 33 34 35 36 37

        it 'creates unique index concurrently' do
          expect(model).to receive(:add_index).
            with(:users, :foo, { algorithm: :concurrently, unique: true })

          model.add_concurrent_index(:users, :foo, unique: true)
        end
38 39 40 41 42 43 44
      end

      context 'using MySQL' do
        it 'creates a regular index' do
          expect(Gitlab::Database).to receive(:postgresql?).and_return(false)

          expect(model).to receive(:add_index).
45
            with(:users, :foo, {})
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

          model.add_concurrent_index(:users, :foo)
        end
      end
    end

    context 'inside a transaction' do
      it 'raises RuntimeError' do
        expect(model).to receive(:transaction_open?).and_return(true)

        expect { model.add_concurrent_index(:users, :foo) }.
          to raise_error(RuntimeError)
      end
    end
  end

  describe '#update_column_in_batches' do
    before do
      create_list(:empty_project, 5)
    end

    it 'updates all the rows in a table' do
      model.update_column_in_batches(:projects, :import_error, 'foo')

      expect(Project.where(import_error: 'foo').count).to eq(5)
    end
72 73 74 75 76 77

    it 'updates boolean values correctly' do
      model.update_column_in_batches(:projects, :archived, true)

      expect(Project.where(archived: true).count).to eq(5)
    end
78 79 80 81 82 83 84 85 86 87 88 89

    context 'when a block is supplied' do
      it 'yields an Arel table and query object to the supplied block' do
        first_id = Project.first.id

        model.update_column_in_batches(:projects, :archived, true) do |t, query|
          query.where(t[:id].eq(first_id))
        end

        expect(Project.where(archived: true).count).to eq(1)
      end
    end
90 91 92 93 94 95 96
  end

  describe '#add_column_with_default' do
    context 'outside of a transaction' do
      before do
        expect(model).to receive(:transaction_open?).and_return(false)

97
        expect(model).to receive(:transaction).and_yield
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

        expect(model).to receive(:add_column).
          with(:projects, :foo, :integer, default: nil)

        expect(model).to receive(:change_column_default).
          with(:projects, :foo, 10)
      end

      it 'adds the column while allowing NULL values' do
        expect(model).to receive(:update_column_in_batches).
          with(:projects, :foo, 10)

        expect(model).not_to receive(:change_column_null)

        model.add_column_with_default(:projects, :foo, :integer,
                                      default: 10,
                                      allow_null: true)
      end

      it 'adds the column while not allowing NULL values' do
        expect(model).to receive(:update_column_in_batches).
          with(:projects, :foo, 10)

        expect(model).to receive(:change_column_null).
          with(:projects, :foo, false)

        model.add_column_with_default(:projects, :foo, :integer, default: 10)
      end

      it 'removes the added column whenever updating the rows fails' do
        expect(model).to receive(:update_column_in_batches).
          with(:projects, :foo, 10).
          and_raise(RuntimeError)

        expect(model).to receive(:remove_column).
          with(:projects, :foo)

135
        expect do
136
          model.add_column_with_default(:projects, :foo, :integer, default: 10)
137
        end.to raise_error(RuntimeError)
138
      end
139 140 141 142 143 144 145 146 147 148 149 150 151

      it 'removes the added column whenever changing a column NULL constraint fails' do
        expect(model).to receive(:change_column_null).
          with(:projects, :foo, false).
          and_raise(RuntimeError)

        expect(model).to receive(:remove_column).
          with(:projects, :foo)

        expect do
          model.add_column_with_default(:projects, :foo, :integer, default: 10)
        end.to raise_error(RuntimeError)
      end
152 153 154 155 156 157
    end

    context 'inside a transaction' do
      it 'raises RuntimeError' do
        expect(model).to receive(:transaction_open?).and_return(true)

158
        expect do
159
          model.add_column_with_default(:projects, :foo, :integer, default: 10)
160
        end.to raise_error(RuntimeError)
161 162 163 164
      end
    end
  end
end