forked from thoughtbot/shoulda-matchers
/
have_db_index_matcher.rb
160 lines (140 loc) · 3.92 KB
/
have_db_index_matcher.rb
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
module Shoulda
module Matchers
module ActiveRecord
# The `have_db_index` matcher tests that the table that backs your model
# has a index on a specific column.
#
# class CreateBlogs < ActiveRecord::Migration
# def change
# create_table :blogs do |t|
# t.integer :user_id
# end
#
# add_index :blogs, :user_id
# end
# end
#
# # RSpec
# describe Blog do
# it { should have_db_index(:user_id) }
# end
#
# # Minitest (Shoulda)
# class BlogTest < ActiveSupport::TestCase
# should have_db_index(:user_id)
# end
#
# #### Qualifiers
#
# ##### unique
#
# Use `unique` to assert that the index is unique.
#
# class CreateBlogs < ActiveRecord::Migration
# def change
# create_table :blogs do |t|
# t.string :name
# end
#
# add_index :blogs, :name, unique: true
# end
# end
#
# # RSpec
# describe Blog do
# it { should have_db_index(:name).unique(true) }
# end
#
# # Minitest (Shoulda)
# class BlogTest < ActiveSupport::TestCase
# should have_db_index(:name).unique(true)
# end
#
# Since it only ever makes since for `unique` to be `true`, you can also
# leave off the argument to save some keystrokes:
#
# # RSpec
# describe Blog do
# it { should have_db_index(:name).unique }
# end
#
# # Minitest (Shoulda)
# class BlogTest < ActiveSupport::TestCase
# should have_db_index(:name).unique
# end
#
# @return [HaveDbIndexMatcher]
#
def have_db_index(columns)
HaveDbIndexMatcher.new(columns)
end
# @private
class HaveDbIndexMatcher
def initialize(columns)
@columns = normalize_columns_to_array(columns)
@options = {}
end
def unique(unique = true)
@options[:unique] = unique
self
end
def matches?(subject)
@subject = subject
index_exists? && correct_unique?
end
def failure_message
"Expected #{expectation} (#{@missing})"
end
def failure_message_when_negated
"Did not expect #{expectation}"
end
def description
if @options.key?(:unique)
"have a #{index_type} index on columns #{@columns.join(' and ')}"
else
"have an index on columns #{@columns.join(' and ')}"
end
end
protected
def index_exists?
! matched_index.nil?
end
def correct_unique?
return true unless @options.key?(:unique)
is_unique = matched_index.unique
is_unique = !is_unique unless @options[:unique]
unless is_unique
@missing = "#{table_name} has an index named #{matched_index.name} " <<
"of unique #{matched_index.unique}, not #{@options[:unique]}."
end
is_unique
end
def matched_index
indexes.detect { |each| each.columns == @columns }
end
def model_class
@subject.class
end
def table_name
model_class.table_name
end
def indexes
::ActiveRecord::Base.connection.indexes(table_name)
end
def expectation
"#{model_class.name} to #{description}"
end
def index_type
if @options[:unique]
'unique'
else
'non-unique'
end
end
def normalize_columns_to_array(columns)
Array.wrap(columns).map(&:to_s)
end
end
end
end
end