mongo 2.4.3 → 2.5.0.beta
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 +3 -2
- data.tar.gz.sig +0 -0
- data/lib/mongo.rb +3 -2
- data/lib/mongo/auth/cr.rb +6 -4
- data/lib/mongo/auth/cr/conversation.rb +33 -17
- data/lib/mongo/auth/ldap.rb +4 -2
- data/lib/mongo/auth/ldap/conversation.rb +19 -9
- data/lib/mongo/auth/scram.rb +7 -4
- data/lib/mongo/auth/scram/conversation.rb +62 -24
- data/lib/mongo/auth/user.rb +10 -0
- data/lib/mongo/auth/user/view.rb +44 -22
- data/lib/mongo/auth/x509.rb +4 -2
- data/lib/mongo/auth/x509/conversation.rb +19 -9
- data/lib/mongo/bulk_write.rb +33 -27
- data/lib/mongo/bulk_write/combineable.rb +5 -0
- data/lib/mongo/bulk_write/transformable.rb +2 -0
- data/lib/mongo/bulk_write/validatable.rb +4 -0
- data/lib/mongo/client.rb +123 -12
- data/lib/mongo/cluster.rb +52 -11
- data/lib/mongo/cluster/app_metadata.rb +8 -2
- data/lib/mongo/cluster/cursor_reaper.rb +0 -1
- data/lib/mongo/cluster/topology.rb +1 -1
- data/lib/mongo/collection.rb +114 -27
- data/lib/mongo/collection/view.rb +8 -2
- data/lib/mongo/collection/view/aggregation.rb +11 -7
- data/lib/mongo/collection/view/builder/aggregation.rb +5 -1
- data/lib/mongo/collection/view/builder/find_command.rb +5 -3
- data/lib/mongo/collection/view/builder/map_reduce.rb +11 -3
- data/lib/mongo/collection/view/builder/op_query.rb +1 -1
- data/lib/mongo/collection/view/change_stream.rb +160 -0
- data/lib/mongo/collection/view/change_stream/retryable.rb +57 -0
- data/lib/mongo/collection/view/iterable.rb +11 -10
- data/lib/mongo/collection/view/map_reduce.rb +22 -18
- data/lib/mongo/collection/view/readable.rb +51 -37
- data/lib/mongo/collection/view/writable.rb +72 -40
- data/lib/mongo/cursor.rb +25 -4
- data/lib/mongo/cursor/builder/get_more_command.rb +4 -2
- data/lib/mongo/database.rb +22 -11
- data/lib/mongo/database/view.rb +16 -12
- data/lib/mongo/error.rb +5 -0
- data/lib/mongo/error/invalid_session.rb +36 -0
- data/lib/mongo/error/missing_resume_token.rb +39 -0
- data/lib/mongo/error/operation_failure.rb +17 -0
- data/lib/mongo/error/parser.rb +3 -2
- data/lib/mongo/error/unknown_payload_type.rb +41 -0
- data/lib/mongo/error/unsupported_array_filters.rb +51 -0
- data/lib/mongo/error/unsupported_message_type.rb +23 -0
- data/lib/mongo/grid/fs_bucket.rb +5 -4
- data/lib/mongo/grid/stream/read.rb +3 -2
- data/lib/mongo/grid/stream/write.rb +2 -2
- data/lib/mongo/index/view.rb +35 -25
- data/lib/mongo/monitoring/event/secure.rb +14 -0
- data/lib/mongo/operation.rb +16 -0
- data/lib/mongo/operation/commands.rb +1 -0
- data/lib/mongo/operation/commands/aggregate.rb +9 -5
- data/lib/mongo/operation/commands/aggregate/result.rb +1 -1
- data/lib/mongo/operation/commands/collections_info.rb +6 -6
- data/lib/mongo/operation/commands/command.rb +2 -1
- data/lib/mongo/operation/commands/create.rb +6 -2
- data/lib/mongo/operation/commands/drop.rb +6 -2
- data/lib/mongo/operation/commands/drop_database.rb +6 -2
- data/lib/mongo/operation/commands/explain.rb +27 -0
- data/lib/mongo/operation/commands/explain/result.rb +52 -0
- data/lib/mongo/operation/commands/indexes.rb +1 -1
- data/lib/mongo/operation/commands/list_collections.rb +1 -1
- data/lib/mongo/operation/commands/list_collections/result.rb +1 -1
- data/lib/mongo/operation/commands/list_indexes.rb +1 -1
- data/lib/mongo/operation/commands/list_indexes/result.rb +1 -1
- data/lib/mongo/operation/commands/map_reduce.rb +8 -4
- data/lib/mongo/operation/commands/map_reduce/result.rb +13 -1
- data/lib/mongo/operation/commands/user_query.rb +1 -1
- data/lib/mongo/operation/commands/users_info.rb +6 -2
- data/lib/mongo/operation/executable.rb +4 -1
- data/lib/mongo/operation/read_preference.rb +10 -5
- data/lib/mongo/operation/result.rb +26 -2
- data/lib/mongo/operation/specifiable.rb +13 -1
- data/lib/mongo/operation/uses_command_op_msg.rb +47 -0
- data/lib/mongo/operation/write/bulk/bulkable.rb +4 -1
- data/lib/mongo/operation/write/bulk/insert/result.rb +4 -4
- data/lib/mongo/operation/write/command/create_index.rb +6 -1
- data/lib/mongo/operation/write/command/delete.rb +28 -4
- data/lib/mongo/operation/write/command/drop_index.rb +6 -1
- data/lib/mongo/operation/write/command/insert.rb +22 -18
- data/lib/mongo/operation/write/command/update.rb +24 -9
- data/lib/mongo/operation/write/command/writable.rb +14 -1
- data/lib/mongo/operation/write/insert.rb +4 -1
- data/lib/mongo/operation/write/insert/result.rb +2 -2
- data/lib/mongo/operation/write/update.rb +7 -1
- data/lib/mongo/operation/write/write_command_enabled.rb +20 -3
- data/lib/mongo/protocol.rb +3 -0
- data/lib/mongo/protocol/bit_vector.rb +2 -2
- data/lib/mongo/protocol/compressed.rb +135 -0
- data/lib/mongo/protocol/delete.rb +8 -6
- data/lib/mongo/protocol/get_more.rb +8 -6
- data/lib/mongo/protocol/insert.rb +8 -6
- data/lib/mongo/protocol/kill_cursors.rb +8 -6
- data/lib/mongo/protocol/message.rb +31 -3
- data/lib/mongo/protocol/msg.rb +172 -0
- data/lib/mongo/protocol/query.rb +26 -6
- data/lib/mongo/protocol/registry.rb +76 -0
- data/lib/mongo/protocol/reply.rb +10 -5
- data/lib/mongo/protocol/serializers.rb +224 -0
- data/lib/mongo/protocol/update.rb +8 -6
- data/lib/mongo/retryable.rb +4 -2
- data/lib/mongo/server.rb +6 -3
- data/lib/mongo/server/connectable.rb +1 -1
- data/lib/mongo/server/connection.rb +30 -8
- data/lib/mongo/server/description.rb +25 -1
- data/lib/mongo/server/description/features.rb +4 -1
- data/lib/mongo/server/monitor.rb +5 -0
- data/lib/mongo/server/monitor/connection.rb +50 -2
- data/lib/mongo/server_selector/nearest.rb +10 -4
- data/lib/mongo/server_selector/primary.rb +20 -0
- data/lib/mongo/server_selector/primary_preferred.rb +10 -4
- data/lib/mongo/server_selector/secondary.rb +10 -4
- data/lib/mongo/server_selector/secondary_preferred.rb +24 -4
- data/lib/mongo/session.rb +180 -0
- data/lib/mongo/session/server_session.rb +73 -0
- data/lib/mongo/session/session_pool.rb +161 -0
- data/lib/mongo/uri.rb +11 -0
- data/lib/mongo/version.rb +1 -1
- data/mongo.gemspec +2 -1
- data/spec/mongo/auth/cr_spec.rb +12 -0
- data/spec/mongo/auth/ldap_spec.rb +2 -0
- data/spec/mongo/auth/scram/conversation_spec.rb +6 -6
- data/spec/mongo/auth/scram_spec.rb +25 -1
- data/spec/mongo/auth/user/view_spec.rb +268 -76
- data/spec/mongo/auth/x509_spec.rb +2 -0
- data/spec/mongo/bulk_write_spec.rb +435 -5
- data/spec/mongo/client_spec.rb +356 -39
- data/spec/mongo/cluster/app_metadata_spec.rb +2 -2
- data/spec/mongo/cluster_spec.rb +176 -0
- data/spec/mongo/collection/view/aggregation_spec.rb +33 -12
- data/spec/mongo/collection/view/builder/find_command_spec.rb +46 -6
- data/spec/mongo/collection/view/change_stream_spec.rb +814 -0
- data/spec/mongo/collection/view/map_reduce_spec.rb +94 -17
- data/spec/mongo/collection/view/readable_spec.rb +3 -12
- data/spec/mongo/collection_spec.rb +1048 -42
- data/spec/mongo/cursor/builder/get_more_command_spec.rb +19 -0
- data/spec/mongo/cursor_spec.rb +2 -2
- data/spec/mongo/database_spec.rb +50 -1
- data/spec/mongo/grid/fs_bucket_spec.rb +225 -137
- data/spec/mongo/grid/stream/read_spec.rb +2 -2
- data/spec/mongo/index/view_spec.rb +146 -8
- data/spec/mongo/monitoring/event/secure_spec.rb +42 -0
- data/spec/mongo/operation/read/query_spec.rb +2 -1
- data/spec/mongo/operation/specifiable_spec.rb +2 -2
- data/spec/mongo/operation/write/command/delete_spec.rb +96 -13
- data/spec/mongo/operation/write/command/insert_spec.rb +111 -12
- data/spec/mongo/operation/write/command/update_spec.rb +93 -10
- data/spec/mongo/operation/write/delete_spec.rb +1 -1
- data/spec/mongo/operation/write/insert_spec.rb +1 -1
- data/spec/mongo/operation/write/update_spec.rb +1 -1
- data/spec/mongo/protocol/compressed_spec.rb +66 -0
- data/spec/mongo/protocol/delete_spec.rb +14 -0
- data/spec/mongo/protocol/get_more_spec.rb +14 -0
- data/spec/mongo/protocol/insert_spec.rb +14 -0
- data/spec/mongo/protocol/kill_cursors_spec.rb +14 -0
- data/spec/mongo/protocol/msg_spec.rb +499 -0
- data/spec/mongo/protocol/query_spec.rb +45 -0
- data/spec/mongo/protocol/registry_spec.rb +31 -0
- data/spec/mongo/protocol/reply_spec.rb +14 -0
- data/spec/mongo/protocol/update_spec.rb +14 -0
- data/spec/mongo/retryable_spec.rb +6 -2
- data/spec/mongo/sdam_spec.rb +4 -0
- data/spec/mongo/server/connection_spec.rb +4 -2
- data/spec/mongo/server/description_spec.rb +28 -1
- data/spec/mongo/session/server_session_spec.rb +16 -0
- data/spec/mongo/session/session_pool_spec.rb +194 -0
- data/spec/mongo/uri_spec.rb +31 -2
- data/spec/spec_helper.rb +104 -0
- data/spec/support/authorization.rb +6 -1
- data/spec/support/crud.rb +3 -1
- data/spec/support/crud/write.rb +6 -1
- data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +69 -0
- data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +63 -0
- data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +109 -0
- data/spec/support/sdam/rs/discover_arbiters.yml +1 -1
- data/spec/support/sdam/rs/discover_passives.yml +2 -2
- data/spec/support/sdam/rs/discover_primary.yml +1 -1
- data/spec/support/sdam/rs/discover_secondary.yml +1 -1
- data/spec/support/sdam/rs/discovery.yml +4 -4
- data/spec/support/sdam/rs/equal_electionids.yml +1 -0
- data/spec/support/sdam/rs/ghost_discovered.yml +1 -1
- data/spec/support/sdam/rs/hosts_differ_from_seeds.yml +1 -1
- data/spec/support/sdam/rs/ls_timeout.yml +88 -0
- data/spec/support/sdam/rs/member_reconfig.yml +2 -2
- data/spec/support/sdam/rs/member_standalone.yml +2 -2
- data/spec/support/sdam/rs/new_primary.yml +2 -2
- data/spec/support/sdam/rs/new_primary_new_electionid.yml +3 -0
- data/spec/support/sdam/rs/new_primary_new_setversion.yml +3 -0
- data/spec/support/sdam/rs/new_primary_wrong_set_name.yml +2 -2
- data/spec/support/sdam/rs/non_rs_member.yml +1 -1
- data/spec/support/sdam/rs/normalize_case.yml +1 -1
- data/spec/support/sdam/rs/null_election_id.yml +4 -0
- data/spec/support/sdam/rs/primary_becomes_standalone.yml +2 -2
- data/spec/support/sdam/rs/primary_changes_set_name.yml +2 -2
- data/spec/support/sdam/rs/primary_disconnect.yml +2 -2
- data/spec/support/sdam/rs/primary_disconnect_electionid.yml +5 -0
- data/spec/support/sdam/rs/primary_disconnect_setversion.yml +5 -0
- data/spec/support/sdam/rs/primary_hint_from_secondary_with_mismatched_me.yml +58 -0
- data/spec/support/sdam/rs/primary_reports_new_member.yml +4 -4
- data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +2 -2
- data/spec/support/sdam/rs/primary_wrong_set_name.yml +1 -1
- data/spec/support/sdam/rs/response_from_removed.yml +2 -2
- data/spec/support/sdam/rs/rsother_discovered.yml +1 -1
- data/spec/support/sdam/rs/sec_not_auth.yml +1 -1
- data/spec/support/sdam/rs/secondary_wrong_set_name.yml +1 -1
- data/spec/support/sdam/rs/secondary_wrong_set_name_with_primary.yml +2 -2
- data/spec/support/sdam/rs/setversion_without_electionid.yml +2 -0
- data/spec/support/sdam/rs/stepdown_change_set_name.yml +2 -2
- data/spec/support/sdam/rs/unexpected_mongos.yml +1 -1
- data/spec/support/sdam/rs/use_setversion_without_electionid.yml +3 -0
- data/spec/support/sdam/rs/wrong_set_name.yml +1 -1
- data/spec/support/sdam/sharded/ls_timeout_mongos.yml +97 -0
- data/spec/support/sdam/sharded/mongos_disconnect.yml +3 -3
- data/spec/support/sdam/sharded/multiple_mongoses.yml +1 -1
- data/spec/support/sdam/sharded/non_mongos_removed.yml +1 -1
- data/spec/support/sdam/sharded/normalize_uri_case.yml +1 -1
- data/spec/support/sdam/single/direct_connection_external_ip.yml +1 -1
- data/spec/support/sdam/single/direct_connection_mongos.yml +1 -1
- data/spec/support/sdam/single/direct_connection_rsarbiter.yml +1 -1
- data/spec/support/sdam/single/direct_connection_rsprimary.yml +1 -1
- data/spec/support/sdam/single/direct_connection_rssecondary.yml +1 -1
- data/spec/support/sdam/single/direct_connection_slave.yml +1 -1
- data/spec/support/sdam/single/direct_connection_standalone.yml +1 -1
- data/spec/support/sdam/single/ls_timeout_standalone.yml +35 -0
- data/spec/support/sdam/single/not_ok_response.yml +1 -1
- data/spec/support/sdam/single/standalone_removed.yml +1 -1
- data/spec/support/sdam/single/unavailable_seed.yml +1 -1
- data/spec/support/server_discovery_and_monitoring.rb +4 -0
- data/spec/support/shared/session.rb +236 -0
- metadata +53 -15
- metadata.gz.sig +0 -0
@@ -93,18 +93,101 @@ describe Mongo::Operation::Write::Command::Update do
|
|
93
93
|
|
94
94
|
describe '#message' do
|
95
95
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
96
|
+
context 'when the server supports OP_MSG', if: op_msg_enabled? do
|
97
|
+
|
98
|
+
let(:global_args) do
|
99
|
+
{
|
100
|
+
update: TEST_COLL,
|
101
|
+
ordered: true,
|
102
|
+
writeConcern: write_concern.options,
|
103
|
+
'$db' => TEST_DB
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
let(:expected_payload_1) do
|
108
|
+
{
|
109
|
+
type: 1,
|
110
|
+
payload: { identifier: 'updates',
|
111
|
+
sequence: updates
|
112
|
+
}
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when the topology is sharded', if: sharded? && op_msg_enabled? do
|
117
|
+
|
118
|
+
let(:expected_global_args) do
|
119
|
+
global_args.merge(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time)
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'creates the correct OP_MSG message' do
|
123
|
+
authorized_client.command(ping:1)
|
124
|
+
expect(Mongo::Protocol::Msg).to receive(:new).with([:none], {}, expected_global_args, expected_payload_1)
|
125
|
+
op.send(:message, authorized_primary)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'when the topology is not sharded', if: !sharded? && op_msg_enabled? do
|
130
|
+
|
131
|
+
let(:expected_global_args) do
|
132
|
+
global_args
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'creates the correct OP_MSG message' do
|
136
|
+
authorized_client.command(ping:1)
|
137
|
+
expect(Mongo::Protocol::Msg).to receive(:new).with([:none], {}, expected_global_args, expected_payload_1)
|
138
|
+
op.send(:message, authorized_primary)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'when the write concern is 0' do
|
143
|
+
|
144
|
+
let(:write_concern) do
|
145
|
+
Mongo::WriteConcern.get(w: 0)
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'when the topology is sharded', if: sharded? && op_msg_enabled? do
|
149
|
+
|
150
|
+
let(:expected_global_args) do
|
151
|
+
global_args.merge(Mongo::Operation::CLUSTER_TIME => authorized_client.cluster.cluster_time)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'creates the correct OP_MSG message' do
|
155
|
+
authorized_client.command(ping:1)
|
156
|
+
expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], {}, expected_global_args, expected_payload_1)
|
157
|
+
op.send(:message, authorized_primary)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'when the topology is not sharded', if: !sharded? && op_msg_enabled? do
|
162
|
+
|
163
|
+
let(:expected_global_args) do
|
164
|
+
global_args
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'creates the correct OP_MSG message' do
|
168
|
+
authorized_client.command(ping:1)
|
169
|
+
expect(Mongo::Protocol::Msg).to receive(:new).with([:more_to_come], {}, expected_global_args, expected_payload_1)
|
170
|
+
op.send(:message, authorized_primary)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
103
174
|
end
|
104
175
|
|
105
|
-
|
106
|
-
|
107
|
-
|
176
|
+
context 'when the server does not support OP_MSG' do
|
177
|
+
|
178
|
+
let(:expected_selector) do
|
179
|
+
{
|
180
|
+
:update => TEST_COLL,
|
181
|
+
:updates => updates,
|
182
|
+
:ordered => true,
|
183
|
+
:writeConcern => write_concern.options
|
184
|
+
}
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'creates the correct Command message', unless: op_msg_enabled? do
|
188
|
+
expect(Mongo::Protocol::Query).to receive(:new).with(TEST_DB, '$cmd', expected_selector, { limit: -1 })
|
189
|
+
op.send(:message, authorized_primary)
|
190
|
+
end
|
108
191
|
end
|
109
192
|
end
|
110
193
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mongo::Protocol::Compressed do
|
4
|
+
|
5
|
+
let(:original_message) { Mongo::Protocol::Query.new(TEST_DB, TEST_COLL, { ping: 1 }) }
|
6
|
+
let(:compressor) { 'zlib' }
|
7
|
+
let(:level) { nil }
|
8
|
+
|
9
|
+
let(:message) do
|
10
|
+
described_class.new(original_message, compressor, level)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#serialize' do
|
14
|
+
|
15
|
+
context 'when zlib compression level is not provided' do
|
16
|
+
|
17
|
+
let(:original_message_bytes) do
|
18
|
+
buf = BSON::ByteBuffer.new
|
19
|
+
original_message.send(:serialize_fields, buf)
|
20
|
+
buf.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'does not set a compression level' do
|
24
|
+
expect(Zlib::Deflate).to receive(:deflate).with(original_message_bytes, nil).and_call_original
|
25
|
+
message.serialize
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when zlib compression level is provided' do
|
30
|
+
|
31
|
+
let(:level) { 1 }
|
32
|
+
|
33
|
+
let(:original_message_bytes) do
|
34
|
+
buf = BSON::ByteBuffer.new
|
35
|
+
original_message.send(:serialize_fields, buf)
|
36
|
+
buf.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'uses the compression level' do
|
40
|
+
expect(Zlib::Deflate).to receive(:deflate).with(original_message_bytes, 1).and_call_original
|
41
|
+
message.serialize
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#replyable?' do
|
47
|
+
|
48
|
+
context 'when the original message is replyable' do
|
49
|
+
|
50
|
+
it 'returns true' do
|
51
|
+
expect(message.replyable?).to be(true)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when the original message is not replyable', if: op_msg_enabled? do
|
56
|
+
|
57
|
+
let(:original_message) do
|
58
|
+
Mongo::Protocol::Msg.new([:more_to_come], {}, { ping: 1 })
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'returns false' do
|
62
|
+
expect(message.replyable?).to be(false)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -164,4 +164,18 @@ describe Mongo::Protocol::Delete do
|
|
164
164
|
end
|
165
165
|
end
|
166
166
|
end
|
167
|
+
|
168
|
+
describe '#registry' do
|
169
|
+
|
170
|
+
context 'when the class is loaded' do
|
171
|
+
|
172
|
+
it 'registers the op code in the Protocol Registry' do
|
173
|
+
expect(Mongo::Protocol::Registry.get(described_class::OP_CODE)).to be(described_class)
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'creates an #op_code instance method' do
|
177
|
+
expect(message.op_code).to eq(described_class::OP_CODE)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
167
181
|
end
|
@@ -143,4 +143,18 @@ describe Mongo::Protocol::GetMore do
|
|
143
143
|
end
|
144
144
|
end
|
145
145
|
end
|
146
|
+
|
147
|
+
describe '#registry' do
|
148
|
+
|
149
|
+
context 'when the class is loaded' do
|
150
|
+
|
151
|
+
it 'registers the op code in the Protocol Registry' do
|
152
|
+
expect(Mongo::Protocol::Registry.get(described_class::OP_CODE)).to be(described_class)
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'creates an #op_code instance method' do
|
156
|
+
expect(message.op_code).to eq(described_class::OP_CODE)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
146
160
|
end
|
@@ -158,4 +158,18 @@ describe Mongo::Protocol::Insert do
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
end
|
161
|
+
|
162
|
+
describe '#registry' do
|
163
|
+
|
164
|
+
context 'when the class is loaded' do
|
165
|
+
|
166
|
+
it 'registers the op code in the Protocol Registry' do
|
167
|
+
expect(Mongo::Protocol::Registry.get(described_class::OP_CODE)).to be(described_class)
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'creates an #op_code instance method' do
|
171
|
+
expect(message.op_code).to eq(described_class::OP_CODE)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
161
175
|
end
|
@@ -100,4 +100,18 @@ describe Mongo::Protocol::KillCursors do
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end
|
103
|
+
|
104
|
+
describe '#registry' do
|
105
|
+
|
106
|
+
context 'when the class is loaded' do
|
107
|
+
|
108
|
+
it 'registers the op code in the Protocol Registry' do
|
109
|
+
expect(Mongo::Protocol::Registry.get(described_class::OP_CODE)).to be(described_class)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'creates an #op_code instance method' do
|
113
|
+
expect(message.op_code).to eq(described_class::OP_CODE)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
103
117
|
end
|
@@ -0,0 +1,499 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mongo::Protocol::Msg do
|
4
|
+
|
5
|
+
let(:opcode) { 2013 }
|
6
|
+
let(:flags) { [:none] }
|
7
|
+
let(:options) { {} }
|
8
|
+
let(:global_args) { { '$db' => TEST_DB, ping: 1 } }
|
9
|
+
let(:sections) { [ ] }
|
10
|
+
|
11
|
+
let(:message) do
|
12
|
+
described_class.new(flags, options, global_args, *sections)
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:deserialized) do
|
16
|
+
Mongo::Protocol::Message.deserialize(StringIO.new(message.serialize.to_s))
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#initialize' do
|
20
|
+
|
21
|
+
it 'adds the global_args to the sections' do
|
22
|
+
expect(message.sections[0]).to eq(type: 0, payload: global_args)
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when flag bits are provided' do
|
26
|
+
|
27
|
+
context 'when valid flags are provided' do
|
28
|
+
|
29
|
+
let(:flags) { [:more_to_come] }
|
30
|
+
|
31
|
+
it 'sets the flags' do
|
32
|
+
expect(message.flags).to eq(flags)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when flags are not provided' do
|
37
|
+
|
38
|
+
let(:flags) { nil }
|
39
|
+
|
40
|
+
it 'sets the flags to [:none]' do
|
41
|
+
expect(message.flags).to eq([:none])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when an invalid flag is provided' do
|
46
|
+
|
47
|
+
let(:flags) { [:checksum_present, :none] }
|
48
|
+
|
49
|
+
let(:flag_bytes) { message.serialize.to_s[16..19] }
|
50
|
+
|
51
|
+
it 'sets the flags' do
|
52
|
+
expect(message.flags).to eq([:checksum_present, :none])
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'only serializes the valid flags' do
|
56
|
+
expect(flag_bytes).to be_int32(1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#==' do
|
63
|
+
|
64
|
+
context 'when the other is a msg' do
|
65
|
+
|
66
|
+
context 'when the fields are equal' do
|
67
|
+
|
68
|
+
let(:other) do
|
69
|
+
described_class.new(flags, options, global_args)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'returns true' do
|
73
|
+
expect(message).to eq(other)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when the flags are not equal' do
|
78
|
+
|
79
|
+
let(:other) do
|
80
|
+
described_class.new([:more_to_come], options, global_args)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'returns false' do
|
84
|
+
expect(message).not_to eq(other)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'when the global_args are not equal' do
|
89
|
+
|
90
|
+
let(:other) do
|
91
|
+
described_class.new(flags, nil, { '$db'=> TEST_DB, ismaster: 1 })
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'returns false' do
|
95
|
+
expect(message).not_to eq(other)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'when the other is not a msg' do
|
101
|
+
|
102
|
+
let(:other) do
|
103
|
+
expect(message).not_to eq('test')
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#hash' do
|
109
|
+
|
110
|
+
let(:values) do
|
111
|
+
message.send(:fields).map do |field|
|
112
|
+
message.instance_variable_get(field[:name])
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'returns a hash of the field values' do
|
117
|
+
expect(message.hash).to eq(values.hash)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#replyable?' do
|
122
|
+
|
123
|
+
context 'when the :more_to_come flag is set' do
|
124
|
+
|
125
|
+
let(:flags) { [:more_to_come] }
|
126
|
+
|
127
|
+
it 'returns false' do
|
128
|
+
expect(message).to_not be_replyable
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'when the :more_to_come flag is not set' do
|
133
|
+
|
134
|
+
it 'returns true' do
|
135
|
+
expect(message).to be_replyable
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#serialize' do
|
141
|
+
|
142
|
+
let(:bytes) do
|
143
|
+
message.serialize
|
144
|
+
end
|
145
|
+
|
146
|
+
let(:flag_bytes) { bytes.to_s[16..19] }
|
147
|
+
let(:payload_type) { bytes.to_s[20] }
|
148
|
+
let(:payload_bytes) { bytes.to_s[21..-1] }
|
149
|
+
let(:global_args) { { ping: 1 } }
|
150
|
+
|
151
|
+
include_examples 'message with a header'
|
152
|
+
|
153
|
+
context 'when flags are provided' do
|
154
|
+
|
155
|
+
context 'when checksum_present is provided' do
|
156
|
+
|
157
|
+
let(:flags) do
|
158
|
+
[:checksum_present]
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'sets the flag bits' do
|
162
|
+
expect(flag_bytes).to be_int32(1)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context 'when more_to_come is provided' do
|
167
|
+
|
168
|
+
let(:flags) do
|
169
|
+
[:more_to_come]
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'sets the flag bits' do
|
173
|
+
expect(flag_bytes).to be_int32(2)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'when no flag is provided' do
|
179
|
+
|
180
|
+
let(:flags) do
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'sets the flag bits to 0' do
|
185
|
+
expect(flag_bytes).to be_int32(0)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context 'when global args are provided' do
|
190
|
+
|
191
|
+
it 'sets the payload type' do
|
192
|
+
expect(payload_type).to eq(0.chr)
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'serializes the global arguments' do
|
196
|
+
expect(payload_bytes).to be_bson(global_args)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context 'when additional sections are provided' do
|
201
|
+
|
202
|
+
let(:sections) do
|
203
|
+
[ section ]
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'when an invalid payload type is specified' do
|
207
|
+
|
208
|
+
let(:section) do
|
209
|
+
{ type: 2,
|
210
|
+
payload: { identifier: 'documents',
|
211
|
+
sequence: [ { a: 1 } ] } }
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'raises an exception' do
|
215
|
+
expect {
|
216
|
+
message.serialize
|
217
|
+
}.to raise_exception(Mongo::Error::UnknownPayloadType)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context 'when a 0 payload type is specified' do
|
222
|
+
|
223
|
+
let(:section) do
|
224
|
+
{ type: 0, payload: { ismaster: 1 } }
|
225
|
+
end
|
226
|
+
|
227
|
+
let(:section_payload_type) { bytes.to_s[36] }
|
228
|
+
let(:section_bytes) { bytes.to_s[37..-1] }
|
229
|
+
|
230
|
+
it 'sets the payload type' do
|
231
|
+
expect(section_payload_type).to eq(0.chr)
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'serializes the section' do
|
235
|
+
expect(section_bytes).to be_bson(section[:payload])
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context 'when a no payload type is specified' do
|
240
|
+
|
241
|
+
let(:section) do
|
242
|
+
{ payload: { ismaster: 1 } }
|
243
|
+
end
|
244
|
+
|
245
|
+
let(:section_payload_type) { bytes.to_s[36] }
|
246
|
+
let(:section_bytes) { bytes.to_s[37..-1] }
|
247
|
+
|
248
|
+
it 'sets the payload type as 0' do
|
249
|
+
expect(section_payload_type).to eq(0.chr)
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'serializes the section' do
|
253
|
+
expect(section_bytes).to be_bson(section[:payload])
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context 'when a 1 payload type is specified' do
|
258
|
+
|
259
|
+
let(:section) do
|
260
|
+
{ type: 1,
|
261
|
+
payload: { identifier: 'documents',
|
262
|
+
sequence: [ { a: 1 } ] } }
|
263
|
+
end
|
264
|
+
|
265
|
+
let(:section_payload_type) { bytes.to_s[36] }
|
266
|
+
let(:section_size) { bytes.to_s[37..40] }
|
267
|
+
let(:section_identifier) { bytes.to_s[41..50] }
|
268
|
+
let(:section_bytes) { bytes.to_s[51..-1] }
|
269
|
+
|
270
|
+
it 'sets the payload type' do
|
271
|
+
expect(section_payload_type).to eq(1.chr)
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'sets the section size' do
|
275
|
+
expect(section_size).to be_int32(26)
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'serializes the section identifier' do
|
279
|
+
expect(section_identifier).to eq("documents#{BSON::NULL_BYTE}")
|
280
|
+
end
|
281
|
+
|
282
|
+
it 'serializes the section bytes' do
|
283
|
+
expect(section_bytes).to be_bson({ a: 1 })
|
284
|
+
end
|
285
|
+
|
286
|
+
context 'when two sections are specified' do
|
287
|
+
|
288
|
+
let(:sections) do
|
289
|
+
[ section1, section2 ]
|
290
|
+
end
|
291
|
+
|
292
|
+
let(:section1) do
|
293
|
+
{ type: 1,
|
294
|
+
payload: { identifier: 'documents',
|
295
|
+
sequence: [ { a: 1 } ] } }
|
296
|
+
end
|
297
|
+
|
298
|
+
let(:section2) do
|
299
|
+
{ type: 1,
|
300
|
+
payload: { identifier: 'updates',
|
301
|
+
sequence: [ {:q => { :bar => 1 },
|
302
|
+
:u => { :$set => { :bar => 2 } },
|
303
|
+
:multi => true,
|
304
|
+
:upsert => false } ] } }
|
305
|
+
end
|
306
|
+
|
307
|
+
let(:section1_payload_type) { bytes.to_s[36] }
|
308
|
+
let(:section1_size) { bytes.to_s[37..40] }
|
309
|
+
let(:section1_identifier) { bytes.to_s[41..50] }
|
310
|
+
let(:section1_bytes) { bytes.to_s[51..62] }
|
311
|
+
|
312
|
+
it 'sets the first payload type' do
|
313
|
+
expect(section1_payload_type).to eq(1.chr)
|
314
|
+
end
|
315
|
+
|
316
|
+
it 'sets the first section size' do
|
317
|
+
expect(section1_size).to be_int32(26)
|
318
|
+
end
|
319
|
+
|
320
|
+
it 'serializes the first section identifier' do
|
321
|
+
expect(section1_identifier).to eq("documents#{BSON::NULL_BYTE}")
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'serializes the first section bytes' do
|
325
|
+
expect(section1_bytes).to be_bson({ a: 1 })
|
326
|
+
end
|
327
|
+
|
328
|
+
let(:section2_payload_type) { bytes.to_s[63] }
|
329
|
+
let(:section2_size) { bytes.to_s[64..67] }
|
330
|
+
let(:section2_identifier) { bytes.to_s[68..75] }
|
331
|
+
let(:section2_bytes) { bytes.to_s[76..-1] }
|
332
|
+
|
333
|
+
it 'sets the second payload type' do
|
334
|
+
expect(section2_payload_type).to eq(1.chr)
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'sets the second section size' do
|
338
|
+
expect(section2_size).to be_int32(79)
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'serializes the second section identifier' do
|
342
|
+
expect(section2_identifier).to eq("updates#{BSON::NULL_BYTE}")
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'serializes the second section bytes' do
|
346
|
+
expect(section2_bytes).to be_bson(section2[:payload][:sequence][0])
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
context 'when the sections are mixed types and payload type 1 comes before type 0' do
|
352
|
+
|
353
|
+
let(:section1) do
|
354
|
+
{ type: 1,
|
355
|
+
payload: { identifier: 'documents', sequence: [ { 'a' => 1 }]}}
|
356
|
+
end
|
357
|
+
|
358
|
+
let(:section2) do
|
359
|
+
{ type: 0, payload: { 'b' => 2 } }
|
360
|
+
end
|
361
|
+
|
362
|
+
let(:sections) do
|
363
|
+
[ section1, section2 ]
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'serializes all sections' do
|
367
|
+
expect(deserialized.documents).to eq([ BSON::Document.new(global_args), { 'a' => 1 }, { 'b' => 2 }])
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
context 'when the validating_keys option is true with payload 1' do
|
373
|
+
|
374
|
+
let(:sections) do
|
375
|
+
[ section ]
|
376
|
+
end
|
377
|
+
|
378
|
+
let(:section) do
|
379
|
+
{ type: 1, payload: { identifier: 'documents', sequence: [ { '$b' => 2 } ] } }
|
380
|
+
end
|
381
|
+
|
382
|
+
let(:options) do
|
383
|
+
{ validating_keys: true }
|
384
|
+
end
|
385
|
+
|
386
|
+
it 'checks the sequence document keys' do
|
387
|
+
expect {
|
388
|
+
message.serialize
|
389
|
+
}.to raise_exception(BSON::String::IllegalKey)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
context 'when the validating_keys option is false with payload 1' do
|
394
|
+
|
395
|
+
let(:sections) do
|
396
|
+
[ section ]
|
397
|
+
end
|
398
|
+
|
399
|
+
let(:section) do
|
400
|
+
{ type: 1, payload: { identifier: 'documents', sequence: [ { '$b' => 2 } ] } }
|
401
|
+
end
|
402
|
+
|
403
|
+
let(:options) do
|
404
|
+
{ validating_keys: false }
|
405
|
+
end
|
406
|
+
|
407
|
+
it 'does not check the sequence document keys' do
|
408
|
+
expect(message.serialize).to be_a(BSON::ByteBuffer)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
describe '#deserialize' do
|
414
|
+
|
415
|
+
context 'when the payload type is valid' do
|
416
|
+
|
417
|
+
it 'deserializes the message' do
|
418
|
+
expect(deserialized.documents).to eq([ BSON::Document.new(global_args) ])
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
context 'when the payload type is not valid' do
|
423
|
+
|
424
|
+
let(:invalid_payload_message) do
|
425
|
+
message.serialize.to_s.tap do |s|
|
426
|
+
s[20] = 5.chr
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
it 'raises an exception' do
|
431
|
+
expect {
|
432
|
+
Mongo::Protocol::Message.deserialize(StringIO.new(invalid_payload_message))
|
433
|
+
}.to raise_exception(Mongo::Error::UnknownPayloadType)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
describe '#payload' do
|
439
|
+
|
440
|
+
context 'when the msg only contains a payload type 0' do
|
441
|
+
|
442
|
+
it 'creates a payload with the command' do
|
443
|
+
expect(message.payload[:command_name]).to eq(:ping)
|
444
|
+
expect(message.payload[:database_name]).to eq(TEST_DB)
|
445
|
+
expect(message.payload[:command]).to eq('ping' => 1)
|
446
|
+
expect(message.payload[:request_id]).to eq(message.request_id)
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
context 'when the contains a payload type 1' do
|
451
|
+
|
452
|
+
let(:section) do
|
453
|
+
{ type: 1,
|
454
|
+
payload: { identifier: 'documents',
|
455
|
+
sequence: [ { a: 1 } ] } }
|
456
|
+
end
|
457
|
+
|
458
|
+
let(:global_args) do
|
459
|
+
{ '$db' => TEST_DB,
|
460
|
+
'insert' => TEST_COLL,
|
461
|
+
'ordered' => true
|
462
|
+
}
|
463
|
+
end
|
464
|
+
|
465
|
+
let(:sections) do
|
466
|
+
[ section ]
|
467
|
+
end
|
468
|
+
|
469
|
+
let(:expected_command_doc) do
|
470
|
+
{
|
471
|
+
'insert' => TEST_COLL,
|
472
|
+
'documents' => [{ 'a' => 1 }],
|
473
|
+
'ordered' => true
|
474
|
+
}
|
475
|
+
end
|
476
|
+
|
477
|
+
it 'creates a payload with the command' do
|
478
|
+
expect(message.payload[:command_name]).to eq('insert')
|
479
|
+
expect(message.payload[:database_name]).to eq(TEST_DB)
|
480
|
+
expect(message.payload[:command]).to eq(expected_command_doc)
|
481
|
+
expect(message.payload[:request_id]).to eq(message.request_id)
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
describe '#registry' do
|
487
|
+
|
488
|
+
context 'when the class is loaded' do
|
489
|
+
|
490
|
+
it 'registers the op code in the Protocol Registry' do
|
491
|
+
expect(Mongo::Protocol::Registry.get(described_class::OP_CODE)).to be(described_class)
|
492
|
+
end
|
493
|
+
|
494
|
+
it 'creates an #op_code instance method' do
|
495
|
+
expect(message.op_code).to eq(described_class::OP_CODE)
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|