mongo 2.0.0.rc → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -1
  4. data/README.md +1 -4
  5. data/lib/mongo/client.rb +1 -1
  6. data/lib/mongo/collection.rb +1 -1
  7. data/lib/mongo/collection/view.rb +1 -1
  8. data/lib/mongo/collection/view/writable.rb +26 -10
  9. data/lib/mongo/database.rb +1 -1
  10. data/lib/mongo/grid/file.rb +12 -0
  11. data/lib/mongo/grid/file/chunk.rb +1 -1
  12. data/lib/mongo/grid/file/metadata.rb +13 -0
  13. data/lib/mongo/operation/aggregate.rb +1 -1
  14. data/lib/mongo/operation/write.rb +1 -5
  15. data/lib/mongo/operation/write/bulk.rb +17 -0
  16. data/lib/mongo/operation/write/{bulk_delete.rb → bulk/bulk_delete.rb} +1 -1
  17. data/lib/mongo/operation/{bulk_delete → write/bulk/bulk_delete}/result.rb +3 -0
  18. data/lib/mongo/operation/write/{bulk_insert.rb → bulk/bulk_insert.rb} +1 -1
  19. data/lib/mongo/operation/{bulk_insert → write/bulk/bulk_insert}/result.rb +3 -0
  20. data/lib/mongo/operation/write/{bulk_mergable.rb → bulk/bulk_mergable.rb} +0 -0
  21. data/lib/mongo/operation/write/{bulk_update.rb → bulk/bulk_update.rb} +1 -1
  22. data/lib/mongo/operation/{bulk_update → write/bulk/bulk_update}/result.rb +8 -1
  23. data/lib/mongo/operation/write/{legacy_bulk_mergable.rb → bulk/legacy_bulk_mergable.rb} +0 -0
  24. data/lib/mongo/operation/write/delete.rb +2 -0
  25. data/lib/mongo/operation/write/delete/result.rb +40 -0
  26. data/lib/mongo/operation/write/insert.rb +2 -0
  27. data/lib/mongo/operation/write/insert/result.rb +32 -0
  28. data/lib/mongo/operation/write/update.rb +9 -4
  29. data/lib/mongo/operation/write/update/result.rb +160 -0
  30. data/lib/mongo/server/connectable.rb +11 -0
  31. data/lib/mongo/server/connection.rb +1 -0
  32. data/lib/mongo/server/connection_pool.rb +1 -1
  33. data/lib/mongo/server/connection_pool/queue.rb +1 -1
  34. data/lib/mongo/server/monitor.rb +16 -0
  35. data/lib/mongo/server/monitor/connection.rb +1 -0
  36. data/lib/mongo/socket/ssl.rb +30 -8
  37. data/lib/mongo/version.rb +1 -1
  38. data/lib/mongo/write_concern/acknowledged.rb +1 -1
  39. data/lib/mongo/write_concern/unacknowledged.rb +1 -1
  40. data/spec/certificates/ca.pem +17 -0
  41. data/spec/certificates/client.pem +101 -0
  42. data/spec/certificates/crl.pem +10 -0
  43. data/spec/certificates/crl_client_revoked.pem +12 -0
  44. data/spec/certificates/password_protected.pem +51 -0
  45. data/spec/certificates/server.pem +34 -0
  46. data/spec/mongo/collection/view/writable_spec.rb +175 -0
  47. data/spec/mongo/crud_spec.rb +42 -0
  48. data/spec/mongo/grid/file/metadata_spec.rb +23 -0
  49. data/spec/mongo/grid/file_spec.rb +34 -0
  50. data/spec/mongo/operation/write/delete_spec.rb +8 -0
  51. data/spec/mongo/operation/write/insert_spec.rb +21 -8
  52. data/spec/mongo/operation/write/update_spec.rb +52 -1
  53. data/spec/mongo/server/connection_spec.rb +33 -10
  54. data/spec/mongo/server/monitor_spec.rb +14 -0
  55. data/spec/spec_helper.rb +2 -17
  56. data/spec/support/crud.rb +203 -0
  57. data/spec/support/crud/read.rb +144 -0
  58. data/spec/support/crud/write.rb +214 -0
  59. data/spec/support/crud_tests/read/aggregate.yml +43 -0
  60. data/spec/support/crud_tests/read/count.yml +37 -0
  61. data/spec/support/crud_tests/read/distinct.yml +33 -0
  62. data/spec/support/crud_tests/read/find.yml +50 -0
  63. data/spec/support/crud_tests/write/deleteMany.yml +36 -0
  64. data/spec/support/crud_tests/write/deleteOne.yml +49 -0
  65. data/spec/support/crud_tests/write/findOneAndDelete.yml +54 -0
  66. data/spec/support/crud_tests/write/findOneAndReplace.yml +153 -0
  67. data/spec/support/crud_tests/write/findOneAndUpdate.yml +161 -0
  68. data/spec/support/crud_tests/write/insertMany.yml +24 -0
  69. data/spec/support/crud_tests/write/insertOne.yml +19 -0
  70. data/spec/support/crud_tests/write/replaceOne.yml +96 -0
  71. data/spec/support/crud_tests/write/updateMany.yml +83 -0
  72. data/spec/support/crud_tests/write/updateOne.yml +80 -0
  73. metadata +64 -20
  74. metadata.gz.sig +0 -0
  75. data/spec/mongo_orchestration_spec.rb +0 -70
  76. data/spec/support/mongo_orchestration.rb +0 -61
  77. data/spec/support/mongo_orchestration/requestable.rb +0 -109
  78. 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 passes' do
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
- connection.dispatch([ delete ])
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
- connection.dispatch([ delete ])
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, :ssl => true)
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(:ssl => true)
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
@@ -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