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

postgres_constraint_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: 75084a691150f91e189879cf24156c4fb4a2e97e (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
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::Database::PostgresConstraint, type: :model do
  # PostgresConstraint does not `behaves_like 'a postgres model'` because it does not correspond 1-1 with a single entry
  # in pg_class
  let(:schema) { ActiveRecord::Base.connection.current_schema }
  let(:table_name) { '_test_table' }
  let(:table_identifier) { "#{schema}.#{table_name}" }
  let(:referenced_name) { '_test_referenced' }
  let(:check_constraint_a_positive) { 'check_constraint_a_positive' }
  let(:check_constraint_a_gt_b) { 'check_constraint_a_gt_b' }
  let(:invalid_constraint_a) { 'check_constraint_b_positive_invalid' }
  let(:unique_constraint_a) { "#{table_name}_a_key" }

  before do
    ActiveRecord::Base.connection.execute(<<~SQL)
    create table #{referenced_name} (
      id bigserial primary key not null
    );

    create table #{table_name} (
      id bigserial not null,
      referenced_id bigint not null references #{referenced_name}(id),
      a integer unique,
      b integer,
      primary key (id, referenced_id),
      constraint #{check_constraint_a_positive} check (a > 0),
      constraint #{check_constraint_a_gt_b} check (a > b)
    );

    alter table #{table_name} add constraint #{invalid_constraint_a} CHECK (a > 1) NOT VALID;
    SQL
  end

  describe '#by_table_identifier' do
    subject(:constraints_for_table) { described_class.by_table_identifier(table_identifier) }

    it 'includes all constraints on the table' do
      all_constraints_for_table = described_class.all.to_a.select { |c| c.table_identifier == table_identifier }
      expect(all_constraints_for_table.map(&:oid)).to match_array(constraints_for_table.pluck(:oid))
    end

    it 'throws an error if the format is incorrect' do
      expect { described_class.by_table_identifier('not-an-identifier') }.to raise_error(ArgumentError)
    end
  end

  describe '#check_constraints' do
    subject(:check_constraints) { described_class.check_constraints.by_table_identifier(table_identifier) }

    it 'finds check constraints for the table' do
      expect(check_constraints.map(&:name)).to contain_exactly(check_constraint_a_positive,
                                                               check_constraint_a_gt_b,
                                                               invalid_constraint_a)
    end

    it 'includes columns for the check constraints', :aggregate_failures do
      expect(check_constraints.find_by(name: check_constraint_a_positive).column_names).to contain_exactly('a')
      expect(check_constraints.find_by(name: check_constraint_a_gt_b).column_names).to contain_exactly('a', 'b')
    end
  end

  describe "#valid" do
    subject(:valid_constraint_names) { described_class.valid.by_table_identifier(table_identifier).pluck(:name) }

    let(:all_constraint_names) { described_class.by_table_identifier(table_identifier).pluck(:name) }

    it 'excludes invalid constraints' do
      expect(valid_constraint_names).not_to include(invalid_constraint_a)
      expect(valid_constraint_names).to match_array(all_constraint_names - [invalid_constraint_a])
    end
  end

  describe '#primary_key_constraints' do
    subject(:pk_constraints) { described_class.primary_key_constraints.by_table_identifier(table_identifier) }

    it 'finds the primary key constraint for the table' do
      expect(pk_constraints.count).to eq(1)
      expect(pk_constraints.first.constraint_type).to eq('p')
    end

    it 'finds the columns in the primary key constraint' do
      constraint = pk_constraints.first
      expect(constraint.column_names).to contain_exactly('id', 'referenced_id')
    end
  end

  describe '#unique_constraints' do
    subject(:unique_constraints) { described_class.unique_constraints.by_table_identifier(table_identifier) }

    it 'finds the unique constraints for the table' do
      expect(unique_constraints.pluck(:name)).to contain_exactly(unique_constraint_a)
    end
  end

  describe '#primary_or_unique_constraints' do
    subject(:pk_or_unique_constraints) do
      described_class.primary_or_unique_constraints.by_table_identifier(table_identifier)
    end

    it 'finds primary and unique constraints' do
      expect(pk_or_unique_constraints.pluck(:name)).to contain_exactly("#{table_name}_pkey", unique_constraint_a)
    end
  end

  describe '#including_column' do
    it 'only matches constraints on the given column' do
      constraints_on_a = described_class.by_table_identifier(table_identifier).including_column('a').map(&:name)
      expect(constraints_on_a).to contain_exactly(check_constraint_a_positive, check_constraint_a_gt_b,
                                                  unique_constraint_a, invalid_constraint_a)
    end
  end

  describe '#not_including_column' do
    it 'only matches constraints not including the given column' do
      constraints_not_on_a = described_class.by_table_identifier(table_identifier).not_including_column('a').map(&:name)

      expect(constraints_not_on_a).to contain_exactly("#{table_name}_pkey", "#{table_name}_referenced_id_fkey")
    end
  end
end