rspec-sequel_expectations 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +3 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +49 -0
- data/Rakefile +2 -0
- data/Thorfile.thor +6 -0
- data/config/database.yml.example +12 -0
- data/db.rb +12 -0
- data/lib/rspec/sequel_expectations.rb +7 -0
- data/lib/rspec/sequel_expectations/matchers/have_column.rb +121 -0
- data/lib/rspec/sequel_expectations/matchers/have_enum.rb +71 -0
- data/lib/rspec/sequel_expectations/matchers/have_index_on.rb +105 -0
- data/lib/rspec/sequel_expectations/matchers/have_primary_key.rb +62 -0
- data/lib/rspec/sequel_expectations/matchers/refer_to.rb +123 -0
- data/lib/rspec/sequel_expectations/version.rb +5 -0
- data/rspec-sequel_expectations.gemspec +30 -0
- data/spec/matchers/have_column_spec.rb +188 -0
- data/spec/matchers/have_enum_spec.rb +38 -0
- data/spec/matchers/have_index_on_spec.rb +160 -0
- data/spec/matchers/have_primary_key_spec.rb +155 -0
- data/spec/matchers/refer_to_spec.rb +171 -0
- data/spec/spec_helper.rb +30 -0
- metadata +186 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
module Sequel
|
4
|
+
|
5
|
+
class HavePrimaryKey
|
6
|
+
|
7
|
+
def matches?(subject)
|
8
|
+
@table = subject
|
9
|
+
|
10
|
+
get_keys
|
11
|
+
|
12
|
+
includes_all?
|
13
|
+
end
|
14
|
+
|
15
|
+
def description
|
16
|
+
%(have #{wording(@names)})
|
17
|
+
end
|
18
|
+
|
19
|
+
def failure_message
|
20
|
+
%(expected #{@table} to #{description} but #{@table} have #{wording(@keys)})
|
21
|
+
end
|
22
|
+
|
23
|
+
def failure_message_when_negated
|
24
|
+
%(did not expect #{@table} to #{description})
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def initialize(*names)
|
30
|
+
@names = names
|
31
|
+
@keys = []
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_keys
|
35
|
+
@keys = DB.schema(@table).reject { |tuple| !tuple.last[:primary_key] }.map(&:first)
|
36
|
+
end
|
37
|
+
|
38
|
+
def includes_all?
|
39
|
+
@names.reject { |k| @keys.include?(k) }.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
def wording(arr)
|
43
|
+
case arr.length
|
44
|
+
when 0
|
45
|
+
%(no primary keys)
|
46
|
+
when 1
|
47
|
+
%(primary key "#{arr.first}")
|
48
|
+
else
|
49
|
+
%(primary keys #{arr.inspect})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def have_primary_key(*names)
|
56
|
+
HavePrimaryKey.new(*names)
|
57
|
+
end
|
58
|
+
alias :have_primary_keys :have_primary_key
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
module Sequel
|
4
|
+
class ReferTo
|
5
|
+
def matches?(subject)
|
6
|
+
get_reference_for(subject)
|
7
|
+
|
8
|
+
refer_to? && correct_fk? && correct_pk? && correct_update? && correct_delete?
|
9
|
+
end
|
10
|
+
|
11
|
+
def from_fk(key)
|
12
|
+
@foreign_key = key
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_pk(key)
|
17
|
+
@primary_key = key
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_update(action)
|
22
|
+
@on_update = action
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_delete(action)
|
27
|
+
@on_delete = action
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def description
|
32
|
+
text = [%(have reference to "#{@table}")]
|
33
|
+
text << %(with column "#{@foreign_key}") if @foreign_key
|
34
|
+
text << %(with primary key column "#{@primary_key}") if @primary_key
|
35
|
+
text << %(with "#{@on_update}" action on update) if @on_update
|
36
|
+
text << %(with "#{@on_delete}" action on delete) if @on_delete
|
37
|
+
|
38
|
+
text.join(' ')
|
39
|
+
end
|
40
|
+
|
41
|
+
def failure_message
|
42
|
+
%(expected "#{@relation}" to #{description} but #{@error})
|
43
|
+
end
|
44
|
+
|
45
|
+
def failure_message_when_negated
|
46
|
+
%(did not expect "#{@relation}" to #{description})
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def initialize(table)
|
52
|
+
@table = table
|
53
|
+
@foreign_key = nil
|
54
|
+
@primary_key = nil
|
55
|
+
@on_update = nil
|
56
|
+
@on_delete = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def refer_to?
|
60
|
+
if @reference
|
61
|
+
true
|
62
|
+
else
|
63
|
+
@error = %("#{@relation}" does not have a reference to "#{@table}")
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def correct_fk?
|
69
|
+
return true unless @foreign_key
|
70
|
+
|
71
|
+
if @reference[:columns].include?(@foreign_key)
|
72
|
+
true
|
73
|
+
else
|
74
|
+
@error = %("#{@relation}" does not have a foreign key column "#{@foreign_key}")
|
75
|
+
false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def correct_pk?
|
80
|
+
return true unless @primary_key
|
81
|
+
|
82
|
+
if @reference[:key].first == @primary_key
|
83
|
+
true
|
84
|
+
else
|
85
|
+
@error = %("#{@table}" does not have a primary key column "#{@primary_key}")
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def correct_update?
|
91
|
+
return true unless @on_update
|
92
|
+
|
93
|
+
if @reference[:on_update] == @on_update
|
94
|
+
true
|
95
|
+
else
|
96
|
+
@error = %(reference does not have action "#{@on_update}" on update)
|
97
|
+
false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def correct_delete?
|
102
|
+
return true unless @on_delete
|
103
|
+
|
104
|
+
if @reference[:on_delete] == @on_delete
|
105
|
+
true
|
106
|
+
else
|
107
|
+
@error = %(reference does not have action "#{@on_delete}" on delete)
|
108
|
+
false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def get_reference_for(relation)
|
113
|
+
@relation = relation
|
114
|
+
@reference = DB.foreign_key_list(relation).select { |fk| fk[:table] == @table }.first
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def refer_to(table)
|
119
|
+
ReferTo.new(table)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rspec/sequel_expectations/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rspec-sequel_expectations"
|
8
|
+
spec.version = Rspec::SequelExpectations::VERSION
|
9
|
+
spec.authors = ["Andrey Savchenko"]
|
10
|
+
spec.email = ["andrey@aejis.eu"]
|
11
|
+
spec.summary = %q{RSpec matchers for Sequel}
|
12
|
+
spec.description = %q{RSpec matchers for Sequel which tests database, not model}
|
13
|
+
spec.homepage = "https://github.com/Ptico/rspec-sequel_expectations"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "sequel", "~> 4.15"
|
22
|
+
spec.add_dependency "rspec-expectations", "~> 3.0"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "sequel_pg"
|
27
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
28
|
+
spec.add_development_dependency "pry"
|
29
|
+
spec.add_development_dependency "faker"
|
30
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RSpec::Matchers::Sequel::HaveColumn do
|
4
|
+
before :all do
|
5
|
+
DB.create_table(:users) do
|
6
|
+
column :full_name, String
|
7
|
+
column :age, Integer, default: 18, null: false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
after :all do
|
12
|
+
DB.drop_table(:users)
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:table) { :users }
|
16
|
+
|
17
|
+
let(:result) { matcher.matches?(table) }
|
18
|
+
|
19
|
+
describe 'column exists' do
|
20
|
+
context 'when exists' do
|
21
|
+
let(:matcher) { have_column(:full_name) }
|
22
|
+
|
23
|
+
it 'should success' do
|
24
|
+
expect(result).to be(true)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should have description' do
|
28
|
+
expect(matcher.description).to eql('have column named "full_name"')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'column not exists' do
|
33
|
+
let(:matcher) { have_column(:abyrvalg) }
|
34
|
+
|
35
|
+
it 'should fail' do
|
36
|
+
expect(result).to be(false)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should set error message' do
|
40
|
+
expect { result }.to change {
|
41
|
+
matcher.failure_message
|
42
|
+
}.to %(expected users to #{matcher.description} but users does not have a column named "abyrvalg")
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should set negative error message' do
|
46
|
+
expect { result }.to change {
|
47
|
+
matcher.failure_message_when_negated
|
48
|
+
}.to("did not expect users to #{matcher.description}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'column type' do
|
54
|
+
context 'when correct type' do
|
55
|
+
[String, :string, 'string', 'text'].each do |type|
|
56
|
+
context "with #{type}" do
|
57
|
+
let(:matcher) { have_column(:full_name).of_type(type) }
|
58
|
+
|
59
|
+
it 'should success' do
|
60
|
+
expect(result).to be(true)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should have description' do
|
64
|
+
expect(matcher.description).to eql %(have column named "full_name" of type #{type})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when incorrect type' do
|
71
|
+
let(:matcher) { have_column(:full_name).of_type(Integer) }
|
72
|
+
|
73
|
+
it 'should fail' do
|
74
|
+
expect(result).to be(false)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should set error message' do
|
78
|
+
expect { result }.to change {
|
79
|
+
matcher.failure_message
|
80
|
+
}.to %(expected users to #{matcher.description} but it have type [string, text])
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should set negative error message' do
|
84
|
+
expect { result }.to change {
|
85
|
+
matcher.failure_message_when_negated
|
86
|
+
}.to %(did not expect users to #{matcher.description})
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'with_default' do
|
92
|
+
context 'when match' do
|
93
|
+
let(:matcher) { have_column(:age).with_default(18) }
|
94
|
+
|
95
|
+
it 'should success' do
|
96
|
+
expect(result).to be(true)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should have description' do
|
100
|
+
expect(matcher.description).to eql('have column named "age" with default value "18"')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'when did not match' do
|
105
|
+
let(:matcher) { have_column(:age).with_default(2) }
|
106
|
+
|
107
|
+
it 'should fail' do
|
108
|
+
expect(result).to be(false)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should have description' do
|
112
|
+
expect(matcher.description).to eql('have column named "age" with default value "2"')
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should set error message' do
|
116
|
+
expect { result }.to change {
|
117
|
+
matcher.failure_message
|
118
|
+
}.to %(expected users to #{matcher.description} but it has default value "18")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe 'allow_null' do
|
124
|
+
context 'when match' do
|
125
|
+
let(:matcher) { have_column(:full_name).allow_null }
|
126
|
+
|
127
|
+
it 'should success' do
|
128
|
+
expect(result).to be(true)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should have description' do
|
132
|
+
expect(matcher.description).to eql('have column named "full_name" allowing null')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'when did not match' do
|
137
|
+
let(:matcher) { have_column(:age).allow_null }
|
138
|
+
|
139
|
+
it 'should fail' do
|
140
|
+
expect(result).to be(false)
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should have description' do
|
144
|
+
expect(matcher.description).to eql('have column named "age" allowing null')
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should set error message' do
|
148
|
+
expect { result }.to change {
|
149
|
+
matcher.failure_message
|
150
|
+
}.to %(expected users to #{matcher.description} but it does not allow null)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe 'not_null' do
|
156
|
+
context 'when match' do
|
157
|
+
let(:matcher) { have_column(:age).not_null }
|
158
|
+
|
159
|
+
it 'should success' do
|
160
|
+
expect(result).to be(true)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should have description' do
|
164
|
+
expect(matcher.description).to eql('have column named "age" not allowing null')
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'when did not match' do
|
169
|
+
let(:matcher) { have_column(:full_name).not_null }
|
170
|
+
|
171
|
+
it 'should fail' do
|
172
|
+
expect(result).to be(false)
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should have description' do
|
176
|
+
expect(matcher.description).to eql('have column named "full_name" not allowing null')
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should set error message' do
|
180
|
+
expect { result }.to change {
|
181
|
+
matcher.failure_message
|
182
|
+
}.to %(expected users to #{matcher.description} but it allow null)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RSpec::Matchers::Sequel::HaveEnum do
|
4
|
+
before :all do
|
5
|
+
@enum_name = Faker::Lorem.word
|
6
|
+
@values = %w(admin manager user inspector)
|
7
|
+
DB.create_enum(@enum_name, @values)
|
8
|
+
end
|
9
|
+
|
10
|
+
after :all do
|
11
|
+
DB.drop_enum(@enum_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#have_enum' do
|
15
|
+
it 'return true if enum exists' do
|
16
|
+
expect(DB).to have_enum(@enum_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'accept symbol as enum name' do
|
20
|
+
expect(DB).to have_enum(@enum_name.to_sym)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'return false if enum does not exists' do
|
24
|
+
expect(DB).to_not have_enum(@enum_name + "1")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#with_values' do
|
29
|
+
|
30
|
+
it 'returns true if enum have exactly that values' do
|
31
|
+
expect(DB).to have_enum(@enum_name).with_values(@values)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'returns false if enum values differ' do
|
35
|
+
expect(DB).not_to have_enum(@enum_name).with_values(@values.dup.push "something_new" )
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|