cuprum-collections 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/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
|