spec/mongo/collection_spec.rb in mongo-2.5.0.beta vs spec/mongo/collection_spec.rb in mongo-2.5.0

- old
+ new

@@ -12,10 +12,14 @@ let(:collection_with_validator) do authorized_client[:validating] end + let(:client) do + authorized_client + end + describe '#==' do let(:database) do Mongo::Database.new(authorized_client, :test) end @@ -646,11 +650,10 @@ it_behaves_like 'a failed operation using a session' end end end - describe '#drop' do let(:database) do authorized_client.database end @@ -761,12 +764,16 @@ let(:operation) do client[TEST_COLL].find.first end + let(:operation_with_session) do + client[TEST_COLL].find({}, session: session).first + end + let(:second_operation) do - client[TEST_COLL].find.first + client[TEST_COLL].find({}, session: session).first end it_behaves_like 'an operation updating cluster time' end @@ -862,14 +869,10 @@ end end context 'when provided options' do - let(:view) do - authorized_collection.find({}, options) - end - context 'when a session is provided' do let(:operation) do authorized_collection.find({}, session: session).to_a end @@ -877,21 +880,73 @@ let(:session) do authorized_client.start_session end let(:failed_operation) do - authorized_collection.find({ '$._id' => 1 }, session: session).to_a + client[authorized_collection.name].find({ '$._id' => 1 }, session: session).to_a end let(:client) do authorized_client end it_behaves_like 'an operation using a session' it_behaves_like 'a failed operation using a session' end + context 'session id', if: test_sessions? do + + let(:options) do + { session: session } + end + + let(:client) do + authorized_client.with(heartbeat_frequency: 100).tap do |cl| + cl.subscribe(Mongo::Monitoring::COMMAND, subscriber) + end + end + + let(:session) do + client.start_session + end + + let(:subscriber) do + EventSubscriber.new + end + + let(:view) do + Mongo::Collection::View.new(client[TEST_COLL], selector, view_options) + end + + let(:command) do + client[TEST_COLL].find({}, session: session).explain + subscriber.started_events.find { |c| c.command_name == :explain }.command + end + + it 'sends the session id' do + expect(command['lsid']).to eq(session.session_id) + end + end + + context 'when a session supporting causal consistency is used' do + + let(:operation) do + collection.find({}, session: session).to_a + end + + let(:command) do + operation + subscriber.started_events.find { |cmd| cmd.command_name == 'find' }.command + end + + it_behaves_like 'an operation supporting causally consistent reads' + end + + let(:view) do + authorized_collection.find({}, options) + end + context 'when provided :allow_partial_results' do let(:options) do { allow_partial_results: true } end @@ -1062,10 +1117,23 @@ it_behaves_like 'an operation using a session' it_behaves_like 'a failed operation using a session' end + context 'when unacknowledged writes is used' do + + let(:collection_with_unacknowledged_write_concern) do + authorized_collection.with(write: { w: 0 }) + end + + let(:operation) do + collection_with_unacknowledged_write_concern.insert_many([{ name: 'test1' }, { name: 'test2' }], session: session) + end + + it_behaves_like 'a causally consistent client session with an unacknowledged write' + end + context 'when a document contains invalid keys' do let(:docs) do [ { 'first.name' => 'test1' }, { name: 'test2' } ] end @@ -1216,39 +1284,54 @@ expect(result3.inserted_count).to eq(2) end end end end + + context 'when unacknowledged writes is used' do + + let(:collection_with_unacknowledged_write_concern) do + authorized_collection.with(write: { w: 0 }) + end + + let(:result) do + collection_with_unacknowledged_write_concern.insert_many([{ _id: 1 }, { _id: 1 }]) + end + + it 'does not raise an exception' do + expect(result.inserted_count).to be(0) + end + end end describe '#insert_one' do describe 'updating cluster time' do let(:operation) do client[TEST_COLL].insert_one({ name: 'testing' }) end + let(:operation_with_session) do + client[TEST_COLL].insert_one({ name: 'testing' }, session: session) + end + let(:second_operation) do - client[TEST_COLL].insert_one({ name: 'testing' }) + client[TEST_COLL].insert_one({ name: 'testing' }, session: session) end it_behaves_like 'an operation updating cluster time' end let(:result) do authorized_collection.insert_one({ name: 'testing' }) end - it 'inserts the document into the collection', if: write_command_enabled? do + it 'inserts the document into the collection'do expect(result.written_count).to eq(1) end - it 'inserts the document into the collection', unless: write_command_enabled? do - expect(result.written_count).to eq(0) - end - it 'contains the id in the result' do expect(result.inserted_id).to_not be_nil end context 'when a session is provided' do @@ -1272,10 +1355,23 @@ it_behaves_like 'an operation using a session' it_behaves_like 'a failed operation using a session' end + context 'when unacknowledged writes is used' do + + let(:collection_with_unacknowledged_write_concern) do + authorized_collection.with(write: { w: 0 }) + end + + let(:operation) do + collection_with_unacknowledged_write_concern.insert_one({ name: 'testing' }, session: session) + end + + it_behaves_like 'a causally consistent client session with an unacknowledged write' + end + context 'when the document contains invalid keys' do let(:doc) do { 'testing.test' => 'value' } end @@ -1457,17 +1553,35 @@ let(:operation) do client[TEST_COLL].aggregate([]).first end + let(:operation_with_session) do + client[TEST_COLL].aggregate([], session: session).first + end + let(:second_operation) do - client[TEST_COLL].aggregate([]).first + client[TEST_COLL].aggregate([], session: session).first end it_behaves_like 'an operation updating cluster time' end + context 'when a session supporting causal consistency is used' do + + let(:operation) do + collection.aggregate([], session: session).first + end + + let(:command) do + operation + subscriber.started_events.find { |cmd| cmd.command_name == 'aggregate' }.command + end + + it_behaves_like 'an operation supporting causally consistent reads' + end + it 'returns an Aggregation object' do expect(authorized_collection.aggregate([])).to be_a(Mongo::Collection::View::Aggregation) end context 'when options are provided' do @@ -1478,10 +1592,21 @@ it 'sets the options on the Aggregation object' do expect(authorized_collection.aggregate([], options).options).to eq(BSON::Document.new(options)) end + context 'when the :comment option is provided' do + + let(:options) do + { :comment => 'testing' } + end + + it 'sets the options on the Aggregation object' do + expect(authorized_collection.aggregate([], options).options).to eq(BSON::Document.new(options)) + end + end + context 'when a session is provided' do let(:session) do authorized_client.start_session end @@ -1603,10 +1728,24 @@ it_behaves_like 'an operation using a session' it_behaves_like 'a failed operation using a session' end + context 'when a session supporting causal consistency is used' do + + let(:operation) do + collection.count({}, session: session) + end + + let(:command) do + operation + subscriber.started_events.find { |cmd| cmd.command_name == :count }.command + end + + it_behaves_like 'an operation supporting causally consistent reads' + end + context 'when a collation is specified' do let(:selector) do { name: 'BANG' } end @@ -1703,10 +1842,24 @@ it_behaves_like 'an operation using a session' it_behaves_like 'a failed operation using a session' end end + context 'when a session supporting causal consistency is used' do + + let(:operation) do + collection.distinct(:field, {}, session: session) + end + + let(:command) do + operation + subscriber.started_events.find { |cmd| cmd.command_name == :distinct }.command + end + + it_behaves_like 'an operation supporting causally consistent reads' + end + context 'when a collation is specified' do let(:result) do authorized_collection.distinct(:name, {}, options) end @@ -1760,11 +1913,11 @@ authorized_collection.insert_one(name: 'bang') authorized_collection.insert_one(name: 'BANG') end it 'does not apply the collation to the distinct' do - expect(result).to eq(['bang', 'BANG']) + expect(result).to match_array(['bang', 'BANG']) end end end describe '#delete_one' do @@ -1840,10 +1993,23 @@ it_behaves_like 'an operation using a session' it_behaves_like 'a failed operation using a session' end + context 'when unacknowledged writes is used' do + + let(:collection_with_unacknowledged_write_concern) do + authorized_collection.with(write: { w: 0 }) + end + + let(:operation) do + collection_with_unacknowledged_write_concern.delete_one({}, session: session) + end + + it_behaves_like 'a causally consistent client session with an unacknowledged write' + end + context 'when a collation is provided' do let(:selector) do { name: 'BANG' } end @@ -1999,10 +2165,23 @@ it_behaves_like 'an operation using a session' it_behaves_like 'a failed operation using a session' end + context 'when unacknowledged writes is used' do + + let(:collection_with_unacknowledged_write_concern) do + authorized_collection.with(write: { w: 0 }) + end + + let(:operation) do + collection_with_unacknowledged_write_concern.delete_many({ '$._id' => 1}, session: session) + end + + it_behaves_like 'a causally consistent client session with an unacknowledged write' + end + context 'when a collation is specified' do let(:selector) do { name: 'BANG' } end @@ -2117,38 +2296,28 @@ let(:cursors) do authorized_collection.parallel_scan(2) end - it 'returns an array of cursors', if: write_command_enabled? do + it 'returns an array of cursors' do cursors.each do |cursor| expect(cursor.class).to be(Mongo::Cursor) end end - it 'returns the correct number of documents', if: write_command_enabled? do + it 'returns the correct number of documents' do expect( cursors.reduce(0) { |total, cursor| total + cursor.to_a.size } ).to eq(200) end - it 'raises an error', unless: write_command_enabled? do - expect { - cursors - }.to raise_error(Mongo::Error::OperationFailure) - end - context 'when a session is provided' do let(:cursors) do authorized_collection.parallel_scan(2, session: session) end - let(:session) do - authorized_client.start_session - end - let(:operation) do cursors.reduce(0) { |total, cursor| total + cursor.to_a.size } end let(:failed_operation) do @@ -2161,10 +2330,28 @@ it_behaves_like 'an operation using a session' it_behaves_like 'a failed operation using a session' end + context 'when a session supporting causal consistency is used' do + + let(:cursors) do + collection.parallel_scan(2, session: session) + end + + let(:operation) do + cursors.reduce(0) { |total, cursor| total + cursor.to_a.size } + end + + let(:command) do + operation + subscriber.started_events.find { |cmd| cmd.command_name == :parallelCollectionScan }.command + end + + it_behaves_like 'an operation supporting causally consistent reads' + end + context 'when a read concern is provided', if: find_command_enabled? do let(:result) do authorized_collection.with(options).parallel_scan(2) end @@ -2222,11 +2409,11 @@ result }.to raise_exception(Mongo::Error::NoServerAvailable) end end - context 'when a max time ms value is provided', if: (!sharded? && write_command_enabled?) do + context 'when a max time ms value is provided', if: !sharded? do let(:result) do authorized_collection.parallel_scan(2, options) end @@ -2274,18 +2461,14 @@ let(:updated) do authorized_collection.find(field: 'testing').first end - it 'updates the first matching document in the collection', if: write_command_enabled? do + it 'updates the first matching document in the collection' do expect(response.modified_count).to eq(1) end - it 'does not return modified count', unless: write_command_enabled? do - expect(response.modified_count).to eq(nil) - end - it 'updates the documents in the collection' do expect(updated[:field]).to eq('testing') end end @@ -2297,18 +2480,14 @@ let(:updated) do authorized_collection.find(field: 'test1').to_a end - it 'reports that no documents were written', if: write_command_enabled? do + it 'reports that no documents were written' do expect(response.modified_count).to eq(0) end - it 'does not return modified count', unless: write_command_enabled? do - expect(response.modified_count).to eq(nil) - end - it 'does not insert the document' do expect(updated).to be_empty end end @@ -2339,18 +2518,14 @@ let(:updated) do authorized_collection.find(field: 'test1').to_a end - it 'reports that no documents were written', if: write_command_enabled? do + it 'reports that no documents were written' do expect(response.modified_count).to eq(0) end - it 'does not return modified count', unless: write_command_enabled? do - expect(response.modified_count).to eq(nil) - end - it 'does not insert the document' do expect(updated).to be_empty end end @@ -2548,10 +2723,23 @@ end it_behaves_like 'an operation using a session' it_behaves_like 'a failed operation using a session' end + + context 'when unacknowledged writes is used' do + + let(:collection_with_unacknowledged_write_concern) do + authorized_collection.with(write: { w: 0 }) + end + + let(:operation) do + collection_with_unacknowledged_write_concern.replace_one({ a: 1 }, { x: 5 }, session: session) + end + + it_behaves_like 'a causally consistent client session with an unacknowledged write' + end end describe '#update_many' do let(:selector) do @@ -2570,18 +2758,14 @@ let(:updated) do authorized_collection.find(field: 'testing').to_a.last end - it 'returns the number updated', if: write_command_enabled? do + it 'returns the number updated' do expect(response.modified_count).to eq(2) end - it 'does not return modified count', unless: write_command_enabled? do - expect(response.modified_count).to eq(nil) - end - it 'updates the documents in the collection' do expect(updated[:field]).to eq('testing') end end @@ -2594,18 +2778,14 @@ let(:updated) do authorized_collection.find.to_a end - it 'reports that no documents were updated', if: write_command_enabled? do + it 'reports that no documents were updated' do expect(response.modified_count).to eq(0) end - it 'does not return modified count', unless: write_command_enabled? do - expect(response.modified_count).to eq(nil) - end - it 'updates no documents in the collection' do expect(updated).to be_empty end end @@ -2637,18 +2817,14 @@ let(:updated) do authorized_collection.find.to_a end - it 'reports that no documents were updated', if: write_command_enabled? do + it 'reports that no documents were updated' do expect(response.modified_count).to eq(0) end - it 'does not return modified count', unless: write_command_enabled? do - expect(response.modified_count).to eq(nil) - end - it 'updates no documents in the collection' do expect(updated).to be_empty end end @@ -2951,10 +3127,23 @@ end it_behaves_like 'an operation using a session' it_behaves_like 'a failed operation using a session' end + + context 'when unacknowledged writes is used' do + + let(:collection_with_unacknowledged_write_concern) do + authorized_collection.with(write: { w: 0 }) + end + + let(:operation) do + collection_with_unacknowledged_write_concern.update_many({a: 1}, { '$set' => {x: 1} }, session: session) + end + + it_behaves_like 'a causally consistent client session with an unacknowledged write' + end end describe '#update_one' do let(:selector) do @@ -2973,18 +3162,14 @@ let(:updated) do authorized_collection.find(field: 'testing').first end - it 'updates the first matching document in the collection', if: write_command_enabled? do + it 'updates the first matching document in the collection' do expect(response.modified_count).to eq(1) end - it 'does not return modified count', unless: write_command_enabled? do - expect(response.modified_count).to eq(nil) - end - it 'updates the documents in the collection' do expect(updated[:field]).to eq('testing') end end @@ -2997,18 +3182,14 @@ let(:updated) do authorized_collection.find.to_a end - it 'reports that no documents were updated', if: write_command_enabled? do + it 'reports that no documents were updated' do expect(response.modified_count).to eq(0) end - it 'does not return modified count', unless: write_command_enabled? do - expect(response.modified_count).to eq(nil) - end - it 'updates no documents in the collection' do expect(updated).to be_empty end end @@ -3040,18 +3221,14 @@ let(:updated) do authorized_collection.find.to_a end - it 'reports that no documents were updated', if: write_command_enabled? do + it 'reports that no documents were updated' do expect(response.modified_count).to eq(0) end - it 'does not return modified count', unless: write_command_enabled? do - expect(response.modified_count).to eq(nil) - end - it 'updates no documents in the collection' do expect(updated).to be_empty end end @@ -3363,10 +3540,23 @@ end it_behaves_like 'an operation using a session' it_behaves_like 'a failed operation using a session' end + + context 'when unacknowledged writes is used' do + + let(:collection_with_unacknowledged_write_concern) do + authorized_collection.with(write: { w: 0 }) + end + + let(:operation) do + collection_with_unacknowledged_write_concern.update_one({a: 1}, { '$set' => {x: 1} }, session: session) + end + + it_behaves_like 'a causally consistent client session with an unacknowledged write' + end end describe '#find_one_and_delete' do before do @@ -3445,11 +3635,11 @@ it 'returns the document with limited fields' do expect(document['field']).to eq('test1') end end - context 'when max_time_ms is provided', if: write_command_enabled? do + context 'when max_time_ms is provided' do it 'includes the max_time_ms value in the command' do expect { authorized_collection.find_one_and_delete(selector, max_time_ms: 0.1) }.to raise_error(Mongo::Error::OperationFailure) @@ -3470,11 +3660,11 @@ it 'returns nil' do expect(document).to be_nil end end - context 'when the operation fails', if: write_command_enabled? do + context 'when the operation fails' do let(:result) do authorized_collection.find_one_and_delete(selector, max_time_ms: 0.1) end @@ -3683,11 +3873,11 @@ end end context 'when max_time_ms is provided' do - it 'includes the max_time_ms value in the command', if: write_command_enabled? do + it 'includes the max_time_ms value in the command' do expect { authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }}, max_time_ms: 0.1) }.to raise_error(Mongo::Error::OperationFailure) end end @@ -3738,11 +3928,11 @@ expect(document['field']).to eq('testing') end end end - context 'when the operation fails', if: write_command_enabled? do + context 'when the operation fails' do let(:result) do authorized_collection.find_one_and_update(selector, { '$set' => { field: 'testing' }}, max_time_ms: 0.1) end @@ -4114,20 +4304,20 @@ expect(document['field']).to eq('testing') end end end - context 'when max_time_ms is provided', if: write_command_enabled? do + context 'when max_time_ms is provided' do it 'includes the max_time_ms value in the command' do expect { authorized_collection.find_one_and_replace(selector, { field: 'testing' }, max_time_ms: 0.1) }.to raise_error(Mongo::Error::OperationFailure) end end - context 'when the operation fails', if: write_command_enabled? do + context 'when the operation fails' do let(:result) do authorized_collection.find_one_and_replace(selector, { field: 'testing' }, max_time_ms: 0.1) end @@ -4362,45 +4552,45 @@ end context 'when batch_size is provided' do before do - authorized_collection.insert_one(a: 2) - authorized_collection.insert_one(a: 3) + Thread.new do + sleep 1 + authorized_collection.insert_one(a: 2) + authorized_collection.insert_one(a: 3) + end end let(:change_stream) do authorized_collection.watch([], batch_size: 2) end it 'returns the documents in the batch size specified' do expect(change_stream.instance_variable_get(:@cursor)).to receive(:get_more).once.and_call_original enum.next - enum.next end end context 'when collation is provided' do - # pending 'server support for collation with the $changeStream operator' - # - # before do - # authorized_collection.update_one({ a: 1 }, { '$set' => { a: 2 } }) - # end - # - # let(:change_doc) do - # change_stream.next - # change_stream.next - # end - # - # let(:change_stream) do - # authorized_collection.watch([ { '$match' => { operationType: 'UPDATE'}}], - # collation: { locale: 'en_US', strength: 2 } ).to_enum - # end - # - # it 'returns the change' do - # expect(change_doc[:operationType]).to eq('update') - # expect(change_doc[:fullDocument][:a]).to eq(2) - # end + + before do + authorized_collection.update_one({ a: 1 }, { '$set' => { a: 2 } }) + end + + let(:change_doc) do + enum.next + end + + let(:change_stream) do + authorized_collection.watch([ { '$match' => { operationType: 'UPDATE'}}], + collation: { locale: 'en_US', strength: 2 } ).to_enum + end + + it 'returns the change' do + expect(change_doc['operationType']).to eq('update') + expect(change_doc['updateDescription']['updatedFields']['a']).to eq(2) + end end end end end end