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

shared_model_spec.rb « database « gitlab « lib « spec - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 2ae6ccf6c6ab7951357a5f10ffb9d6568dee89cf (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
135
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::Database::SharedModel, feature_category: :database do
  describe 'using an external connection' do
    let!(:original_connection) { described_class.connection }
    let(:new_connection) { double('connection') }

    it 'overrides the connection for the duration of the block', :aggregate_failures do
      expect_original_connection_around do
        described_class.using_connection(new_connection) do
          expect(described_class.connection).to be(new_connection)
        end
      end
    end

    it 'does not affect connections in other threads', :aggregate_failures do
      expect_original_connection_around do
        described_class.using_connection(new_connection) do
          expect(described_class.connection).to be(new_connection)

          Thread.new do
            expect(described_class.connection).not_to be(new_connection)
          end.join
        end
      end
    end

    it 'raises an error if the connection does not include `:gitlab_shared` schema' do
      allow(Gitlab::Database)
        .to receive(:gitlab_schemas_for_connection)
        .with(new_connection)
        .and_return([:gitlab_main])

      expect_original_connection_around do
        expect do
          described_class.using_connection(new_connection) {}
        end.to raise_error(/Cannot set `SharedModel` to connection/)
      end
    end

    context 'when multiple connection overrides are nested', :aggregate_failures do
      let(:second_connection) { double('connection') }

      it 'allows the nesting with the same connection object' do
        expect_original_connection_around do
          described_class.using_connection(new_connection) do
            expect(described_class.connection).to be(new_connection)

            described_class.using_connection(new_connection) do
              expect(described_class.connection).to be(new_connection)
            end

            expect(described_class.connection).to be(new_connection)
          end
        end
      end

      it 'raises an error if the connection is changed' do
        expect_original_connection_around do
          described_class.using_connection(new_connection) do
            expect(described_class.connection).to be(new_connection)

            expect do
              described_class.using_connection(second_connection) {}
            end.to raise_error(/Cannot change connection for Gitlab::Database::SharedModel/)

            expect(described_class.connection).to be(new_connection)
          end
        end
      end
    end

    context 'when the block raises an error', :aggregate_failures do
      it 're-raises the error, removing the overridden connection' do
        expect_original_connection_around do
          expect do
            described_class.using_connection(new_connection) do
              expect(described_class.connection).to be(new_connection)

              raise 'here comes an error!'
            end
          end.to raise_error(RuntimeError, 'here comes an error!')
        end
      end
    end
  end

  describe '#connection_db_config' do
    let!(:original_connection) { shared_model_class.connection }
    let!(:original_connection_db_config) { shared_model_class.connection_db_config }
    let(:shared_model) { shared_model_class.new }
    let(:shared_model_class) do
      Class.new(described_class) do
        self.table_name = 'postgres_async_indexes'
      end
    end

    it 'returns the class connection_db_config' do
      expect(shared_model.connection_db_config).to eq(described_class.connection_db_config)
    end

    context 'when switching the class connection' do
      before do
        skip_if_multiple_databases_not_setup
      end

      let(:new_base_model) { Ci::ApplicationRecord }
      let(:new_connection) { new_base_model.connection }

      it 'returns the db_config of the used connection when using load balancing' do
        expect_original_connection_around do
          described_class.using_connection(new_connection) do
            expect(shared_model.connection_db_config).to eq(new_base_model.connection_db_config)
          end
        end

        # it restores the connection_db_config afterwards
        expect(shared_model.connection_db_config).to eq(original_connection_db_config)
      end
    end
  end

  def expect_original_connection_around
    # For safety, ensure our original connection is distinct from our double
    # This should be the case, but in case of something leaking we should verify
    expect(original_connection).not_to be(new_connection)
    expect(described_class.connection).to be(original_connection)

    yield

    expect(described_class.connection).to be(original_connection)
  end
end