Welcome to mirror list, hosted at ThFree Co, Russian Federation.

active_record_relation_union_reset_spec.rb « initializers « spec - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 013dfa1b49be2abe8304b5ca76d35eb933a9703a (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
130
131
132
133
134
# frozen_string_literal: true

require 'spec_helper'

# rubocop:disable Database/MultipleDatabases
RSpec.describe ActiveRecordRelationUnionReset, :delete, feature_category: :shared do
  let(:test_unioned_model) do
    Class.new(ActiveRecord::Base) do
      include FromUnion

      self.table_name = '_test_unioned_model'

      def self.name
        'TestUnion'
      end
    end
  end

  before(:context) do
    ActiveRecord::Base.connection.execute(<<~SQL)
      CREATE TABLE _test_unioned_model (
        id serial NOT NULL PRIMARY KEY,
        created_at timestamptz NOT NULL
      );
    SQL
  end

  after(:context) do
    ActiveRecord::Base.connection.execute(<<~SQL)
      DROP TABLE _test_unioned_model
    SQL
  end

  context 'with mismatched columns due to schema cache' do
    def load_query
      scopes = [
        test_unioned_model.select('*'),
        test_unioned_model.select(test_unioned_model.column_names.join(','))
      ]

      test_unioned_model.from_union(scopes).load
    end

    before do
      load_query

      ActiveRecord::Base.connection.execute(<<~SQL)
        ALTER TABLE _test_unioned_model ADD COLUMN _test_new_column int;
      SQL
    end

    after do
      ActiveRecord::Base.connection.execute(<<~SQL)
        ALTER TABLE _test_unioned_model DROP COLUMN _test_new_column;
      SQL

      test_unioned_model.reset_column_information
    end

    it 'resets column information when encountering an UNION error' do
      expect do
        load_query
      end.to raise_error(ActiveRecord::StatementInvalid, /must have the same number of columns/)
        .and change { test_unioned_model.column_names }.from(%w[id created_at]).to(%w[id created_at _test_new_column])

      # Subsequent query load from new schema cache, so no more error
      expect do
        load_query
      end.not_to raise_error
    end

    it 'logs when column is reset' do
      expect(Gitlab::ErrorTracking::Logger).to receive(:error)
        .with(hash_including("extra.reset_model_name" => "TestUnion"))
        .and_call_original

      expect do
        load_query
      end.to raise_error(ActiveRecord::StatementInvalid, /must have the same number of columns/)
    end

    context 'when reset_column_information_on_statement_invalid FF is disabled' do
      before do
        stub_feature_flags(reset_column_information_on_statement_invalid: false)
      end

      it 'does not reset column information' do
        expect do
          load_query
        end.to raise_error(ActiveRecord::StatementInvalid, /must have the same number of columns/)
          .and not_change { test_unioned_model.column_names }
      end
    end
  end

  context 'with mismatched columns due to coding error' do
    def load_mismatched_query
      scopes = [
        test_unioned_model.select("id"),
        test_unioned_model.select("id, created_at")
      ]

      test_unioned_model.from_union(scopes).load
    end

    it 'limits reset_column_information calls' do
      expect(test_unioned_model).to receive(:reset_column_information).and_call_original

      expect do
        load_mismatched_query
      end.to raise_error(ActiveRecord::StatementInvalid, /must have the same number of columns/)

      expect(test_unioned_model).not_to receive(:reset_column_information)

      expect do
        load_mismatched_query
      end.to raise_error(ActiveRecord::StatementInvalid, /must have the same number of columns/)
    end

    it 'does reset_column_information after some time has passed' do
      expect do
        load_mismatched_query
      end.to raise_error(ActiveRecord::StatementInvalid, /must have the same number of columns/)

      travel_to(described_class::MAX_RESET_PERIOD.from_now + 1.minute)
      expect(test_unioned_model).to receive(:reset_column_information).and_call_original

      expect do
        load_mismatched_query
      end.to raise_error(ActiveRecord::StatementInvalid, /must have the same number of columns/)
    end
  end
end
# rubocop:enable Database/MultipleDatabases