summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/database/bulk_update_spec.rb
blob: fa519cffd6bfffc4790780aad1f071cc14bd8a76 (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
76
77
78
79
80
81
82
83
84
85
86
87
88
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
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::Database::BulkUpdate do
  describe 'error states' do
    let(:columns) { %i[title] }

    let_it_be(:mapping) do
      create_default(:user)
      create_default(:project)

      i_a, i_b = create_list(:issue, 2)

      {
        i_a => { title: 'Issue a' },
        i_b => { title: 'Issue b' }
      }
    end

    it 'does not raise errors on valid inputs' do
      expect { described_class.execute(columns, mapping) }.not_to raise_error
    end

    it 'expects a non-empty list of column names' do
      expect { described_class.execute([], mapping) }.to raise_error(ArgumentError)
    end

    it 'expects all columns to be symbols' do
      expect { described_class.execute([1], mapping) }.to raise_error(ArgumentError)
    end

    it 'expects all columns to be valid columns on the tables' do
      expect { described_class.execute([:foo], mapping) }.to raise_error(ArgumentError)
    end

    it 'refuses to set ID' do
      expect { described_class.execute([:id], mapping) }.to raise_error(ArgumentError)
    end

    it 'expects a non-empty mapping' do
      expect { described_class.execute(columns, []) }.to raise_error(ArgumentError)
    end

    it 'expects all map values to be Hash instances' do
      bad_map = mapping.merge(build(:issue) => 2)

      expect { described_class.execute(columns, bad_map) }.to raise_error(ArgumentError)
    end
  end

  it 'is possible to update all objects in a single query' do
    users = create_list(:user, 3)
    mapping = users.zip(%w[foo bar baz]).to_h do |u, name|
      [u, { username: name, admin: true }]
    end

    expect do
      described_class.execute(%i[username admin], mapping)
    end.not_to exceed_query_limit(1)

    # We have optimistically updated the values
    expect(users).to all(be_admin)
    expect(users.map(&:username)).to eq(%w[foo bar baz])

    users.each(&:reset)

    # The values are correct on reset
    expect(users).to all(be_admin)
    expect(users.map(&:username)).to eq(%w[foo bar baz])
  end

  it 'is possible to update heterogeneous sets' do
    create_default(:user)
    create_default(:project)

    mr_a = create(:merge_request)
    i_a, i_b = create_list(:issue, 2)

    mapping = {
      mr_a => { title: 'MR a' },
      i_a => { title: 'Issue a' },
      i_b => { title: 'Issue b' }
    }

    expect do
      described_class.execute(%i[title], mapping)
    end.not_to exceed_query_limit(2)

    expect([mr_a, i_a, i_b].map { |x| x.reset.title })
      .to eq(['MR a', 'Issue a', 'Issue b'])
  end

  context 'validates prepared_statements support', :reestablished_active_record_base,
          :suppress_gitlab_schemas_validate_connection do
    using RSpec::Parameterized::TableSyntax

    where(:prepared_statements) do
      [false, true]
    end

    before do
      configuration_hash = ActiveRecord::Base.connection_db_config.configuration_hash

      ActiveRecord::Base.establish_connection( # rubocop: disable Database/EstablishConnection
        configuration_hash.merge(prepared_statements: prepared_statements)
      )
    end

    with_them do
      it 'sets multiple values' do
        create_default(:user)
        create_default(:project)

        i_a, i_b = create_list(:issue, 2)

        mapping = {
          i_a => { title: 'Issue a' },
          i_b => { title: 'Issue b' }
        }

        described_class.execute(%i[title], mapping)

        expect([i_a, i_b].map { |x| x.reset.title })
          .to eq(['Issue a', 'Issue b'])
      end
    end
  end
end