mongo 2.0.0.rc → 2.0.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -1
- data/README.md +1 -4
- data/lib/mongo/client.rb +1 -1
- data/lib/mongo/collection.rb +1 -1
- data/lib/mongo/collection/view.rb +1 -1
- data/lib/mongo/collection/view/writable.rb +26 -10
- data/lib/mongo/database.rb +1 -1
- data/lib/mongo/grid/file.rb +12 -0
- data/lib/mongo/grid/file/chunk.rb +1 -1
- data/lib/mongo/grid/file/metadata.rb +13 -0
- data/lib/mongo/operation/aggregate.rb +1 -1
- data/lib/mongo/operation/write.rb +1 -5
- data/lib/mongo/operation/write/bulk.rb +17 -0
- data/lib/mongo/operation/write/{bulk_delete.rb → bulk/bulk_delete.rb} +1 -1
- data/lib/mongo/operation/{bulk_delete → write/bulk/bulk_delete}/result.rb +3 -0
- data/lib/mongo/operation/write/{bulk_insert.rb → bulk/bulk_insert.rb} +1 -1
- data/lib/mongo/operation/{bulk_insert → write/bulk/bulk_insert}/result.rb +3 -0
- data/lib/mongo/operation/write/{bulk_mergable.rb → bulk/bulk_mergable.rb} +0 -0
- data/lib/mongo/operation/write/{bulk_update.rb → bulk/bulk_update.rb} +1 -1
- data/lib/mongo/operation/{bulk_update → write/bulk/bulk_update}/result.rb +8 -1
- data/lib/mongo/operation/write/{legacy_bulk_mergable.rb → bulk/legacy_bulk_mergable.rb} +0 -0
- data/lib/mongo/operation/write/delete.rb +2 -0
- data/lib/mongo/operation/write/delete/result.rb +40 -0
- data/lib/mongo/operation/write/insert.rb +2 -0
- data/lib/mongo/operation/write/insert/result.rb +32 -0
- data/lib/mongo/operation/write/update.rb +9 -4
- data/lib/mongo/operation/write/update/result.rb +160 -0
- data/lib/mongo/server/connectable.rb +11 -0
- data/lib/mongo/server/connection.rb +1 -0
- data/lib/mongo/server/connection_pool.rb +1 -1
- data/lib/mongo/server/connection_pool/queue.rb +1 -1
- data/lib/mongo/server/monitor.rb +16 -0
- data/lib/mongo/server/monitor/connection.rb +1 -0
- data/lib/mongo/socket/ssl.rb +30 -8
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo/write_concern/acknowledged.rb +1 -1
- data/lib/mongo/write_concern/unacknowledged.rb +1 -1
- data/spec/certificates/ca.pem +17 -0
- data/spec/certificates/client.pem +101 -0
- data/spec/certificates/crl.pem +10 -0
- data/spec/certificates/crl_client_revoked.pem +12 -0
- data/spec/certificates/password_protected.pem +51 -0
- data/spec/certificates/server.pem +34 -0
- data/spec/mongo/collection/view/writable_spec.rb +175 -0
- data/spec/mongo/crud_spec.rb +42 -0
- data/spec/mongo/grid/file/metadata_spec.rb +23 -0
- data/spec/mongo/grid/file_spec.rb +34 -0
- data/spec/mongo/operation/write/delete_spec.rb +8 -0
- data/spec/mongo/operation/write/insert_spec.rb +21 -8
- data/spec/mongo/operation/write/update_spec.rb +52 -1
- data/spec/mongo/server/connection_spec.rb +33 -10
- data/spec/mongo/server/monitor_spec.rb +14 -0
- data/spec/spec_helper.rb +2 -17
- data/spec/support/crud.rb +203 -0
- data/spec/support/crud/read.rb +144 -0
- data/spec/support/crud/write.rb +214 -0
- data/spec/support/crud_tests/read/aggregate.yml +43 -0
- data/spec/support/crud_tests/read/count.yml +37 -0
- data/spec/support/crud_tests/read/distinct.yml +33 -0
- data/spec/support/crud_tests/read/find.yml +50 -0
- data/spec/support/crud_tests/write/deleteMany.yml +36 -0
- data/spec/support/crud_tests/write/deleteOne.yml +49 -0
- data/spec/support/crud_tests/write/findOneAndDelete.yml +54 -0
- data/spec/support/crud_tests/write/findOneAndReplace.yml +153 -0
- data/spec/support/crud_tests/write/findOneAndUpdate.yml +161 -0
- data/spec/support/crud_tests/write/insertMany.yml +24 -0
- data/spec/support/crud_tests/write/insertOne.yml +19 -0
- data/spec/support/crud_tests/write/replaceOne.yml +96 -0
- data/spec/support/crud_tests/write/updateMany.yml +83 -0
- data/spec/support/crud_tests/write/updateOne.yml +80 -0
- metadata +64 -20
- metadata.gz.sig +0 -0
- data/spec/mongo_orchestration_spec.rb +0 -70
- data/spec/support/mongo_orchestration.rb +0 -61
- data/spec/support/mongo_orchestration/requestable.rb +0 -109
- data/spec/support/mongo_orchestration/standalone.rb +0 -57
@@ -92,7 +92,7 @@ describe Mongo::Operation::Write::Update do
|
|
92
92
|
})
|
93
93
|
end
|
94
94
|
|
95
|
-
context 'when the update
|
95
|
+
context 'when the update succeeds' do
|
96
96
|
|
97
97
|
let(:document) do
|
98
98
|
{ q: { name: 'test' }, u: { '$set' => { field: 'blah' }}, limit: 1 }
|
@@ -105,6 +105,18 @@ describe Mongo::Operation::Write::Update do
|
|
105
105
|
it 'updates the document' do
|
106
106
|
expect(result.written_count).to eq(1)
|
107
107
|
end
|
108
|
+
|
109
|
+
it 'reports the modified count' do
|
110
|
+
expect(result.modified_count).to eq(1)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'reports the matched count' do
|
114
|
+
expect(result.matched_count).to eq(1)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'reports the upserted id as nil' do
|
118
|
+
expect(result.upserted_id).to eq(nil)
|
119
|
+
end
|
108
120
|
end
|
109
121
|
|
110
122
|
context 'when the update fails' do
|
@@ -145,6 +157,18 @@ describe Mongo::Operation::Write::Update do
|
|
145
157
|
it 'updates the documents' do
|
146
158
|
expect(result.written_count).to eq(2)
|
147
159
|
end
|
160
|
+
|
161
|
+
it 'reports the modified count' do
|
162
|
+
expect(result.modified_count).to eq(2)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'reports the matched count' do
|
166
|
+
expect(result.matched_count).to eq(2)
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'reports the upserted id as nil' do
|
170
|
+
expect(result.upserted_id).to eq(nil)
|
171
|
+
end
|
148
172
|
end
|
149
173
|
|
150
174
|
context 'when an update fails' do
|
@@ -172,6 +196,33 @@ describe Mongo::Operation::Write::Update do
|
|
172
196
|
}.to raise_error(Mongo::Error::MaxBSONSize)
|
173
197
|
end
|
174
198
|
end
|
199
|
+
|
200
|
+
context 'when upsert is true' do
|
201
|
+
|
202
|
+
let(:document) do
|
203
|
+
{ q: { field: 'non-existent' }, u: { '$set' => { other: 'blah' }}, upsert: true }
|
204
|
+
end
|
205
|
+
|
206
|
+
let(:result) do
|
207
|
+
update.execute(authorized_primary.context)
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'inserts the document' do
|
211
|
+
expect(result.written_count).to eq(1)
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'reports the modified count' do
|
215
|
+
expect(result.modified_count).to eq(0)
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'reports the matched count' do
|
219
|
+
expect(result.matched_count).to eq(0)
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'retruns the upserted id' do
|
223
|
+
expect(result.upserted_id).to be_a(BSON::ObjectId)
|
224
|
+
end
|
225
|
+
end
|
175
226
|
end
|
176
227
|
end
|
177
228
|
end
|
@@ -154,19 +154,14 @@ describe Mongo::Server::Connection do
|
|
154
154
|
Mongo::Protocol::Query.new(TEST_DB, TEST_COLL, { 'name' => 'testing' })
|
155
155
|
end
|
156
156
|
|
157
|
-
let(:delete) do
|
158
|
-
Mongo::Protocol::Delete.new(TEST_DB, TEST_COLL, {})
|
159
|
-
end
|
160
|
-
|
161
157
|
context 'when providing a single message' do
|
162
158
|
|
163
159
|
let(:reply) do
|
164
160
|
connection.dispatch([ insert, query ])
|
165
161
|
end
|
166
162
|
|
167
|
-
# @todo: Can remove this once we have more implemented with global hooks.
|
168
163
|
after do
|
169
|
-
|
164
|
+
authorized_collection.find.delete_many
|
170
165
|
end
|
171
166
|
|
172
167
|
it 'it dispatchs the message to the socket' do
|
@@ -188,9 +183,8 @@ describe Mongo::Server::Connection do
|
|
188
183
|
connection.dispatch([ insert, command ])
|
189
184
|
end
|
190
185
|
|
191
|
-
# @todo: Can remove this once we have more implemented with global hooks.
|
192
186
|
after do
|
193
|
-
|
187
|
+
authorized_collection.find.delete_many
|
194
188
|
end
|
195
189
|
|
196
190
|
it 'it dispatchs the message to the socket' do
|
@@ -216,6 +210,31 @@ describe Mongo::Server::Connection do
|
|
216
210
|
expect(connection).to_not be_connected
|
217
211
|
end
|
218
212
|
end
|
213
|
+
|
214
|
+
context 'when the process is forked' do
|
215
|
+
|
216
|
+
let(:insert) do
|
217
|
+
Mongo::Protocol::Insert.new(TEST_DB, TEST_COLL, documents)
|
218
|
+
end
|
219
|
+
|
220
|
+
before do
|
221
|
+
expect(Process).to receive(:pid).at_least(:once).and_return(1)
|
222
|
+
end
|
223
|
+
|
224
|
+
after do
|
225
|
+
authorized_collection.find.delete_many
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'disconnects the connection' do
|
229
|
+
expect(connection).to receive(:disconnect!).and_call_original
|
230
|
+
connection.dispatch([ insert ])
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'sets a new pid' do
|
234
|
+
connection.dispatch([ insert ])
|
235
|
+
expect(connection.pid).to eq(1)
|
236
|
+
end
|
237
|
+
end
|
219
238
|
end
|
220
239
|
|
221
240
|
describe '#initialize' do
|
@@ -252,12 +271,16 @@ describe Mongo::Server::Connection do
|
|
252
271
|
|
253
272
|
context 'when ssl options are provided' do
|
254
273
|
|
274
|
+
let(:ssl_options) do
|
275
|
+
{ :ssl => true, :ssl_key => 'file', :ssl_key_pass_phrase => 'iamaphrase' }
|
276
|
+
end
|
277
|
+
|
255
278
|
let(:connection) do
|
256
|
-
described_class.new(server,
|
279
|
+
described_class.new(server, ssl_options)
|
257
280
|
end
|
258
281
|
|
259
282
|
it 'sets the ssl options' do
|
260
|
-
expect(connection.send(:ssl_options)).to eq(
|
283
|
+
expect(connection.send(:ssl_options)).to eq(ssl_options)
|
261
284
|
end
|
262
285
|
end
|
263
286
|
|
@@ -12,6 +12,20 @@ describe Mongo::Server::Monitor do
|
|
12
12
|
|
13
13
|
describe '#scan!' do
|
14
14
|
|
15
|
+
context 'when calling multiple times in succession' do
|
16
|
+
|
17
|
+
let(:monitor) do
|
18
|
+
described_class.new(address, listeners)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'throttles the scans to minimum 500ms' do
|
22
|
+
start = Time.now
|
23
|
+
monitor.scan!
|
24
|
+
monitor.scan!
|
25
|
+
expect(Time.now - start).to be > 0.5
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
15
29
|
context 'when the ismaster command succeeds' do
|
16
30
|
|
17
31
|
let(:monitor) do
|
data/spec/spec_helper.rb
CHANGED
@@ -22,10 +22,10 @@ require 'mongo'
|
|
22
22
|
require 'support/travis'
|
23
23
|
require 'support/matchers'
|
24
24
|
require 'support/authorization'
|
25
|
-
require 'support/mongo_orchestration'
|
26
25
|
require 'support/server_discovery_and_monitoring'
|
27
26
|
require 'support/server_selection_rtt'
|
28
27
|
require 'support/server_selection'
|
28
|
+
require 'support/crud'
|
29
29
|
|
30
30
|
Mongo::Logger.logger = Logger.new($stdout)
|
31
31
|
Mongo::Logger.logger.level = Logger::INFO
|
@@ -67,6 +67,7 @@ CURRENT_PATH = File.expand_path(File.dirname(__FILE__))
|
|
67
67
|
SERVER_DISCOVERY_TESTS = Dir.glob("#{CURRENT_PATH}/support/sdam/**/*.yml")
|
68
68
|
SERVER_SELECTION_RTT_TESTS = Dir.glob("#{CURRENT_PATH}/support/server_selection/rtt/*.yml")
|
69
69
|
SERVER_SELECTION_TESTS = Dir.glob("#{CURRENT_PATH}/support/server_selection/selection/**/*.yml")
|
70
|
+
CRUD_TESTS = Dir.glob("#{CURRENT_PATH}/support/crud_tests/**/*.yml")
|
70
71
|
|
71
72
|
# Determine whether the test clients are connecting to a standlone.
|
72
73
|
#
|
@@ -128,21 +129,5 @@ def initialize_scanned_client!
|
|
128
129
|
Mongo::Client.new(ADDRESSES, database: TEST_DB, connect: CONNECT)
|
129
130
|
end
|
130
131
|
|
131
|
-
def initialize_mo_standalone!(path = nil)
|
132
|
-
$mongo_standalone ||= MongoOrchestration.get(:standalone, path: path)
|
133
|
-
end
|
134
|
-
|
135
|
-
def stop_mo_standalone!
|
136
|
-
$mongo_standalone.stop if $mongo_standalone
|
137
|
-
end
|
138
|
-
|
139
|
-
def mongo_orchestration_available?(path = nil)
|
140
|
-
begin
|
141
|
-
MongoOrchestration.get(:standalone, path: path)
|
142
|
-
rescue MongoOrchestration::ServiceNotAvailable
|
143
|
-
return false
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
132
|
# require all shared examples
|
148
133
|
Dir['./spec/support/shared/*.rb'].sort.each { |file| require file }
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# Copyright (C) 2014-2015 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# Matcher for determining if the results of the opeartion match the
|
16
|
+
# test's expected results.
|
17
|
+
#
|
18
|
+
# @since 2.0.0
|
19
|
+
|
20
|
+
# Matcher for determining if the collection's data matches the
|
21
|
+
# test's expected collection data.
|
22
|
+
#
|
23
|
+
# @since 2.0.0
|
24
|
+
RSpec::Matchers.define :match_collection_data do |test|
|
25
|
+
|
26
|
+
match do |actual|
|
27
|
+
test.compare_collection_data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'support/crud/read'
|
32
|
+
require 'support/crud/write'
|
33
|
+
|
34
|
+
module Mongo
|
35
|
+
module CRUD
|
36
|
+
|
37
|
+
# Represents a CRUD specification test.
|
38
|
+
#
|
39
|
+
# @since 2.0.0
|
40
|
+
class Spec
|
41
|
+
|
42
|
+
# @return [ String ] description The spec description.
|
43
|
+
#
|
44
|
+
# @since 2.0.0
|
45
|
+
attr_reader :description
|
46
|
+
|
47
|
+
# Instantiate the new spec.
|
48
|
+
#
|
49
|
+
# @example Create the spec.
|
50
|
+
# Spec.new(file)
|
51
|
+
#
|
52
|
+
# @param [ String ] file The name of the file.
|
53
|
+
#
|
54
|
+
# @since 2.0.0
|
55
|
+
def initialize(file)
|
56
|
+
@spec = YAML.load(ERB.new(File.new(file).read).result)
|
57
|
+
@description = File.basename(file)
|
58
|
+
@data = @spec['data']
|
59
|
+
@crud_tests = @spec['tests']
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get a list of CRUDTests for each test definition.
|
63
|
+
#
|
64
|
+
# @example Get the list of CRUDTests.
|
65
|
+
# spec.tests
|
66
|
+
#
|
67
|
+
# @return [ Array<CRUDTest> ] The list of CRUDTests.
|
68
|
+
#
|
69
|
+
# @since 2.0.0
|
70
|
+
def tests
|
71
|
+
@crud_tests.collect do |test|
|
72
|
+
Mongo::CRUD::CRUDTest.new(@data, test)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Represents a single CRUD test.
|
78
|
+
#
|
79
|
+
# @since 2.0.0
|
80
|
+
class CRUDTest
|
81
|
+
|
82
|
+
# The test description.
|
83
|
+
#
|
84
|
+
# @return [ String ] description The test description.
|
85
|
+
#
|
86
|
+
# @since 2.0.0
|
87
|
+
attr_reader :description
|
88
|
+
|
89
|
+
# Instantiate the new CRUDTest.
|
90
|
+
#
|
91
|
+
# @example Create the test.
|
92
|
+
# CRUDTest.new(data, test)
|
93
|
+
#
|
94
|
+
# @param [ Array<Hash> ] data The documents the collection
|
95
|
+
# must have before the test runs.
|
96
|
+
# @param [ Hash ] test The test specification.
|
97
|
+
#
|
98
|
+
# @since 2.0.0
|
99
|
+
def initialize(data, test)
|
100
|
+
@data = data
|
101
|
+
@description = test['description']
|
102
|
+
@operation = Operation.get(test['operation'])
|
103
|
+
@outcome = test['outcome']
|
104
|
+
end
|
105
|
+
|
106
|
+
# Run the test.
|
107
|
+
#
|
108
|
+
# @example Run the test.
|
109
|
+
# test.run(collection)
|
110
|
+
#
|
111
|
+
# @param [ Collection ] collection The collection the test
|
112
|
+
# should be run on.
|
113
|
+
#
|
114
|
+
# @return [ Result, Array<Hash> ] The result(s) of running the test.
|
115
|
+
#
|
116
|
+
# @since 2.0.0
|
117
|
+
def run(collection)
|
118
|
+
@collection = collection
|
119
|
+
@collection.insert_many(@data)
|
120
|
+
@operation.execute(collection)
|
121
|
+
end
|
122
|
+
|
123
|
+
# The expected result of running the test.
|
124
|
+
#
|
125
|
+
# @example Get the expected result of running the test.
|
126
|
+
# test.result
|
127
|
+
#
|
128
|
+
# @return [ Array<Hash> ] The expected result of running the test.
|
129
|
+
#
|
130
|
+
# @since 2.0.0
|
131
|
+
def result
|
132
|
+
@operation.has_results? ? @outcome['result'] : []
|
133
|
+
end
|
134
|
+
|
135
|
+
# Compare the existing collection data and the expected collection data.
|
136
|
+
#
|
137
|
+
# @example Compare the existing and expected collection data.
|
138
|
+
# test.compare_collection_data
|
139
|
+
#
|
140
|
+
# @return [ true, false ] The result of comparing the existing and expected
|
141
|
+
# collection data.
|
142
|
+
#
|
143
|
+
# @since 2.0.0
|
144
|
+
def compare_collection_data
|
145
|
+
actual_collection_data == outcome_collection_data
|
146
|
+
end
|
147
|
+
|
148
|
+
# Whether this test requires server version >= 2.6 for its results to match
|
149
|
+
# the expected results.
|
150
|
+
#
|
151
|
+
# @example If this test requires >= 2.6.
|
152
|
+
# test.requires_2_6?(collection)
|
153
|
+
#
|
154
|
+
# @param [ true, false ] If write commands are enabled on the server.
|
155
|
+
# @param [ Collection ] The collection the test is run on.
|
156
|
+
#
|
157
|
+
# @return [ true, false ] Whether the test requires server >= 2.6.
|
158
|
+
#
|
159
|
+
# @since 2.0.0
|
160
|
+
def requires_2_6?(write_command_enabled, collection)
|
161
|
+
!write_command_enabled && @operation.requires_2_6?(collection)
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def outcome_collection_data
|
167
|
+
@outcome['collection']['data'] if @outcome['collection']
|
168
|
+
end
|
169
|
+
|
170
|
+
def actual_collection_data
|
171
|
+
if @outcome['collection']
|
172
|
+
collection_name = @outcome['collection']['name'] || @collection.name
|
173
|
+
@collection.database[collection_name].find.to_a
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Helper module for instantiating either a Read or Write test operation.
|
179
|
+
#
|
180
|
+
# @since 2.0.0
|
181
|
+
module Operation
|
182
|
+
extend self
|
183
|
+
|
184
|
+
# Get a new Operation.
|
185
|
+
#
|
186
|
+
# @example Get the operation.
|
187
|
+
# Operation.get(spec)
|
188
|
+
#
|
189
|
+
# @param [ Hash ] spec The operation specification.
|
190
|
+
#
|
191
|
+
# @return [ Operation::Write, Operation::Read ] The Operation object.
|
192
|
+
#
|
193
|
+
# @since 2.0.0
|
194
|
+
def get(spec)
|
195
|
+
if Write::OPERATIONS.keys.include?(spec['name'])
|
196
|
+
Write.new(spec)
|
197
|
+
else
|
198
|
+
Read.new(spec)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# Copyright (C) 2014-2015 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
module CRUD
|
17
|
+
module Operation
|
18
|
+
|
19
|
+
# Defines common behaviour for running CRUD read operation tests
|
20
|
+
# on a collection.
|
21
|
+
#
|
22
|
+
# @since 2.0.0
|
23
|
+
class Read
|
24
|
+
|
25
|
+
# Map of method names to test operation argument names.
|
26
|
+
#
|
27
|
+
# @since 2.0.0
|
28
|
+
ARGUMENT_MAP = { :sort => 'sort',
|
29
|
+
:skip => 'skip',
|
30
|
+
:batch_size => 'batch_size',
|
31
|
+
:limit => 'limit'
|
32
|
+
}
|
33
|
+
|
34
|
+
# The operation name.
|
35
|
+
#
|
36
|
+
# @return [ String ] name The operation name.
|
37
|
+
#
|
38
|
+
# @since 2.0.0
|
39
|
+
attr_reader :name
|
40
|
+
|
41
|
+
# Instantiate the operation.
|
42
|
+
#
|
43
|
+
# @return [ Hash ] spec The operation spec.
|
44
|
+
#
|
45
|
+
# @since 2.0.0
|
46
|
+
def initialize(spec)
|
47
|
+
@spec = spec
|
48
|
+
@name = spec['name']
|
49
|
+
end
|
50
|
+
|
51
|
+
# Execute the operation.
|
52
|
+
#
|
53
|
+
# @example Execute the operation.
|
54
|
+
# operation.execute
|
55
|
+
#
|
56
|
+
# @param [ Collection ] collection The collection to execute the operation on.
|
57
|
+
#
|
58
|
+
# @return [ Result, Array<Hash> ] The result of executing the operation.
|
59
|
+
#
|
60
|
+
# @since 2.0.0
|
61
|
+
def execute(collection)
|
62
|
+
send(name.to_sym, collection)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Whether the operation is expected to have restuls.
|
66
|
+
#
|
67
|
+
# @example Whether the operation is expected to have results.
|
68
|
+
# operation.has_results?
|
69
|
+
#
|
70
|
+
# @return [ true, false ] If the operation is expected to have results.
|
71
|
+
#
|
72
|
+
# @since 2.0.0
|
73
|
+
def has_results?
|
74
|
+
!(name == 'aggregate' &&
|
75
|
+
pipeline.find {|op| op.keys.include?('$out') })
|
76
|
+
end
|
77
|
+
|
78
|
+
# Whether the operation requires server version >= 2.6 for its results to
|
79
|
+
# match expected results.
|
80
|
+
#
|
81
|
+
# @example Whether the operation requires >= 2.6 for its results to match.
|
82
|
+
# operation.requires_2_6?(collection)
|
83
|
+
#
|
84
|
+
# @param [ Collection ] collection The collection the operation is executed on.
|
85
|
+
#
|
86
|
+
# @return [ true, false ] If the operation requires 2.6 for its results to match.
|
87
|
+
#
|
88
|
+
# @since 2.0.0
|
89
|
+
def requires_2_6?(collection)
|
90
|
+
name == 'aggregate' && pipeline.find {|op| op.keys.include?('$out') }
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def count(collection)
|
96
|
+
view = collection.find(filter)
|
97
|
+
options = ARGUMENT_MAP.reduce({}) do |opts, (key, value)|
|
98
|
+
opts.merge!(key => arguments[value]) if arguments[value]
|
99
|
+
opts
|
100
|
+
end
|
101
|
+
view.count(options)
|
102
|
+
end
|
103
|
+
|
104
|
+
def aggregate(collection)
|
105
|
+
collection.find.tap do |view|
|
106
|
+
view = view.batch_size(batch_size) if batch_size
|
107
|
+
end.aggregate(pipeline).to_a
|
108
|
+
end
|
109
|
+
|
110
|
+
def distinct(collection)
|
111
|
+
collection.find(filter).distinct(field_name)
|
112
|
+
end
|
113
|
+
|
114
|
+
def find(collection)
|
115
|
+
view = collection.find(filter)
|
116
|
+
ARGUMENT_MAP.each do |key, value|
|
117
|
+
view = view.send(key, arguments[value]) if arguments[value]
|
118
|
+
end
|
119
|
+
view.to_a
|
120
|
+
end
|
121
|
+
|
122
|
+
def batch_size
|
123
|
+
arguments['batchSize']
|
124
|
+
end
|
125
|
+
|
126
|
+
def filter
|
127
|
+
arguments['filter']
|
128
|
+
end
|
129
|
+
|
130
|
+
def pipeline
|
131
|
+
arguments['pipeline']
|
132
|
+
end
|
133
|
+
|
134
|
+
def field_name
|
135
|
+
arguments['fieldName']
|
136
|
+
end
|
137
|
+
|
138
|
+
def arguments
|
139
|
+
@spec['arguments']
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|