cuprum-collections 0.4.0 → 0.5.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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -0
  3. data/README.md +5 -5
  4. data/lib/cuprum/collections/association.rb +9 -28
  5. data/lib/cuprum/collections/associations/belongs_to.rb +1 -8
  6. data/lib/cuprum/collections/associations/has_many.rb +1 -10
  7. data/lib/cuprum/collections/associations/has_one.rb +1 -10
  8. data/lib/cuprum/collections/basic/collection.rb +56 -49
  9. data/lib/cuprum/collections/basic/command.rb +22 -88
  10. data/lib/cuprum/collections/basic/commands/assign_one.rb +2 -6
  11. data/lib/cuprum/collections/basic/commands/build_one.rb +1 -4
  12. data/lib/cuprum/collections/basic/commands/destroy_one.rb +4 -8
  13. data/lib/cuprum/collections/basic/commands/find_many.rb +4 -24
  14. data/lib/cuprum/collections/basic/commands/find_matching.rb +5 -21
  15. data/lib/cuprum/collections/basic/commands/find_one.rb +3 -20
  16. data/lib/cuprum/collections/basic/commands/insert_one.rb +3 -6
  17. data/lib/cuprum/collections/basic/commands/update_one.rb +3 -6
  18. data/lib/cuprum/collections/basic/commands/validate_one.rb +13 -18
  19. data/lib/cuprum/collections/basic/query.rb +26 -40
  20. data/lib/cuprum/collections/basic/repository.rb +4 -3
  21. data/lib/cuprum/collections/basic/scopes/all_scope.rb +25 -0
  22. data/lib/cuprum/collections/basic/scopes/base.rb +32 -0
  23. data/lib/cuprum/collections/basic/scopes/builder.rb +39 -0
  24. data/lib/cuprum/collections/basic/scopes/conjunction_scope.rb +20 -0
  25. data/lib/cuprum/collections/basic/scopes/criteria_scope.rb +62 -0
  26. data/lib/cuprum/collections/basic/scopes/disjunction_scope.rb +20 -0
  27. data/lib/cuprum/collections/basic/scopes/none_scope.rb +33 -0
  28. data/lib/cuprum/collections/basic/scopes.rb +23 -0
  29. data/lib/cuprum/collections/basic.rb +1 -0
  30. data/lib/cuprum/collections/collection.rb +24 -82
  31. data/lib/cuprum/collections/collection_command.rb +116 -0
  32. data/lib/cuprum/collections/commands/abstract_find_many.rb +11 -21
  33. data/lib/cuprum/collections/commands/abstract_find_matching.rb +43 -24
  34. data/lib/cuprum/collections/commands/abstract_find_one.rb +7 -10
  35. data/lib/cuprum/collections/commands/associations/find_many.rb +3 -8
  36. data/lib/cuprum/collections/commands/associations/require_many.rb +5 -5
  37. data/lib/cuprum/collections/commands/create.rb +3 -3
  38. data/lib/cuprum/collections/commands/find_one_matching.rb +6 -6
  39. data/lib/cuprum/collections/commands/query_command.rb +19 -0
  40. data/lib/cuprum/collections/commands/update.rb +3 -3
  41. data/lib/cuprum/collections/commands/upsert.rb +10 -10
  42. data/lib/cuprum/collections/commands.rb +1 -0
  43. data/lib/cuprum/collections/constraints/ordering.rb +2 -2
  44. data/lib/cuprum/collections/errors/abstract_find_error.rb +25 -42
  45. data/lib/cuprum/collections/errors/extra_attributes.rb +3 -3
  46. data/lib/cuprum/collections/errors/failed_validation.rb +2 -2
  47. data/lib/cuprum/collections/errors/invalid_parameters.rb +2 -2
  48. data/lib/cuprum/collections/errors/invalid_query.rb +10 -16
  49. data/lib/cuprum/collections/errors/missing_default_contract.rb +1 -1
  50. data/lib/cuprum/collections/errors/unknown_operator.rb +1 -1
  51. data/lib/cuprum/collections/queries.rb +31 -0
  52. data/lib/cuprum/collections/query.rb +50 -62
  53. data/lib/cuprum/collections/relation.rb +5 -383
  54. data/lib/cuprum/collections/relations/cardinality.rb +66 -0
  55. data/lib/cuprum/collections/relations/options.rb +18 -0
  56. data/lib/cuprum/collections/relations/parameters.rb +217 -0
  57. data/lib/cuprum/collections/relations/primary_keys.rb +23 -0
  58. data/lib/cuprum/collections/relations/scope.rb +65 -0
  59. data/lib/cuprum/collections/relations.rb +14 -0
  60. data/lib/cuprum/collections/repository.rb +5 -5
  61. data/lib/cuprum/collections/resource.rb +10 -41
  62. data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +80 -90
  63. data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +69 -111
  64. data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +42 -1335
  65. data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +352 -531
  66. data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +74 -191
  67. data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +13 -13
  68. data/lib/cuprum/collections/rspec/contracts/scope_contracts.rb +1029 -0
  69. data/lib/cuprum/collections/rspec/contracts/scopes/builder_contracts.rb +856 -0
  70. data/lib/cuprum/collections/rspec/contracts/scopes/composition_contracts.rb +1430 -0
  71. data/lib/cuprum/collections/rspec/contracts/scopes/criteria_contracts.rb +2217 -0
  72. data/lib/cuprum/collections/rspec/contracts/scopes/logical_contracts.rb +297 -0
  73. data/lib/cuprum/collections/rspec/contracts/scopes.rb +13 -0
  74. data/lib/cuprum/collections/rspec/contracts.rb +2 -0
  75. data/lib/cuprum/collections/rspec/deferred/association_examples.rb +2098 -0
  76. data/lib/cuprum/collections/rspec/deferred/collection_examples.rb +338 -0
  77. data/lib/cuprum/collections/rspec/deferred/command_examples.rb +160 -0
  78. data/lib/cuprum/collections/rspec/deferred/commands/assign_one_examples.rb +178 -0
  79. data/lib/cuprum/collections/rspec/deferred/commands/build_one_examples.rb +94 -0
  80. data/lib/cuprum/collections/rspec/deferred/commands/destroy_one_examples.rb +118 -0
  81. data/lib/cuprum/collections/rspec/deferred/commands/find_many_examples.rb +307 -0
  82. data/lib/cuprum/collections/rspec/deferred/commands/find_matching_examples.rb +143 -0
  83. data/lib/cuprum/collections/rspec/deferred/commands/find_one_examples.rb +116 -0
  84. data/lib/cuprum/collections/rspec/deferred/commands/insert_one_examples.rb +103 -0
  85. data/lib/cuprum/collections/rspec/deferred/commands/update_one_examples.rb +99 -0
  86. data/lib/cuprum/collections/rspec/deferred/commands/validate_one_examples.rb +117 -0
  87. data/lib/cuprum/collections/rspec/deferred/commands.rb +8 -0
  88. data/lib/cuprum/collections/rspec/deferred/relation_examples.rb +1437 -0
  89. data/lib/cuprum/collections/rspec/deferred/resource_examples.rb +26 -0
  90. data/lib/cuprum/collections/rspec/deferred.rb +8 -0
  91. data/lib/cuprum/collections/scope.rb +29 -0
  92. data/lib/cuprum/collections/scopes/all.rb +51 -0
  93. data/lib/cuprum/collections/scopes/all_scope.rb +18 -0
  94. data/lib/cuprum/collections/scopes/base.rb +79 -0
  95. data/lib/cuprum/collections/scopes/builder.rb +39 -0
  96. data/lib/cuprum/collections/scopes/building.rb +221 -0
  97. data/lib/cuprum/collections/scopes/composition.rb +162 -0
  98. data/lib/cuprum/collections/scopes/conjunction.rb +44 -0
  99. data/lib/cuprum/collections/scopes/conjunction_scope.rb +12 -0
  100. data/lib/cuprum/collections/scopes/container.rb +65 -0
  101. data/lib/cuprum/collections/scopes/criteria/parser.rb +241 -0
  102. data/lib/cuprum/collections/scopes/criteria.rb +206 -0
  103. data/lib/cuprum/collections/scopes/criteria_scope.rb +12 -0
  104. data/lib/cuprum/collections/scopes/disjunction.rb +45 -0
  105. data/lib/cuprum/collections/scopes/disjunction_scope.rb +12 -0
  106. data/lib/cuprum/collections/scopes/none.rb +62 -0
  107. data/lib/cuprum/collections/scopes/none_scope.rb +18 -0
  108. data/lib/cuprum/collections/scopes.rb +23 -0
  109. data/lib/cuprum/collections/version.rb +2 -2
  110. data/lib/cuprum/collections.rb +14 -9
  111. metadata +61 -15
  112. data/lib/cuprum/collections/basic/query_builder.rb +0 -69
  113. data/lib/cuprum/collections/command.rb +0 -26
  114. data/lib/cuprum/collections/queries/parse.rb +0 -22
  115. data/lib/cuprum/collections/queries/parse_block.rb +0 -206
  116. data/lib/cuprum/collections/queries/parse_strategy.rb +0 -91
  117. data/lib/cuprum/collections/query_builder.rb +0 -61
  118. data/lib/cuprum/collections/rspec/contracts/basic/command_contracts.rb +0 -484
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections/rspec/deferred/command_examples'
4
+ require 'cuprum/collections/rspec/deferred/commands'
5
+
6
+ module Cuprum::Collections::RSpec::Deferred::Commands
7
+ # Namespace for deferred example groups for validating BuildOne commands.
8
+ module BuildOneExamples
9
+ include RSpec::SleepingKingStudios::Deferred::Provider
10
+
11
+ deferred_examples 'should implement the BuildOne command' \
12
+ do |allow_extra_attributes: true|
13
+ describe '#call' do
14
+ include Cuprum::Collections::RSpec::Deferred::CommandExamples
15
+
16
+ shared_examples 'should build the entity' do
17
+ it { expect(result).to be_a_passing_result }
18
+
19
+ it { expect(result.value).to be == expected_value }
20
+ end
21
+
22
+ let(:attributes) { {} }
23
+ let(:result) { command.call(attributes:) }
24
+ let(:expected_attributes) do
25
+ attributes
26
+ end
27
+ let(:expected_value) do
28
+ defined?(super()) ? super() : attributes
29
+ end
30
+
31
+ def call_command
32
+ command.call(attributes:)
33
+ end
34
+
35
+ include_deferred 'should validate the attributes parameter'
36
+
37
+ describe 'with an empty attributes hash' do
38
+ let(:attributes) { {} }
39
+
40
+ include_examples 'should build the entity'
41
+ end
42
+
43
+ describe 'with an attributes hash with partial attributes' do
44
+ let(:attributes) { { title: 'Gideon the Ninth' } }
45
+
46
+ include_examples 'should build the entity'
47
+ end
48
+
49
+ describe 'with an attributes hash with full attributes' do
50
+ let(:attributes) do
51
+ {
52
+ title: 'Gideon the Ninth',
53
+ author: 'Tamsyn Muir',
54
+ series: 'The Locked Tomb',
55
+ category: 'Horror'
56
+ }
57
+ end
58
+
59
+ include_examples 'should build the entity'
60
+ end
61
+
62
+ describe 'with an attributes hash with extra attributes' do
63
+ let(:attributes) do
64
+ {
65
+ title: 'The Book of Lost Tales',
66
+ audiobook: true
67
+ }
68
+ end
69
+
70
+ if allow_extra_attributes
71
+ include_examples 'should build the entity'
72
+ else
73
+ # :nocov:
74
+ let(:valid_attributes) do
75
+ defined?(super()) ? super() : expected_attributes.keys
76
+ end
77
+ let(:expected_error) do
78
+ Cuprum::Collections::Errors::ExtraAttributes.new(
79
+ entity_class:,
80
+ extra_attributes: %w[audiobook],
81
+ valid_attributes:
82
+ )
83
+ end
84
+
85
+ it 'should return a failing result' do
86
+ expect(result).to be_a_failing_result.with_error(expected_error)
87
+ end
88
+ # :nocov:
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections/rspec/deferred/command_examples'
4
+ require 'cuprum/collections/rspec/deferred/commands'
5
+
6
+ module Cuprum::Collections::RSpec::Deferred::Commands
7
+ # Namespace for deferred example groups for validating DestroyOne commands.
8
+ module DestroyOneExamples
9
+ include RSpec::SleepingKingStudios::Deferred::Provider
10
+
11
+ deferred_examples 'should implement the DestroyOne command' do
12
+ describe '#call' do
13
+ include Cuprum::Collections::RSpec::Deferred::CommandExamples
14
+
15
+ let(:query) { collection.query }
16
+ let(:mapped_data) do
17
+ defined?(super()) ? super() : data
18
+ end
19
+ let(:invalid_primary_key_value) do
20
+ defined?(super()) ? super() : 100
21
+ end
22
+ let(:valid_primary_key_value) do
23
+ defined?(super()) ? super() : 0
24
+ end
25
+
26
+ def call_command
27
+ command.call(primary_key:)
28
+ end
29
+
30
+ include_deferred 'should validate the primary key parameter'
31
+
32
+ describe 'with an invalid primary key' do
33
+ let(:primary_key) { invalid_primary_key_value }
34
+ let(:expected_error) do
35
+ Cuprum::Collections::Errors::NotFound.new(
36
+ attribute_name: collection.primary_key_name,
37
+ attribute_value: primary_key,
38
+ collection_name: collection.name,
39
+ primary_key: true
40
+ )
41
+ end
42
+
43
+ it 'should return a failing result' do
44
+ expect(command.call(primary_key:))
45
+ .to be_a_failing_result
46
+ .with_error(expected_error)
47
+ end
48
+
49
+ it 'should not remove an entity from the collection' do
50
+ expect { command.call(primary_key:) }
51
+ .not_to(change { query.reset.count })
52
+ end
53
+ end
54
+
55
+ context 'when the collection has many items' do
56
+ let(:data) { fixtures_data }
57
+ let(:matching_data) do
58
+ mapped_data.find do |item|
59
+ item[collection.primary_key_name.to_s] == primary_key
60
+ end
61
+ end
62
+ let!(:expected_data) do
63
+ defined?(super()) ? super() : matching_data
64
+ end
65
+
66
+ describe 'with an invalid primary key' do
67
+ let(:primary_key) { invalid_primary_key_value }
68
+ let(:expected_error) do
69
+ Cuprum::Collections::Errors::NotFound.new(
70
+ attribute_name: collection.primary_key_name,
71
+ attribute_value: primary_key,
72
+ collection_name: collection.name,
73
+ primary_key: true
74
+ )
75
+ end
76
+
77
+ it 'should return a failing result' do
78
+ expect(command.call(primary_key:))
79
+ .to be_a_failing_result
80
+ .with_error(expected_error)
81
+ end
82
+
83
+ it 'should not remove an entity from the collection' do
84
+ expect { command.call(primary_key:) }
85
+ .not_to(change { query.reset.count })
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:))
94
+ .to be_a_passing_result
95
+ .with_value(expected_data)
96
+ end
97
+
98
+ it 'should remove an entity from the collection' do
99
+ expect { command.call(primary_key:) }
100
+ .to(
101
+ change { query.reset.count }.by(-1)
102
+ )
103
+ end
104
+
105
+ it 'should remove the entity from the collection' do
106
+ command.call(primary_key:)
107
+
108
+ expect(
109
+ query.map { |item| item[collection.primary_key_name.to_s] }
110
+ )
111
+ .not_to include primary_key
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,307 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections/rspec/deferred/command_examples'
4
+ require 'cuprum/collections/rspec/deferred/commands'
5
+
6
+ module Cuprum::Collections::RSpec::Deferred::Commands
7
+ # Namespace for deferred example groups for validating FindMany commands.
8
+ module FindManyExamples
9
+ include RSpec::SleepingKingStudios::Deferred::Provider
10
+
11
+ deferred_examples 'should implement the FindMany command' do
12
+ describe '#call' do
13
+ include Cuprum::Collections::RSpec::Deferred::CommandExamples
14
+
15
+ let(:mapped_data) do
16
+ defined?(super()) ? super() : data
17
+ end
18
+ let(:primary_key_name) { defined?(super()) ? super() : 'id' }
19
+ let(:primary_key_type) { defined?(super()) ? super() : Integer }
20
+ let(:invalid_primary_key_values) do
21
+ defined?(super()) ? super() : [100, 101, 102]
22
+ end
23
+ let(:valid_primary_key_values) do
24
+ defined?(super()) ? super() : [0, 1, 2]
25
+ end
26
+ let(:primary_keys) { [] }
27
+ let(:options) { {} }
28
+
29
+ def call_command
30
+ command.call(primary_keys:, **options)
31
+ end
32
+
33
+ include_deferred 'should validate the primary keys parameter'
34
+
35
+ describe 'with an invalid allow_partial value' do
36
+ let(:options) { super().merge(allow_partial: Object.new.freeze) }
37
+
38
+ include_deferred 'should validate the parameter',
39
+ :allow_partial,
40
+ 'sleeping_king_studios.tools.assertions.boolean'
41
+ end
42
+
43
+ describe 'with an invalid envelope value' do
44
+ let(:options) { super().merge(envelope: Object.new.freeze) }
45
+
46
+ include_deferred 'should validate the parameter',
47
+ :envelope,
48
+ 'sleeping_king_studios.tools.assertions.boolean'
49
+ end
50
+
51
+ describe 'with an empty array of primary keys' do
52
+ let(:primary_keys) { [] }
53
+ let(:expected_data) { [] }
54
+
55
+ it 'should return a passing result' do
56
+ expect(command.call(primary_keys:))
57
+ .to be_a_passing_result
58
+ .with_value(expected_data)
59
+ end
60
+ end
61
+
62
+ describe 'with an array of invalid primary keys' do
63
+ let(:primary_keys) { invalid_primary_key_values }
64
+ let(:expected_error) do
65
+ Cuprum::Errors::MultipleErrors.new(
66
+ errors: primary_keys.map do |primary_key|
67
+ Cuprum::Collections::Errors::NotFound.new(
68
+ attribute_name: collection.primary_key_name,
69
+ attribute_value: primary_key,
70
+ collection_name: collection.name,
71
+ primary_key: true
72
+ )
73
+ end
74
+ )
75
+ end
76
+
77
+ it 'should return a failing result' do
78
+ expect(command.call(primary_keys:))
79
+ .to be_a_failing_result
80
+ .with_error(expected_error)
81
+ end
82
+ end
83
+
84
+ context 'when the collection has many items' do
85
+ let(:data) { fixtures_data }
86
+ let(:matching_data) do
87
+ primary_keys
88
+ .map do |key|
89
+ mapped_data.find { |item| item[primary_key_name.to_s] == key }
90
+ end
91
+ end
92
+ let(:expected_data) do
93
+ defined?(super()) ? super() : matching_data
94
+ end
95
+
96
+ describe 'with an empty array of primary keys' do
97
+ let(:primary_keys) { [] }
98
+ let(:expected_data) { [] }
99
+
100
+ it 'should return a passing result' do
101
+ expect(command.call(primary_keys:))
102
+ .to be_a_passing_result
103
+ .with_value(expected_data)
104
+ end
105
+ end
106
+
107
+ describe 'with an array of invalid primary keys' do
108
+ let(:primary_keys) { invalid_primary_key_values }
109
+ let(:expected_error) do
110
+ Cuprum::Errors::MultipleErrors.new(
111
+ errors: primary_keys.map do |primary_key|
112
+ Cuprum::Collections::Errors::NotFound.new(
113
+ attribute_name: collection.primary_key_name,
114
+ attribute_value: primary_key,
115
+ collection_name: collection.name,
116
+ primary_key: true
117
+ )
118
+ end
119
+ )
120
+ end
121
+
122
+ it 'should return a failing result' do
123
+ expect(command.call(primary_keys:))
124
+ .to be_a_failing_result
125
+ .with_error(expected_error)
126
+ end
127
+ end
128
+
129
+ describe 'with a partially valid array of primary keys' do
130
+ let(:primary_keys) do
131
+ invalid_primary_key_values + valid_primary_key_values
132
+ end
133
+ let(:expected_error) do
134
+ Cuprum::Errors::MultipleErrors.new(
135
+ errors: primary_keys.map do |primary_key|
136
+ unless invalid_primary_key_values.include?(primary_key)
137
+ next nil
138
+ end
139
+
140
+ Cuprum::Collections::Errors::NotFound.new(
141
+ attribute_name: collection.primary_key_name,
142
+ attribute_value: primary_key,
143
+ collection_name: collection.name,
144
+ primary_key: true
145
+ )
146
+ end
147
+ )
148
+ end
149
+
150
+ it 'should return a failing result' do
151
+ expect(command.call(primary_keys:))
152
+ .to be_a_failing_result
153
+ .with_error(expected_error)
154
+ end
155
+ end
156
+
157
+ describe 'with a valid array of primary keys' do
158
+ let(:primary_keys) { valid_primary_key_values }
159
+
160
+ it 'should return a passing result' do
161
+ expect(command.call(primary_keys:))
162
+ .to be_a_passing_result
163
+ .with_value(expected_data)
164
+ end
165
+
166
+ describe 'with an ordered array of primary keys' do
167
+ let(:primary_keys) { valid_primary_key_values.reverse }
168
+
169
+ it 'should return a passing result' do
170
+ expect(command.call(primary_keys:))
171
+ .to be_a_passing_result
172
+ .with_value(expected_data)
173
+ end
174
+ end
175
+ end
176
+
177
+ describe 'with allow_partial: true' do
178
+ describe 'with an array of invalid primary keys' do
179
+ let(:primary_keys) { invalid_primary_key_values }
180
+ let(:expected_error) do
181
+ Cuprum::Errors::MultipleErrors.new(
182
+ errors: invalid_primary_key_values.map do |primary_key|
183
+ Cuprum::Collections::Errors::NotFound.new(
184
+ attribute_name: collection.primary_key_name,
185
+ attribute_value: primary_key,
186
+ collection_name: collection.name,
187
+ primary_key: true
188
+ )
189
+ end
190
+ )
191
+ end
192
+
193
+ it 'should return a failing result' do
194
+ expect(command.call(primary_keys:))
195
+ .to be_a_failing_result
196
+ .with_error(expected_error)
197
+ end
198
+ end
199
+
200
+ describe 'with a partially valid array of primary keys' do
201
+ let(:primary_keys) do
202
+ invalid_primary_key_values + valid_primary_key_values
203
+ end
204
+ let(:expected_error) do
205
+ Cuprum::Errors::MultipleErrors.new(
206
+ errors: primary_keys.map do |primary_key|
207
+ unless invalid_primary_key_values.include?(primary_key)
208
+ next nil
209
+ end
210
+
211
+ Cuprum::Collections::Errors::NotFound.new(
212
+ attribute_name: collection.primary_key_name,
213
+ attribute_value: primary_key,
214
+ collection_name: collection.name,
215
+ primary_key: true
216
+ )
217
+ end
218
+ )
219
+ end
220
+
221
+ it 'should return a passing result' do
222
+ expect(
223
+ command.call(
224
+ primary_keys:,
225
+ allow_partial: true
226
+ )
227
+ )
228
+ .to be_a_passing_result
229
+ .with_value(expected_data)
230
+ .and_error(expected_error)
231
+ end
232
+ end
233
+
234
+ describe 'with a valid array of primary keys' do
235
+ let(:primary_keys) { valid_primary_key_values }
236
+
237
+ it 'should return a passing result' do
238
+ expect(
239
+ command.call(
240
+ primary_keys:,
241
+ allow_partial: true
242
+ )
243
+ )
244
+ .to be_a_passing_result
245
+ .with_value(expected_data)
246
+ end
247
+
248
+ describe 'with an ordered array of primary keys' do
249
+ let(:primary_keys) { valid_primary_key_values.reverse }
250
+
251
+ it 'should return a passing result' do
252
+ expect(
253
+ command.call(
254
+ primary_keys:,
255
+ allow_partial: true
256
+ )
257
+ )
258
+ .to be_a_passing_result
259
+ .with_value(expected_data)
260
+ end
261
+ end
262
+ end
263
+ end
264
+
265
+ describe 'with envelope: true' do
266
+ describe 'with an empty array of primary keys' do
267
+ let(:primary_keys) { [] }
268
+ let(:expected_data) { [] }
269
+
270
+ it 'should return a passing result' do
271
+ expect(
272
+ command.call(primary_keys:, envelope: true)
273
+ )
274
+ .to be_a_passing_result
275
+ .with_value(match({ collection.name => expected_data }))
276
+ end
277
+ end
278
+
279
+ describe 'with a valid array of primary keys' do
280
+ let(:primary_keys) { valid_primary_key_values }
281
+
282
+ it 'should return a passing result' do
283
+ expect(
284
+ command.call(primary_keys:, envelope: true)
285
+ )
286
+ .to be_a_passing_result
287
+ .with_value(match({ collection.name => expected_data }))
288
+ end
289
+
290
+ describe 'with an ordered array of primary keys' do
291
+ let(:primary_keys) { valid_primary_key_values.reverse }
292
+
293
+ it 'should return a passing result' do
294
+ expect(
295
+ command.call(primary_keys:, envelope: true)
296
+ )
297
+ .to be_a_passing_result
298
+ .with_value(match({ collection.name => expected_data }))
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections/rspec/contracts/query_contracts'
4
+ require 'cuprum/collections/rspec/deferred/command_examples'
5
+ require 'cuprum/collections/rspec/deferred/commands'
6
+
7
+ module Cuprum::Collections::RSpec::Deferred::Commands
8
+ # Namespace for deferred example groups for validating FindMatching commands.
9
+ module FindMatchingExamples
10
+ include RSpec::SleepingKingStudios::Deferred::Provider
11
+
12
+ deferred_examples 'should implement the FindMatching command' do
13
+ describe '#call' do
14
+ include Cuprum::Collections::RSpec::Contracts::QueryContracts
15
+ include Cuprum::Collections::RSpec::Deferred::CommandExamples
16
+
17
+ shared_examples 'should return the matching items' do
18
+ it { expect(result).to be_a_passing_result }
19
+
20
+ it { expect(result.value).to be_a Enumerator }
21
+
22
+ it { expect(result.value.to_a).to be == expected_data }
23
+ end
24
+
25
+ shared_examples 'should return the wrapped items' do
26
+ it { expect(result).to be_a_passing_result }
27
+
28
+ it { expect(result.value).to be_a Hash }
29
+
30
+ it { expect(result.value.keys).to be == [collection.name] }
31
+
32
+ it { expect(result.value[collection.name]).to be == expected_data }
33
+ end
34
+
35
+ let(:filter) { nil }
36
+ let(:limit) { nil }
37
+ let(:offset) { nil }
38
+ let(:order) { nil }
39
+ let(:options) do
40
+ opts = {}
41
+
42
+ opts[:limit] = limit if limit
43
+ opts[:offset] = offset if offset
44
+ opts[:order] = order if order
45
+ opts[:where] = filter unless filter.nil? || filter.is_a?(Proc)
46
+
47
+ opts
48
+ end
49
+ let(:block) { filter.is_a?(Proc) ? filter : nil }
50
+ let(:result) { command.call(**options, &block) }
51
+ let(:data) { [] }
52
+ let(:filtered_data) do
53
+ defined?(super()) ? super() : data
54
+ end
55
+ let(:matching_data) do
56
+ defined?(super()) ? super() : filtered_data
57
+ end
58
+ let(:expected_data) do
59
+ defined?(super()) ? super() : matching_data
60
+ end
61
+
62
+ def call_command
63
+ command.call(**options, &block)
64
+ end
65
+
66
+ describe 'with an invalid envelope value' do
67
+ let(:options) { super().merge(envelope: Object.new.freeze) }
68
+
69
+ include_deferred 'should validate the parameter',
70
+ :envelope,
71
+ 'sleeping_king_studios.tools.assertions.boolean'
72
+ end
73
+
74
+ describe 'with an invalid limit value' do
75
+ let(:options) { super().merge(limit: Object.new.freeze) }
76
+
77
+ include_deferred 'should validate the parameter',
78
+ :limit,
79
+ 'sleeping_king_studios.tools.assertions.instance_of',
80
+ expected: Integer
81
+ end
82
+
83
+ describe 'with an invalid offset value' do
84
+ let(:options) { super().merge(offset: Object.new.freeze) }
85
+
86
+ include_deferred 'should validate the parameter',
87
+ :offset,
88
+ 'sleeping_king_studios.tools.assertions.instance_of',
89
+ expected: Integer
90
+ end
91
+
92
+ describe 'with an invalid order value' do
93
+ let(:options) { super().merge(order: Object.new.freeze) }
94
+
95
+ include_deferred 'should validate the parameter',
96
+ :order,
97
+ message: 'order is not a valid sort order'
98
+ end
99
+
100
+ describe 'with an invalid where value' do
101
+ let(:options) { super().merge(where: Object.new.freeze) }
102
+
103
+ include_deferred 'should validate the parameter',
104
+ :where,
105
+ message: 'where is not a scope or query hash'
106
+ end
107
+
108
+ describe 'with an invalid filter block' do
109
+ let(:block) { -> {} }
110
+ let(:expected_error) do
111
+ an_instance_of(Cuprum::Collections::Errors::InvalidQuery)
112
+ end
113
+
114
+ it 'should return a failing result' do
115
+ expect(result).to be_a_failing_result.with_error(expected_error)
116
+ end
117
+ end
118
+
119
+ include_examples 'should return the matching items'
120
+
121
+ describe 'with envelope: true' do
122
+ let(:options) { super().merge(envelope: true) }
123
+
124
+ include_examples 'should return the wrapped items'
125
+ end
126
+
127
+ context 'when the collection has many items' do
128
+ let(:data) { fixtures_data }
129
+
130
+ include_contract 'should query the collection' do
131
+ include_examples 'should return the matching items'
132
+
133
+ describe 'with envelope: true' do
134
+ let(:options) { super().merge(envelope: true) }
135
+
136
+ include_examples 'should return the wrapped items'
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end