cuprum-collections 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +59 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/DEVELOPMENT.md +25 -0
- data/LICENSE +22 -0
- data/README.md +950 -0
- data/lib/cuprum/collections/base.rb +11 -0
- data/lib/cuprum/collections/basic/collection.rb +135 -0
- data/lib/cuprum/collections/basic/command.rb +112 -0
- data/lib/cuprum/collections/basic/commands/assign_one.rb +54 -0
- data/lib/cuprum/collections/basic/commands/build_one.rb +45 -0
- data/lib/cuprum/collections/basic/commands/destroy_one.rb +48 -0
- data/lib/cuprum/collections/basic/commands/find_many.rb +65 -0
- data/lib/cuprum/collections/basic/commands/find_matching.rb +126 -0
- data/lib/cuprum/collections/basic/commands/find_one.rb +49 -0
- data/lib/cuprum/collections/basic/commands/insert_one.rb +50 -0
- data/lib/cuprum/collections/basic/commands/update_one.rb +52 -0
- data/lib/cuprum/collections/basic/commands/validate_one.rb +69 -0
- data/lib/cuprum/collections/basic/commands.rb +18 -0
- data/lib/cuprum/collections/basic/query.rb +160 -0
- data/lib/cuprum/collections/basic/query_builder.rb +69 -0
- data/lib/cuprum/collections/basic/rspec/command_contract.rb +392 -0
- data/lib/cuprum/collections/basic/rspec.rb +8 -0
- data/lib/cuprum/collections/basic.rb +22 -0
- data/lib/cuprum/collections/command.rb +26 -0
- data/lib/cuprum/collections/commands/abstract_find_many.rb +77 -0
- data/lib/cuprum/collections/commands/abstract_find_matching.rb +64 -0
- data/lib/cuprum/collections/commands/abstract_find_one.rb +44 -0
- data/lib/cuprum/collections/commands.rb +8 -0
- data/lib/cuprum/collections/constraints/attribute_name.rb +22 -0
- data/lib/cuprum/collections/constraints/order/attributes_array.rb +26 -0
- data/lib/cuprum/collections/constraints/order/attributes_hash.rb +27 -0
- data/lib/cuprum/collections/constraints/order/complex_ordering.rb +46 -0
- data/lib/cuprum/collections/constraints/order/sort_direction.rb +32 -0
- data/lib/cuprum/collections/constraints/order.rb +8 -0
- data/lib/cuprum/collections/constraints/ordering.rb +114 -0
- data/lib/cuprum/collections/constraints/query_hash.rb +25 -0
- data/lib/cuprum/collections/constraints.rb +8 -0
- data/lib/cuprum/collections/errors/already_exists.rb +86 -0
- data/lib/cuprum/collections/errors/extra_attributes.rb +66 -0
- data/lib/cuprum/collections/errors/failed_validation.rb +66 -0
- data/lib/cuprum/collections/errors/invalid_parameters.rb +50 -0
- data/lib/cuprum/collections/errors/invalid_query.rb +55 -0
- data/lib/cuprum/collections/errors/missing_default_contract.rb +49 -0
- data/lib/cuprum/collections/errors/not_found.rb +81 -0
- data/lib/cuprum/collections/errors/unknown_operator.rb +71 -0
- data/lib/cuprum/collections/errors.rb +8 -0
- data/lib/cuprum/collections/queries/ordering.rb +74 -0
- data/lib/cuprum/collections/queries/parse.rb +22 -0
- data/lib/cuprum/collections/queries/parse_block.rb +206 -0
- data/lib/cuprum/collections/queries/parse_strategy.rb +91 -0
- data/lib/cuprum/collections/queries.rb +25 -0
- data/lib/cuprum/collections/query.rb +247 -0
- data/lib/cuprum/collections/query_builder.rb +61 -0
- data/lib/cuprum/collections/rspec/assign_one_command_contract.rb +168 -0
- data/lib/cuprum/collections/rspec/build_one_command_contract.rb +93 -0
- data/lib/cuprum/collections/rspec/collection_contract.rb +153 -0
- data/lib/cuprum/collections/rspec/destroy_one_command_contract.rb +106 -0
- data/lib/cuprum/collections/rspec/find_many_command_contract.rb +327 -0
- data/lib/cuprum/collections/rspec/find_matching_command_contract.rb +194 -0
- data/lib/cuprum/collections/rspec/find_one_command_contract.rb +154 -0
- data/lib/cuprum/collections/rspec/fixtures.rb +89 -0
- data/lib/cuprum/collections/rspec/insert_one_command_contract.rb +83 -0
- data/lib/cuprum/collections/rspec/query_builder_contract.rb +92 -0
- data/lib/cuprum/collections/rspec/query_contract.rb +650 -0
- data/lib/cuprum/collections/rspec/querying_contract.rb +298 -0
- data/lib/cuprum/collections/rspec/update_one_command_contract.rb +79 -0
- data/lib/cuprum/collections/rspec/validate_one_command_contract.rb +96 -0
- data/lib/cuprum/collections/rspec.rb +8 -0
- data/lib/cuprum/collections/version.rb +59 -0
- data/lib/cuprum/collections.rb +26 -0
- metadata +219 -0
@@ -0,0 +1,194 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stannum/rspec/validate_parameter'
|
4
|
+
|
5
|
+
require 'cuprum/collections/constraints/ordering'
|
6
|
+
require 'cuprum/collections/rspec'
|
7
|
+
require 'cuprum/collections/rspec/querying_contract'
|
8
|
+
|
9
|
+
module Cuprum::Collections::RSpec
|
10
|
+
# Contract validating the behavior of a FindMatching command implementation.
|
11
|
+
FIND_MATCHING_COMMAND_CONTRACT = lambda do
|
12
|
+
include Stannum::RSpec::Matchers
|
13
|
+
|
14
|
+
describe '#call' do
|
15
|
+
shared_examples 'should return the matching items' do
|
16
|
+
it { expect(result).to be_a_passing_result }
|
17
|
+
|
18
|
+
it { expect(result.value).to be_a Enumerator }
|
19
|
+
|
20
|
+
it { expect(result.value.to_a).to be == expected_data }
|
21
|
+
end
|
22
|
+
|
23
|
+
shared_examples 'should return the wrapped items' do
|
24
|
+
it { expect(result).to be_a_passing_result }
|
25
|
+
|
26
|
+
it { expect(result.value).to be_a Hash }
|
27
|
+
|
28
|
+
it { expect(result.value.keys).to be == [collection_name] }
|
29
|
+
|
30
|
+
it { expect(result.value[collection_name]).to be == expected_data }
|
31
|
+
end
|
32
|
+
|
33
|
+
include_contract Cuprum::Collections::RSpec::QUERYING_CONTEXTS
|
34
|
+
|
35
|
+
let(:options) do
|
36
|
+
opts = {}
|
37
|
+
|
38
|
+
opts[:limit] = limit if limit
|
39
|
+
opts[:offset] = offset if offset
|
40
|
+
opts[:order] = order if order
|
41
|
+
opts[:where] = filter unless filter.nil? || filter.is_a?(Proc)
|
42
|
+
|
43
|
+
opts
|
44
|
+
end
|
45
|
+
let(:block) { filter.is_a?(Proc) ? filter : nil }
|
46
|
+
let(:result) { command.call(**options, &block) }
|
47
|
+
let(:data) { [] }
|
48
|
+
let(:matching_data) { data }
|
49
|
+
let(:expected_data) do
|
50
|
+
defined?(super()) ? super() : matching_data
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should validate the :envelope keyword' do
|
54
|
+
expect(command)
|
55
|
+
.to validate_parameter(:call, :envelope)
|
56
|
+
.using_constraint(Stannum::Constraints::Boolean.new)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should validate the :limit keyword' do
|
60
|
+
expect(command)
|
61
|
+
.to validate_parameter(:call, :limit)
|
62
|
+
.with_value(Object.new)
|
63
|
+
.using_constraint(Integer, required: false)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should validate the :offset keyword' do
|
67
|
+
expect(command)
|
68
|
+
.to validate_parameter(:call, :offset)
|
69
|
+
.with_value(Object.new)
|
70
|
+
.using_constraint(Integer, required: false)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should validate the :order keyword' do
|
74
|
+
constraint = Cuprum::Collections::Constraints::Ordering.new
|
75
|
+
|
76
|
+
expect(command)
|
77
|
+
.to validate_parameter(:call, :order)
|
78
|
+
.with_value(Object.new)
|
79
|
+
.using_constraint(constraint, required: false)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should validate the :scope keyword' do
|
83
|
+
expect(command)
|
84
|
+
.to validate_parameter(:call, :scope)
|
85
|
+
.using_constraint(
|
86
|
+
Stannum::Constraints::Type.new(query.class, optional: true)
|
87
|
+
)
|
88
|
+
.with_value(Object.new.freeze)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should validate the :where keyword' do
|
92
|
+
expect(command).to validate_parameter(:call, :where)
|
93
|
+
end
|
94
|
+
|
95
|
+
include_examples 'should return the matching items'
|
96
|
+
|
97
|
+
include_contract Cuprum::Collections::RSpec::QUERYING_CONTRACT,
|
98
|
+
block: lambda {
|
99
|
+
include_examples 'should return the matching items'
|
100
|
+
}
|
101
|
+
|
102
|
+
describe 'with an invalid filter block' do
|
103
|
+
let(:block) { -> {} }
|
104
|
+
let(:expected_error) do
|
105
|
+
an_instance_of(Cuprum::Collections::Errors::InvalidQuery)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should return a failing result' do
|
109
|
+
expect(result).to be_a_failing_result.with_error(expected_error)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'with envelope: true' do
|
114
|
+
let(:options) { super().merge(envelope: true) }
|
115
|
+
|
116
|
+
include_examples 'should return the wrapped items'
|
117
|
+
|
118
|
+
include_contract Cuprum::Collections::RSpec::QUERYING_CONTRACT,
|
119
|
+
block: lambda {
|
120
|
+
include_examples 'should return the wrapped items'
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'when the collection has many items' do
|
125
|
+
let(:data) { fixtures_data }
|
126
|
+
|
127
|
+
include_examples 'should return the matching items'
|
128
|
+
|
129
|
+
include_contract Cuprum::Collections::RSpec::QUERYING_CONTRACT,
|
130
|
+
block: lambda {
|
131
|
+
include_examples 'should return the matching items'
|
132
|
+
}
|
133
|
+
|
134
|
+
describe 'with envelope: true' do
|
135
|
+
let(:options) { super().merge(envelope: true) }
|
136
|
+
|
137
|
+
include_examples 'should return the wrapped items'
|
138
|
+
|
139
|
+
include_contract Cuprum::Collections::RSpec::QUERYING_CONTRACT,
|
140
|
+
block: lambda {
|
141
|
+
include_examples 'should return the wrapped items'
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
145
|
+
describe 'with scope: query' do
|
146
|
+
let(:scope_filter) { -> { {} } }
|
147
|
+
let(:options) { super().merge(scope: scope) }
|
148
|
+
|
149
|
+
describe 'with a scope that does not match any values' do
|
150
|
+
let(:scope_filter) { -> { { series: 'Mistborn' } } }
|
151
|
+
let(:matching_data) { [] }
|
152
|
+
|
153
|
+
include_examples 'should return the matching items'
|
154
|
+
end
|
155
|
+
|
156
|
+
describe 'with a scope that matches some values' do
|
157
|
+
let(:scope_filter) { -> { { series: nil } } }
|
158
|
+
let(:matching_data) do
|
159
|
+
super().select { |item| item['series'].nil? }
|
160
|
+
end
|
161
|
+
|
162
|
+
include_examples 'should return the matching items'
|
163
|
+
|
164
|
+
describe 'with a where filter' do
|
165
|
+
let(:filter) { -> { { author: 'Ursula K. LeGuin' } } }
|
166
|
+
let(:options) { super().merge(where: filter) }
|
167
|
+
let(:matching_data) do
|
168
|
+
super().select { |item| item['author'] == 'Ursula K. LeGuin' }
|
169
|
+
end
|
170
|
+
|
171
|
+
include_examples 'should return the matching items'
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe 'with a scope that matches all values' do
|
176
|
+
let(:scope_filter) { -> { { id: not_equal(nil) } } }
|
177
|
+
|
178
|
+
include_examples 'should return the matching items'
|
179
|
+
|
180
|
+
describe 'with a where filter' do
|
181
|
+
let(:filter) { -> { { author: 'Ursula K. LeGuin' } } }
|
182
|
+
let(:options) { super().merge(where: filter) }
|
183
|
+
let(:matching_data) do
|
184
|
+
super().select { |item| item['author'] == 'Ursula K. LeGuin' }
|
185
|
+
end
|
186
|
+
|
187
|
+
include_examples 'should return the matching items'
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/rspec'
|
4
|
+
|
5
|
+
module Cuprum::Collections::RSpec
|
6
|
+
# Contract validating the behavior of a FindOne command implementation.
|
7
|
+
FIND_ONE_COMMAND_CONTRACT = lambda do
|
8
|
+
describe '#call' do
|
9
|
+
let(:mapped_data) do
|
10
|
+
defined?(super()) ? super() : data
|
11
|
+
end
|
12
|
+
let(:primary_key_name) { defined?(super()) ? super() : :id }
|
13
|
+
let(:primary_key_type) { defined?(super()) ? super() : Integer }
|
14
|
+
let(:invalid_primary_key_value) do
|
15
|
+
defined?(super()) ? super() : 100
|
16
|
+
end
|
17
|
+
let(:valid_primary_key_value) do
|
18
|
+
defined?(super()) ? super() : 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def tools
|
22
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should validate the :envelope keyword' do
|
26
|
+
expect(command)
|
27
|
+
.to validate_parameter(:call, :envelope)
|
28
|
+
.using_constraint(Stannum::Constraints::Boolean.new)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should validate the :primary_key keyword' do
|
32
|
+
expect(command)
|
33
|
+
.to validate_parameter(:call, :primary_key)
|
34
|
+
.using_constraint(primary_key_type)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should validate the :scope keyword' do
|
38
|
+
expect(command)
|
39
|
+
.to validate_parameter(:call, :scope)
|
40
|
+
.using_constraint(
|
41
|
+
Stannum::Constraints::Type.new(query.class, optional: true)
|
42
|
+
)
|
43
|
+
.with_value(Object.new.freeze)
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'with an invalid primary key' do
|
47
|
+
let(:primary_key) { invalid_primary_key_value }
|
48
|
+
let(:expected_error) do
|
49
|
+
Cuprum::Collections::Errors::NotFound.new(
|
50
|
+
collection_name: command.collection_name,
|
51
|
+
primary_key_name: primary_key_name,
|
52
|
+
primary_key_values: primary_key
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should return a failing result' do
|
57
|
+
expect(command.call(primary_key: primary_key))
|
58
|
+
.to be_a_failing_result
|
59
|
+
.with_error(expected_error)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when the collection has many items' do
|
64
|
+
let(:data) { fixtures_data }
|
65
|
+
let(:matching_data) do
|
66
|
+
mapped_data.find { |item| item[primary_key_name.to_s] == primary_key }
|
67
|
+
end
|
68
|
+
let(:expected_data) do
|
69
|
+
defined?(super()) ? super() : matching_data
|
70
|
+
end
|
71
|
+
|
72
|
+
describe 'with an invalid primary key' do
|
73
|
+
let(:primary_key) { invalid_primary_key_value }
|
74
|
+
let(:expected_error) do
|
75
|
+
Cuprum::Collections::Errors::NotFound.new(
|
76
|
+
collection_name: command.collection_name,
|
77
|
+
primary_key_name: primary_key_name,
|
78
|
+
primary_key_values: primary_key
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should return a failing result' do
|
83
|
+
expect(command.call(primary_key: primary_key))
|
84
|
+
.to be_a_failing_result
|
85
|
+
.with_error(expected_error)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'with a valid primary key' do
|
90
|
+
let(:primary_key) { valid_primary_key_value }
|
91
|
+
|
92
|
+
it 'should return a passing result' do
|
93
|
+
expect(command.call(primary_key: primary_key))
|
94
|
+
.to be_a_passing_result
|
95
|
+
.with_value(expected_data)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe 'with envelope: true' do
|
100
|
+
let(:member_name) { tools.str.singularize(collection_name) }
|
101
|
+
|
102
|
+
describe 'with a valid primary key' do
|
103
|
+
let(:primary_key) { valid_primary_key_value }
|
104
|
+
|
105
|
+
it 'should return a passing result' do
|
106
|
+
expect(command.call(primary_key: primary_key, envelope: true))
|
107
|
+
.to be_a_passing_result
|
108
|
+
.with_value({ member_name => expected_data })
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'with scope: query' do
|
114
|
+
let(:scope_filter) { -> { {} } }
|
115
|
+
|
116
|
+
describe 'with a scope that does not match the key' do
|
117
|
+
let(:scope_filter) { -> { { author: 'Ursula K. LeGuin' } } }
|
118
|
+
|
119
|
+
describe 'with an valid primary key' do
|
120
|
+
let(:primary_key) { valid_primary_key_value }
|
121
|
+
let(:expected_error) do
|
122
|
+
Cuprum::Collections::Errors::NotFound.new(
|
123
|
+
collection_name: command.collection_name,
|
124
|
+
primary_key_name: primary_key_name,
|
125
|
+
primary_key_values: primary_key
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should return a failing result' do
|
130
|
+
expect(command.call(primary_key: primary_key, scope: scope))
|
131
|
+
.to be_a_failing_result
|
132
|
+
.with_error(expected_error)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe 'with a scope that matches the key' do
|
138
|
+
let(:scope_filter) { -> { { author: 'J.R.R. Tolkien' } } }
|
139
|
+
|
140
|
+
describe 'with a valid primary key' do
|
141
|
+
let(:primary_key) { valid_primary_key_value }
|
142
|
+
|
143
|
+
it 'should return a passing result' do
|
144
|
+
expect(command.call(primary_key: primary_key))
|
145
|
+
.to be_a_passing_result
|
146
|
+
.with_value(expected_data)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/rspec'
|
4
|
+
|
5
|
+
module Cuprum::Collections::RSpec
|
6
|
+
# Sample data for validating collection implementations.
|
7
|
+
BOOKS_FIXTURES = [
|
8
|
+
{
|
9
|
+
'id' => 0,
|
10
|
+
'title' => 'The Hobbit',
|
11
|
+
'author' => 'J.R.R. Tolkien',
|
12
|
+
'series' => nil,
|
13
|
+
'category' => 'Science Fiction and Fantasy',
|
14
|
+
'published_at' => '1937-09-21'
|
15
|
+
},
|
16
|
+
{
|
17
|
+
'id' => 1,
|
18
|
+
'title' => 'The Silmarillion',
|
19
|
+
'author' => 'J.R.R. Tolkien',
|
20
|
+
'series' => nil,
|
21
|
+
'category' => 'Science Fiction and Fantasy',
|
22
|
+
'published_at' => '1977-09-15'
|
23
|
+
},
|
24
|
+
{
|
25
|
+
'id' => 2,
|
26
|
+
'title' => 'The Fellowship of the Ring',
|
27
|
+
'author' => 'J.R.R. Tolkien',
|
28
|
+
'series' => 'The Lord of the Rings',
|
29
|
+
'category' => 'Science Fiction and Fantasy',
|
30
|
+
'published_at' => '1954-07-29'
|
31
|
+
},
|
32
|
+
{
|
33
|
+
'id' => 3,
|
34
|
+
'title' => 'The Two Towers',
|
35
|
+
'author' => 'J.R.R. Tolkien',
|
36
|
+
'series' => 'The Lord of the Rings',
|
37
|
+
'category' => 'Science Fiction and Fantasy',
|
38
|
+
'published_at' => '1954-11-11'
|
39
|
+
},
|
40
|
+
{
|
41
|
+
'id' => 4,
|
42
|
+
'title' => 'The Return of the King',
|
43
|
+
'author' => 'J.R.R. Tolkien',
|
44
|
+
'series' => 'The Lord of the Rings',
|
45
|
+
'category' => 'Science Fiction and Fantasy',
|
46
|
+
'published_at' => '1955-10-20'
|
47
|
+
},
|
48
|
+
{
|
49
|
+
'id' => 5,
|
50
|
+
'title' => 'The Word for World is Forest',
|
51
|
+
'author' => 'Ursula K. LeGuin',
|
52
|
+
'series' => nil,
|
53
|
+
'category' => 'Science Fiction and Fantasy',
|
54
|
+
'published_at' => '1972-03-13'
|
55
|
+
},
|
56
|
+
{
|
57
|
+
'id' => 6,
|
58
|
+
'title' => 'The Ones Who Walk Away From Omelas',
|
59
|
+
'author' => 'Ursula K. LeGuin',
|
60
|
+
'series' => nil,
|
61
|
+
'category' => 'Science Fiction and Fantasy',
|
62
|
+
'published_at' => '1973-10-01'
|
63
|
+
},
|
64
|
+
{
|
65
|
+
'id' => 7,
|
66
|
+
'title' => 'A Wizard of Earthsea',
|
67
|
+
'author' => 'Ursula K. LeGuin',
|
68
|
+
'series' => 'Earthsea',
|
69
|
+
'category' => 'Science Fiction and Fantasy',
|
70
|
+
'published_at' => '1968-11-01'
|
71
|
+
},
|
72
|
+
{
|
73
|
+
'id' => 8,
|
74
|
+
'title' => 'The Tombs of Atuan',
|
75
|
+
'author' => 'Ursula K. LeGuin',
|
76
|
+
'series' => 'Earthsea',
|
77
|
+
'category' => 'Science Fiction and Fantasy',
|
78
|
+
'published_at' => '1970-12-01'
|
79
|
+
},
|
80
|
+
{
|
81
|
+
'id' => 9,
|
82
|
+
'title' => 'The Farthest Shore',
|
83
|
+
'author' => 'Ursula K. LeGuin',
|
84
|
+
'series' => 'Earthsea',
|
85
|
+
'category' => 'Science Fiction and Fantasy',
|
86
|
+
'published_at' => '1972-09-01'
|
87
|
+
}
|
88
|
+
].map(&:freeze).freeze
|
89
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/rspec'
|
4
|
+
|
5
|
+
module Cuprum::Collections::RSpec
|
6
|
+
# Contract validating the behavior of an InsertOne command implementation.
|
7
|
+
INSERT_ONE_COMMAND_CONTRACT = lambda do
|
8
|
+
describe '#call' do
|
9
|
+
let(:matching_data) { attributes }
|
10
|
+
let(:expected_data) do
|
11
|
+
defined?(super()) ? super() : matching_data
|
12
|
+
end
|
13
|
+
let(:primary_key_name) do
|
14
|
+
defined?(super()) ? super() : :id
|
15
|
+
end
|
16
|
+
let(:primary_key_type) do
|
17
|
+
defined?(super()) ? super() : Integer
|
18
|
+
end
|
19
|
+
let(:scoped) do
|
20
|
+
key = primary_key_name
|
21
|
+
value = entity[primary_key_name.to_s]
|
22
|
+
|
23
|
+
query.where { { key => value } }
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should validate the :entity keyword' do
|
27
|
+
expect(command)
|
28
|
+
.to validate_parameter(:call, :entity)
|
29
|
+
.using_constraint(entity_type)
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'when the item does not exist in the collection' do
|
33
|
+
it 'should return a passing result' do
|
34
|
+
expect(command.call(entity: entity))
|
35
|
+
.to be_a_passing_result
|
36
|
+
.with_value(be == expected_data)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should append an item to the collection' do
|
40
|
+
expect { command.call(entity: entity) }
|
41
|
+
.to(
|
42
|
+
change { query.reset.count }
|
43
|
+
.by(1)
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should add the entity to the collection' do
|
48
|
+
expect { command.call(entity: entity) }
|
49
|
+
.to change(scoped, :exists?)
|
50
|
+
.to be true
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should set the attributes' do
|
54
|
+
command.call(entity: entity)
|
55
|
+
|
56
|
+
expect(scoped.to_a.first).to be == expected_data
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when the item exists in the collection' do
|
61
|
+
let(:data) { fixtures_data }
|
62
|
+
let(:expected_error) do
|
63
|
+
Cuprum::Collections::Errors::AlreadyExists.new(
|
64
|
+
collection_name: collection_name,
|
65
|
+
primary_key_name: primary_key_name,
|
66
|
+
primary_key_values: attributes[primary_key_name]
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should return a failing result' do
|
71
|
+
expect(command.call(entity: entity))
|
72
|
+
.to be_a_failing_result
|
73
|
+
.with_error(expected_error)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should not append an item to the collection' do
|
77
|
+
expect { command.call(entity: entity) }
|
78
|
+
.not_to(change { query.reset.count })
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/rspec'
|
4
|
+
|
5
|
+
module Cuprum::Collections::RSpec
|
6
|
+
# Contract validating the behavior of a QueryBuilder implementation.
|
7
|
+
QUERY_BUILDER_CONTRACT = lambda do
|
8
|
+
describe '#base_query' do
|
9
|
+
include_examples 'should define reader', :base_query, -> { base_query }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#call' do
|
13
|
+
let(:criteria) { [['title', :equal, 'The Naked Sun']] }
|
14
|
+
let(:expected) { criteria }
|
15
|
+
let(:filter) { { title: 'The Naked Sun' } }
|
16
|
+
let(:strategy) { :custom }
|
17
|
+
let(:parser) do
|
18
|
+
instance_double(
|
19
|
+
Cuprum::Collections::Queries::Parse,
|
20
|
+
call: Cuprum::Result.new(value: criteria)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
let(:query) do
|
24
|
+
builder.call(strategy: strategy, where: filter)
|
25
|
+
end
|
26
|
+
|
27
|
+
before(:example) do
|
28
|
+
allow(Cuprum::Collections::Queries::Parse)
|
29
|
+
.to receive(:new)
|
30
|
+
.and_return(parser)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should define the method' do
|
34
|
+
expect(builder).to respond_to(:call)
|
35
|
+
.with(0).arguments
|
36
|
+
.and_keywords(:strategy, :where)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should parse the criteria' do
|
40
|
+
builder.call(strategy: strategy, where: filter)
|
41
|
+
|
42
|
+
expect(parser)
|
43
|
+
.to have_received(:call)
|
44
|
+
.with(strategy: strategy, where: filter)
|
45
|
+
end
|
46
|
+
|
47
|
+
it { expect(query).to be_a base_query.class }
|
48
|
+
|
49
|
+
it { expect(query).not_to be base_query }
|
50
|
+
|
51
|
+
it { expect(query.criteria).to be == expected }
|
52
|
+
|
53
|
+
describe 'with strategy: :unsafe' do
|
54
|
+
let(:strategy) { :unsafe }
|
55
|
+
let(:filter) { criteria }
|
56
|
+
|
57
|
+
it 'should not parse the criteria' do
|
58
|
+
builder.call(strategy: strategy, where: filter)
|
59
|
+
|
60
|
+
expect(parser).not_to have_received(:call)
|
61
|
+
end
|
62
|
+
|
63
|
+
it { expect(query.criteria).to be == expected }
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when the query has existing criteria' do
|
67
|
+
let(:old_criteria) { [['genre', :eq, 'Science Fiction']] }
|
68
|
+
let(:expected) { old_criteria + criteria }
|
69
|
+
let(:base_query) { super().send(:with_criteria, old_criteria) }
|
70
|
+
|
71
|
+
it { expect(query.criteria).to be == expected }
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when the parser is unable to parse the query' do
|
75
|
+
let(:error) { Cuprum::Error.new(message: 'Something went wrong.') }
|
76
|
+
let(:result) { Cuprum::Result.new(error: error) }
|
77
|
+
|
78
|
+
before(:example) do
|
79
|
+
allow(parser).to receive(:call).and_return(result)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should raise an exception' do
|
83
|
+
expect do
|
84
|
+
builder.call(strategy: strategy, where: filter)
|
85
|
+
end
|
86
|
+
.to raise_error Cuprum::Collections::QueryBuilder::ParseError,
|
87
|
+
error.message
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|