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.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -2
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo.rb +3 -2
  5. data/lib/mongo/auth/cr.rb +6 -4
  6. data/lib/mongo/auth/cr/conversation.rb +33 -17
  7. data/lib/mongo/auth/ldap.rb +4 -2
  8. data/lib/mongo/auth/ldap/conversation.rb +19 -9
  9. data/lib/mongo/auth/scram.rb +7 -4
  10. data/lib/mongo/auth/scram/conversation.rb +62 -24
  11. data/lib/mongo/auth/user.rb +10 -0
  12. data/lib/mongo/auth/user/view.rb +44 -22
  13. data/lib/mongo/auth/x509.rb +4 -2
  14. data/lib/mongo/auth/x509/conversation.rb +19 -9
  15. data/lib/mongo/bulk_write.rb +33 -27
  16. data/lib/mongo/bulk_write/combineable.rb +5 -0
  17. data/lib/mongo/bulk_write/transformable.rb +2 -0
  18. data/lib/mongo/bulk_write/validatable.rb +4 -0
  19. data/lib/mongo/client.rb +123 -12
  20. data/lib/mongo/cluster.rb +52 -11
  21. data/lib/mongo/cluster/app_metadata.rb +8 -2
  22. data/lib/mongo/cluster/cursor_reaper.rb +0 -1
  23. data/lib/mongo/cluster/topology.rb +1 -1
  24. data/lib/mongo/collection.rb +114 -27
  25. data/lib/mongo/collection/view.rb +8 -2
  26. data/lib/mongo/collection/view/aggregation.rb +11 -7
  27. data/lib/mongo/collection/view/builder/aggregation.rb +5 -1
  28. data/lib/mongo/collection/view/builder/find_command.rb +5 -3
  29. data/lib/mongo/collection/view/builder/map_reduce.rb +11 -3
  30. data/lib/mongo/collection/view/builder/op_query.rb +1 -1
  31. data/lib/mongo/collection/view/change_stream.rb +160 -0
  32. data/lib/mongo/collection/view/change_stream/retryable.rb +57 -0
  33. data/lib/mongo/collection/view/iterable.rb +11 -10
  34. data/lib/mongo/collection/view/map_reduce.rb +22 -18
  35. data/lib/mongo/collection/view/readable.rb +51 -37
  36. data/lib/mongo/collection/view/writable.rb +72 -40
  37. data/lib/mongo/cursor.rb +25 -4
  38. data/lib/mongo/cursor/builder/get_more_command.rb +4 -2
  39. data/lib/mongo/database.rb +22 -11
  40. data/lib/mongo/database/view.rb +16 -12
  41. data/lib/mongo/error.rb +5 -0
  42. data/lib/mongo/error/invalid_session.rb +36 -0
  43. data/lib/mongo/error/missing_resume_token.rb +39 -0
  44. data/lib/mongo/error/operation_failure.rb +17 -0
  45. data/lib/mongo/error/parser.rb +3 -2
  46. data/lib/mongo/error/unknown_payload_type.rb +41 -0
  47. data/lib/mongo/error/unsupported_array_filters.rb +51 -0
  48. data/lib/mongo/error/unsupported_message_type.rb +23 -0
  49. data/lib/mongo/grid/fs_bucket.rb +5 -4
  50. data/lib/mongo/grid/stream/read.rb +3 -2
  51. data/lib/mongo/grid/stream/write.rb +2 -2
  52. data/lib/mongo/index/view.rb +35 -25
  53. data/lib/mongo/monitoring/event/secure.rb +14 -0
  54. data/lib/mongo/operation.rb +16 -0
  55. data/lib/mongo/operation/commands.rb +1 -0
  56. data/lib/mongo/operation/commands/aggregate.rb +9 -5
  57. data/lib/mongo/operation/commands/aggregate/result.rb +1 -1
  58. data/lib/mongo/operation/commands/collections_info.rb +6 -6
  59. data/lib/mongo/operation/commands/command.rb +2 -1
  60. data/lib/mongo/operation/commands/create.rb +6 -2
  61. data/lib/mongo/operation/commands/drop.rb +6 -2
  62. data/lib/mongo/operation/commands/drop_database.rb +6 -2
  63. data/lib/mongo/operation/commands/explain.rb +27 -0
  64. data/lib/mongo/operation/commands/explain/result.rb +52 -0
  65. data/lib/mongo/operation/commands/indexes.rb +1 -1
  66. data/lib/mongo/operation/commands/list_collections.rb +1 -1
  67. data/lib/mongo/operation/commands/list_collections/result.rb +1 -1
  68. data/lib/mongo/operation/commands/list_indexes.rb +1 -1
  69. data/lib/mongo/operation/commands/list_indexes/result.rb +1 -1
  70. data/lib/mongo/operation/commands/map_reduce.rb +8 -4
  71. data/lib/mongo/operation/commands/map_reduce/result.rb +13 -1
  72. data/lib/mongo/operation/commands/user_query.rb +1 -1
  73. data/lib/mongo/operation/commands/users_info.rb +6 -2
  74. data/lib/mongo/operation/executable.rb +4 -1
  75. data/lib/mongo/operation/read_preference.rb +10 -5
  76. data/lib/mongo/operation/result.rb +26 -2
  77. data/lib/mongo/operation/specifiable.rb +13 -1
  78. data/lib/mongo/operation/uses_command_op_msg.rb +47 -0
  79. data/lib/mongo/operation/write/bulk/bulkable.rb +4 -1
  80. data/lib/mongo/operation/write/bulk/insert/result.rb +4 -4
  81. data/lib/mongo/operation/write/command/create_index.rb +6 -1
  82. data/lib/mongo/operation/write/command/delete.rb +28 -4
  83. data/lib/mongo/operation/write/command/drop_index.rb +6 -1
  84. data/lib/mongo/operation/write/command/insert.rb +22 -18
  85. data/lib/mongo/operation/write/command/update.rb +24 -9
  86. data/lib/mongo/operation/write/command/writable.rb +14 -1
  87. data/lib/mongo/operation/write/insert.rb +4 -1
  88. data/lib/mongo/operation/write/insert/result.rb +2 -2
  89. data/lib/mongo/operation/write/update.rb +7 -1
  90. data/lib/mongo/operation/write/write_command_enabled.rb +20 -3
  91. data/lib/mongo/protocol.rb +3 -0
  92. data/lib/mongo/protocol/bit_vector.rb +2 -2
  93. data/lib/mongo/protocol/compressed.rb +135 -0
  94. data/lib/mongo/protocol/delete.rb +8 -6
  95. data/lib/mongo/protocol/get_more.rb +8 -6
  96. data/lib/mongo/protocol/insert.rb +8 -6
  97. data/lib/mongo/protocol/kill_cursors.rb +8 -6
  98. data/lib/mongo/protocol/message.rb +31 -3
  99. data/lib/mongo/protocol/msg.rb +172 -0
  100. data/lib/mongo/protocol/query.rb +26 -6
  101. data/lib/mongo/protocol/registry.rb +76 -0
  102. data/lib/mongo/protocol/reply.rb +10 -5
  103. data/lib/mongo/protocol/serializers.rb +224 -0
  104. data/lib/mongo/protocol/update.rb +8 -6
  105. data/lib/mongo/retryable.rb +4 -2
  106. data/lib/mongo/server.rb +6 -3
  107. data/lib/mongo/server/connectable.rb +1 -1
  108. data/lib/mongo/server/connection.rb +30 -8
  109. data/lib/mongo/server/description.rb +25 -1
  110. data/lib/mongo/server/description/features.rb +4 -1
  111. data/lib/mongo/server/monitor.rb +5 -0
  112. data/lib/mongo/server/monitor/connection.rb +50 -2
  113. data/lib/mongo/server_selector/nearest.rb +10 -4
  114. data/lib/mongo/server_selector/primary.rb +20 -0
  115. data/lib/mongo/server_selector/primary_preferred.rb +10 -4
  116. data/lib/mongo/server_selector/secondary.rb +10 -4
  117. data/lib/mongo/server_selector/secondary_preferred.rb +24 -4
  118. data/lib/mongo/session.rb +180 -0
  119. data/lib/mongo/session/server_session.rb +73 -0
  120. data/lib/mongo/session/session_pool.rb +161 -0
  121. data/lib/mongo/uri.rb +11 -0
  122. data/lib/mongo/version.rb +1 -1
  123. data/mongo.gemspec +2 -1
  124. data/spec/mongo/auth/cr_spec.rb +12 -0
  125. data/spec/mongo/auth/ldap_spec.rb +2 -0
  126. data/spec/mongo/auth/scram/conversation_spec.rb +6 -6
  127. data/spec/mongo/auth/scram_spec.rb +25 -1
  128. data/spec/mongo/auth/user/view_spec.rb +268 -76
  129. data/spec/mongo/auth/x509_spec.rb +2 -0
  130. data/spec/mongo/bulk_write_spec.rb +435 -5
  131. data/spec/mongo/client_spec.rb +356 -39
  132. data/spec/mongo/cluster/app_metadata_spec.rb +2 -2
  133. data/spec/mongo/cluster_spec.rb +176 -0
  134. data/spec/mongo/collection/view/aggregation_spec.rb +33 -12
  135. data/spec/mongo/collection/view/builder/find_command_spec.rb +46 -6
  136. data/spec/mongo/collection/view/change_stream_spec.rb +814 -0
  137. data/spec/mongo/collection/view/map_reduce_spec.rb +94 -17
  138. data/spec/mongo/collection/view/readable_spec.rb +3 -12
  139. data/spec/mongo/collection_spec.rb +1048 -42
  140. data/spec/mongo/cursor/builder/get_more_command_spec.rb +19 -0
  141. data/spec/mongo/cursor_spec.rb +2 -2
  142. data/spec/mongo/database_spec.rb +50 -1
  143. data/spec/mongo/grid/fs_bucket_spec.rb +225 -137
  144. data/spec/mongo/grid/stream/read_spec.rb +2 -2
  145. data/spec/mongo/index/view_spec.rb +146 -8
  146. data/spec/mongo/monitoring/event/secure_spec.rb +42 -0
  147. data/spec/mongo/operation/read/query_spec.rb +2 -1
  148. data/spec/mongo/operation/specifiable_spec.rb +2 -2
  149. data/spec/mongo/operation/write/command/delete_spec.rb +96 -13
  150. data/spec/mongo/operation/write/command/insert_spec.rb +111 -12
  151. data/spec/mongo/operation/write/command/update_spec.rb +93 -10
  152. data/spec/mongo/operation/write/delete_spec.rb +1 -1
  153. data/spec/mongo/operation/write/insert_spec.rb +1 -1
  154. data/spec/mongo/operation/write/update_spec.rb +1 -1
  155. data/spec/mongo/protocol/compressed_spec.rb +66 -0
  156. data/spec/mongo/protocol/delete_spec.rb +14 -0
  157. data/spec/mongo/protocol/get_more_spec.rb +14 -0
  158. data/spec/mongo/protocol/insert_spec.rb +14 -0
  159. data/spec/mongo/protocol/kill_cursors_spec.rb +14 -0
  160. data/spec/mongo/protocol/msg_spec.rb +499 -0
  161. data/spec/mongo/protocol/query_spec.rb +45 -0
  162. data/spec/mongo/protocol/registry_spec.rb +31 -0
  163. data/spec/mongo/protocol/reply_spec.rb +14 -0
  164. data/spec/mongo/protocol/update_spec.rb +14 -0
  165. data/spec/mongo/retryable_spec.rb +6 -2
  166. data/spec/mongo/sdam_spec.rb +4 -0
  167. data/spec/mongo/server/connection_spec.rb +4 -2
  168. data/spec/mongo/server/description_spec.rb +28 -1
  169. data/spec/mongo/session/server_session_spec.rb +16 -0
  170. data/spec/mongo/session/session_pool_spec.rb +194 -0
  171. data/spec/mongo/uri_spec.rb +31 -2
  172. data/spec/spec_helper.rb +104 -0
  173. data/spec/support/authorization.rb +6 -1
  174. data/spec/support/crud.rb +3 -1
  175. data/spec/support/crud/write.rb +6 -1
  176. data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +69 -0
  177. data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +63 -0
  178. data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +109 -0
  179. data/spec/support/sdam/rs/discover_arbiters.yml +1 -1
  180. data/spec/support/sdam/rs/discover_passives.yml +2 -2
  181. data/spec/support/sdam/rs/discover_primary.yml +1 -1
  182. data/spec/support/sdam/rs/discover_secondary.yml +1 -1
  183. data/spec/support/sdam/rs/discovery.yml +4 -4
  184. data/spec/support/sdam/rs/equal_electionids.yml +1 -0
  185. data/spec/support/sdam/rs/ghost_discovered.yml +1 -1
  186. data/spec/support/sdam/rs/hosts_differ_from_seeds.yml +1 -1
  187. data/spec/support/sdam/rs/ls_timeout.yml +88 -0
  188. data/spec/support/sdam/rs/member_reconfig.yml +2 -2
  189. data/spec/support/sdam/rs/member_standalone.yml +2 -2
  190. data/spec/support/sdam/rs/new_primary.yml +2 -2
  191. data/spec/support/sdam/rs/new_primary_new_electionid.yml +3 -0
  192. data/spec/support/sdam/rs/new_primary_new_setversion.yml +3 -0
  193. data/spec/support/sdam/rs/new_primary_wrong_set_name.yml +2 -2
  194. data/spec/support/sdam/rs/non_rs_member.yml +1 -1
  195. data/spec/support/sdam/rs/normalize_case.yml +1 -1
  196. data/spec/support/sdam/rs/null_election_id.yml +4 -0
  197. data/spec/support/sdam/rs/primary_becomes_standalone.yml +2 -2
  198. data/spec/support/sdam/rs/primary_changes_set_name.yml +2 -2
  199. data/spec/support/sdam/rs/primary_disconnect.yml +2 -2
  200. data/spec/support/sdam/rs/primary_disconnect_electionid.yml +5 -0
  201. data/spec/support/sdam/rs/primary_disconnect_setversion.yml +5 -0
  202. data/spec/support/sdam/rs/primary_hint_from_secondary_with_mismatched_me.yml +58 -0
  203. data/spec/support/sdam/rs/primary_reports_new_member.yml +4 -4
  204. data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +2 -2
  205. data/spec/support/sdam/rs/primary_wrong_set_name.yml +1 -1
  206. data/spec/support/sdam/rs/response_from_removed.yml +2 -2
  207. data/spec/support/sdam/rs/rsother_discovered.yml +1 -1
  208. data/spec/support/sdam/rs/sec_not_auth.yml +1 -1
  209. data/spec/support/sdam/rs/secondary_wrong_set_name.yml +1 -1
  210. data/spec/support/sdam/rs/secondary_wrong_set_name_with_primary.yml +2 -2
  211. data/spec/support/sdam/rs/setversion_without_electionid.yml +2 -0
  212. data/spec/support/sdam/rs/stepdown_change_set_name.yml +2 -2
  213. data/spec/support/sdam/rs/unexpected_mongos.yml +1 -1
  214. data/spec/support/sdam/rs/use_setversion_without_electionid.yml +3 -0
  215. data/spec/support/sdam/rs/wrong_set_name.yml +1 -1
  216. data/spec/support/sdam/sharded/ls_timeout_mongos.yml +97 -0
  217. data/spec/support/sdam/sharded/mongos_disconnect.yml +3 -3
  218. data/spec/support/sdam/sharded/multiple_mongoses.yml +1 -1
  219. data/spec/support/sdam/sharded/non_mongos_removed.yml +1 -1
  220. data/spec/support/sdam/sharded/normalize_uri_case.yml +1 -1
  221. data/spec/support/sdam/single/direct_connection_external_ip.yml +1 -1
  222. data/spec/support/sdam/single/direct_connection_mongos.yml +1 -1
  223. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +1 -1
  224. data/spec/support/sdam/single/direct_connection_rsprimary.yml +1 -1
  225. data/spec/support/sdam/single/direct_connection_rssecondary.yml +1 -1
  226. data/spec/support/sdam/single/direct_connection_slave.yml +1 -1
  227. data/spec/support/sdam/single/direct_connection_standalone.yml +1 -1
  228. data/spec/support/sdam/single/ls_timeout_standalone.yml +35 -0
  229. data/spec/support/sdam/single/not_ok_response.yml +1 -1
  230. data/spec/support/sdam/single/standalone_removed.yml +1 -1
  231. data/spec/support/sdam/single/unavailable_seed.yml +1 -1
  232. data/spec/support/server_discovery_and_monitoring.rb +4 -0
  233. data/spec/support/shared/session.rb +236 -0
  234. metadata +53 -15
  235. 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
- let(:expected_selector) do
97
- {
98
- :update => TEST_COLL,
99
- :updates => updates,
100
- :ordered => true,
101
- :writeConcern => write_concern.options
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
- it 'creates the correct Command message' do
106
- expect(Mongo::Protocol::Query).to receive(:new).with(TEST_DB, '$cmd', expected_selector, { limit: -1 })
107
- op.send(:message, double('server'))
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
@@ -183,7 +183,7 @@ describe Mongo::Operation::Write::Delete do
183
183
  end
184
184
  end
185
185
 
186
- context 'when write concern { w: 0 } is used' do
186
+ context 'when write concern { w: 0 } is used', unless: op_msg_enabled? do
187
187
 
188
188
  let(:delete) do
189
189
  described_class.new({
@@ -257,7 +257,7 @@ describe Mongo::Operation::Write::Insert do
257
257
  end
258
258
  end
259
259
 
260
- context 'when write concern { w: 0 } is used' do
260
+ context 'when write concern { w: 0 } is used', unless: op_msg_enabled? do
261
261
 
262
262
  let(:spec) do
263
263
  { :documents => documents,
@@ -237,7 +237,7 @@ describe Mongo::Operation::Write::Update do
237
237
  end
238
238
  end
239
239
 
240
- context 'when write concern { w: 0 } is used' do
240
+ context 'when write concern { w: 0 } is used', unless: op_msg_enabled? do
241
241
 
242
242
  let(:update) do
243
243
  described_class.new({
@@ -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