mongo 2.14.1 → 2.15.0.alpha

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 (230) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +4 -1
  4. data/Rakefile +8 -15
  5. data/lib/mongo/auth/aws/conversation.rb +1 -4
  6. data/lib/mongo/auth/base.rb +13 -7
  7. data/lib/mongo/auth/conversation_base.rb +32 -0
  8. data/lib/mongo/auth/cr/conversation.rb +6 -29
  9. data/lib/mongo/auth/gssapi/conversation.rb +4 -15
  10. data/lib/mongo/auth/ldap/conversation.rb +3 -14
  11. data/lib/mongo/auth/sasl_conversation_base.rb +1 -13
  12. data/lib/mongo/auth/scram_conversation_base.rb +7 -34
  13. data/lib/mongo/auth/user/view.rb +16 -9
  14. data/lib/mongo/auth/x509/conversation.rb +4 -25
  15. data/lib/mongo/bulk_write.rb +21 -18
  16. data/lib/mongo/client.rb +82 -6
  17. data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -2
  18. data/lib/mongo/cluster.rb +19 -2
  19. data/lib/mongo/collection/view/aggregation.rb +1 -1
  20. data/lib/mongo/collection/view/change_stream.rb +1 -1
  21. data/lib/mongo/collection/view/iterable.rb +7 -17
  22. data/lib/mongo/collection/view/map_reduce.rb +2 -2
  23. data/lib/mongo/collection/view/readable.rb +42 -20
  24. data/lib/mongo/collection/view/writable.rb +14 -14
  25. data/lib/mongo/collection.rb +6 -6
  26. data/lib/mongo/cursor.rb +2 -12
  27. data/lib/mongo/database/view.rb +1 -1
  28. data/lib/mongo/database.rb +8 -3
  29. data/lib/mongo/error/bulk_write_error.rb +17 -3
  30. data/lib/mongo/error/internal_driver_error.rb +22 -0
  31. data/lib/mongo/error/operation_failure.rb +21 -2
  32. data/lib/mongo/error/parser.rb +65 -12
  33. data/lib/mongo/error/server_api_conflict.rb +23 -0
  34. data/lib/mongo/error/server_api_not_supported.rb +24 -0
  35. data/lib/mongo/error/unmet_dependency.rb +21 -0
  36. data/lib/mongo/error.rb +9 -1
  37. data/lib/mongo/index/view.rb +21 -11
  38. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +27 -16
  39. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +26 -15
  40. data/lib/mongo/monitoring.rb +13 -4
  41. data/lib/mongo/operation/collections_info/command.rb +2 -2
  42. data/lib/mongo/operation/collections_info.rb +18 -1
  43. data/lib/mongo/operation/context.rb +99 -0
  44. data/lib/mongo/operation/indexes.rb +15 -1
  45. data/lib/mongo/operation/insert/command.rb +2 -2
  46. data/lib/mongo/operation/insert/legacy.rb +2 -2
  47. data/lib/mongo/operation/insert/op_msg.rb +2 -2
  48. data/lib/mongo/operation/list_collections/result.rb +4 -1
  49. data/lib/mongo/operation/parallel_scan/command.rb +2 -1
  50. data/lib/mongo/operation/result.rb +2 -0
  51. data/lib/mongo/operation/shared/executable.rb +24 -14
  52. data/lib/mongo/operation/shared/executable_no_validate.rb +2 -2
  53. data/lib/mongo/operation/shared/op_msg_or_command.rb +1 -7
  54. data/lib/mongo/operation/shared/op_msg_or_find_command.rb +1 -7
  55. data/lib/mongo/operation/shared/polymorphic_operation.rb +39 -0
  56. data/lib/mongo/operation/shared/read_preference_supported.rb +36 -38
  57. data/lib/mongo/operation/shared/response_handling.rb +23 -23
  58. data/lib/mongo/operation/shared/sessions_supported.rb +15 -5
  59. data/lib/mongo/operation/shared/write.rb +8 -18
  60. data/lib/mongo/operation.rb +2 -2
  61. data/lib/mongo/protocol/compressed.rb +51 -5
  62. data/lib/mongo/protocol/message.rb +20 -2
  63. data/lib/mongo/protocol/msg.rb +38 -13
  64. data/lib/mongo/protocol/query.rb +11 -11
  65. data/lib/mongo/query_cache.rb +30 -0
  66. data/lib/mongo/retryable.rb +1 -1
  67. data/lib/mongo/server/app_metadata.rb +52 -18
  68. data/lib/mongo/server/connection.rb +5 -0
  69. data/lib/mongo/server/connection_base.rb +13 -10
  70. data/lib/mongo/server/connection_pool.rb +6 -2
  71. data/lib/mongo/server/description/features.rb +9 -8
  72. data/lib/mongo/server/description.rb +4 -0
  73. data/lib/mongo/server/monitor/app_metadata.rb +1 -1
  74. data/lib/mongo/server/monitor/connection.rb +9 -10
  75. data/lib/mongo/server/monitor.rb +20 -1
  76. data/lib/mongo/server/pending_connection.rb +24 -6
  77. data/lib/mongo/server/push_monitor.rb +11 -1
  78. data/lib/mongo/server.rb +7 -1
  79. data/lib/mongo/server_selector/secondary_preferred.rb +7 -2
  80. data/lib/mongo/session/session_pool.rb +4 -2
  81. data/lib/mongo/session.rb +2 -2
  82. data/lib/mongo/socket/ssl.rb +8 -0
  83. data/lib/mongo/socket.rb +29 -4
  84. data/lib/mongo/uri/options_mapper.rb +38 -0
  85. data/lib/mongo/utils.rb +15 -0
  86. data/lib/mongo/version.rb +1 -1
  87. data/lib/mongo.rb +23 -0
  88. data/spec/README.md +24 -1
  89. data/spec/integration/auth_spec.rb +25 -15
  90. data/spec/integration/bulk_write_error_message_spec.rb +41 -0
  91. data/spec/integration/change_stream_spec.rb +4 -4
  92. data/spec/integration/command_monitoring_spec.rb +2 -2
  93. data/spec/integration/connection_spec.rb +2 -0
  94. data/spec/integration/docs_examples_spec.rb +8 -1
  95. data/spec/integration/fork_reconnect_spec.rb +4 -1
  96. data/spec/integration/ocsp_verifier_spec.rb +13 -7
  97. data/spec/integration/operation_failure_code_spec.rb +1 -1
  98. data/spec/integration/operation_failure_message_spec.rb +90 -0
  99. data/spec/integration/query_cache_spec.rb +0 -45
  100. data/spec/integration/reconnect_spec.rb +1 -1
  101. data/spec/integration/snappy_compression_spec.rb +25 -0
  102. data/spec/integration/srv_monitoring_spec.rb +1 -1
  103. data/spec/integration/transactions_examples_spec.rb +6 -0
  104. data/spec/integration/zlib_compression_spec.rb +1 -1
  105. data/spec/integration/zstd_compression_spec.rb +26 -0
  106. data/spec/lite_spec_helper.rb +7 -1
  107. data/spec/mongo/address_spec.rb +15 -11
  108. data/spec/mongo/auth/ldap/conversation_spec.rb +1 -1
  109. data/spec/mongo/auth/ldap_spec.rb +5 -1
  110. data/spec/mongo/auth/scram_negotiation_spec.rb +1 -1
  111. data/spec/mongo/auth/scram_spec.rb +1 -1
  112. data/spec/mongo/auth/x509/conversation_spec.rb +3 -3
  113. data/spec/mongo/client_construction_spec.rb +207 -33
  114. data/spec/mongo/client_spec.rb +17 -0
  115. data/spec/mongo/cluster_spec.rb +1 -0
  116. data/spec/mongo/collection/view/explainable_spec.rb +1 -1
  117. data/spec/mongo/collection/view/readable_spec.rb +33 -19
  118. data/spec/mongo/collection_crud_spec.rb +4357 -0
  119. data/spec/mongo/collection_ddl_spec.rb +534 -0
  120. data/spec/mongo/collection_spec.rb +5 -4859
  121. data/spec/mongo/database_spec.rb +66 -4
  122. data/spec/mongo/error/bulk_write_error_spec.rb +3 -3
  123. data/spec/mongo/error/parser_spec.rb +37 -6
  124. data/spec/mongo/index/view_spec.rb +4 -0
  125. data/spec/mongo/monitoring/event/server_heartbeat_failed_spec.rb +1 -1
  126. data/spec/mongo/monitoring/event/server_heartbeat_succeeded_spec.rb +1 -1
  127. data/spec/mongo/operation/aggregate_spec.rb +2 -1
  128. data/spec/mongo/operation/collections_info_spec.rb +4 -1
  129. data/spec/mongo/operation/command_spec.rb +6 -3
  130. data/spec/mongo/operation/create_index_spec.rb +6 -3
  131. data/spec/mongo/operation/create_user_spec.rb +6 -3
  132. data/spec/mongo/operation/delete/bulk_spec.rb +9 -6
  133. data/spec/mongo/operation/delete_spec.rb +11 -7
  134. data/spec/mongo/operation/drop_index_spec.rb +6 -2
  135. data/spec/mongo/operation/find/legacy_spec.rb +3 -1
  136. data/spec/mongo/operation/get_more_spec.rb +3 -1
  137. data/spec/mongo/operation/indexes_spec.rb +5 -1
  138. data/spec/mongo/operation/insert/bulk_spec.rb +10 -7
  139. data/spec/mongo/operation/insert_spec.rb +15 -12
  140. data/spec/mongo/operation/map_reduce_spec.rb +5 -2
  141. data/spec/mongo/operation/read_preference_legacy_spec.rb +19 -9
  142. data/spec/mongo/operation/read_preference_op_msg_spec.rb +3 -3
  143. data/spec/mongo/operation/remove_user_spec.rb +6 -3
  144. data/spec/mongo/operation/result_spec.rb +1 -1
  145. data/spec/mongo/operation/update/bulk_spec.rb +9 -6
  146. data/spec/mongo/operation/update_spec.rb +10 -7
  147. data/spec/mongo/operation/update_user_spec.rb +4 -1
  148. data/spec/mongo/protocol/compressed_spec.rb +26 -12
  149. data/spec/mongo/query_cache_middleware_spec.rb +55 -0
  150. data/spec/mongo/retryable_spec.rb +3 -2
  151. data/spec/mongo/server/app_metadata_shared.rb +7 -33
  152. data/spec/mongo/server/app_metadata_spec.rb +2 -0
  153. data/spec/mongo/server/connection_pool/populator_spec.rb +3 -1
  154. data/spec/mongo/server/connection_pool_spec.rb +1 -1
  155. data/spec/mongo/server/connection_spec.rb +24 -17
  156. data/spec/mongo/server/monitor/connection_spec.rb +17 -7
  157. data/spec/mongo/server/monitor_spec.rb +9 -1
  158. data/spec/mongo/server_selector/secondary_preferred_spec.rb +6 -6
  159. data/spec/mongo/server_spec.rb +15 -2
  160. data/spec/mongo/socket/ssl_spec.rb +40 -0
  161. data/spec/mongo/socket_spec.rb +2 -2
  162. data/spec/mongo/tls_context_hooks_spec.rb +37 -0
  163. data/spec/runners/connection_string.rb +0 -4
  164. data/spec/runners/crud/requirement.rb +40 -3
  165. data/spec/runners/crud/verifier.rb +8 -0
  166. data/spec/runners/transactions/operation.rb +1 -1
  167. data/spec/runners/transactions/test.rb +1 -0
  168. data/spec/runners/unified/assertions.rb +249 -0
  169. data/spec/runners/unified/change_stream_operations.rb +26 -0
  170. data/spec/runners/unified/crud_operations.rb +199 -0
  171. data/spec/runners/unified/ddl_operations.rb +96 -0
  172. data/spec/runners/unified/entity_map.rb +39 -0
  173. data/spec/runners/unified/error.rb +25 -0
  174. data/spec/runners/unified/event_subscriber.rb +91 -0
  175. data/spec/runners/unified/exceptions.rb +21 -0
  176. data/spec/runners/unified/grid_fs_operations.rb +55 -0
  177. data/spec/runners/unified/support_operations.rb +250 -0
  178. data/spec/runners/unified/test.rb +393 -0
  179. data/spec/runners/unified/test_group.rb +28 -0
  180. data/spec/runners/unified/using_hash.rb +31 -0
  181. data/spec/runners/unified.rb +96 -0
  182. data/spec/shared/lib/mrss/cluster_config.rb +0 -3
  183. data/spec/shared/lib/mrss/docker_runner.rb +0 -3
  184. data/spec/shared/lib/mrss/lite_constraints.rb +0 -16
  185. data/spec/shared/lib/mrss/server_version_registry.rb +0 -3
  186. data/spec/shared/lib/mrss/spec_organizer.rb +0 -3
  187. data/spec/shared/shlib/server.sh +1 -1
  188. data/spec/spec_helper.rb +4 -1
  189. data/spec/spec_tests/crud_unified_spec.rb +10 -0
  190. data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
  191. data/spec/spec_tests/data/crud_unified/estimatedDocumentCount.yml +267 -0
  192. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-4.9.yml +60 -0
  193. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount.yml → estimatedDocumentCount-pre4.9.yml} +2 -0
  194. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors-4.9.yml +146 -0
  195. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount-serverErrors.yml → estimatedDocumentCount-serverErrors-pre4.9.yml} +2 -0
  196. data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +1 -1
  197. data/spec/spec_tests/data/unified/valid-fail/operation-failure.yml +31 -0
  198. data/spec/spec_tests/data/unified/valid-pass/poc-change-streams.yml +220 -0
  199. data/spec/spec_tests/data/unified/valid-pass/poc-command-monitoring.yml +102 -0
  200. data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +184 -0
  201. data/spec/spec_tests/data/unified/valid-pass/poc-gridfs.yml +155 -0
  202. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-reads.yml +193 -0
  203. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +210 -0
  204. data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +215 -0
  205. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +235 -0
  206. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +169 -0
  207. data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +170 -0
  208. data/spec/spec_tests/data/uri_options/compression-options.yml +1 -1
  209. data/spec/spec_tests/data/versioned_api/crud-api-version-1-strict.yml +416 -0
  210. data/spec/spec_tests/data/versioned_api/crud-api-version-1.yml +409 -0
  211. data/spec/spec_tests/data/versioned_api/runcommand-helper-no-api-version-declared.yml +67 -0
  212. data/spec/spec_tests/data/versioned_api/test-commands-deprecation-errors.yml +47 -0
  213. data/spec/spec_tests/data/versioned_api/test-commands-strict-mode.yml +44 -0
  214. data/spec/spec_tests/data/versioned_api/transaction-handling.yml +180 -0
  215. data/spec/spec_tests/unified_spec.rb +15 -0
  216. data/spec/spec_tests/uri_options_spec.rb +16 -0
  217. data/spec/spec_tests/versioned_api_spec.rb +10 -0
  218. data/spec/support/client_registry.rb +4 -8
  219. data/spec/support/client_registry_macros.rb +4 -4
  220. data/spec/support/common_shortcuts.rb +15 -1
  221. data/spec/support/shared/session.rb +2 -2
  222. data/spec/support/spec_config.rb +42 -11
  223. data/spec/support/utils.rb +64 -3
  224. data.tar.gz.sig +0 -0
  225. metadata +1005 -915
  226. metadata.gz.sig +0 -0
  227. data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +0 -58
  228. data/lib/mongo/operation/shared/op_msg_or_list_indexes_command.rb +0 -47
  229. data/spec/integration/secondary_reads_spec.rb +0 -102
  230. data/spec/support/cluster_config.rb +0 -207
@@ -0,0 +1,393 @@
1
+ require 'runners/crud/requirement'
2
+ require 'runners/unified/crud_operations'
3
+ require 'runners/unified/grid_fs_operations'
4
+ require 'runners/unified/ddl_operations'
5
+ require 'runners/unified/change_stream_operations'
6
+ require 'runners/unified/support_operations'
7
+ require 'runners/unified/assertions'
8
+ require 'support/utils'
9
+
10
+ module Unified
11
+
12
+ class Test
13
+ include CrudOperations
14
+ include GridFsOperations
15
+ include DdlOperations
16
+ include ChangeStreamOperations
17
+ include SupportOperations
18
+ include Assertions
19
+
20
+ def initialize(spec, **opts)
21
+ @spec = spec
22
+ @entities = EntityMap.new
23
+ @test_spec = UsingHash[@spec.fetch('test')]
24
+ @description = @test_spec.use('description')
25
+ @outcome = @test_spec.use('outcome')
26
+ @expected_events = @test_spec.use('expectEvents')
27
+ @skip_reason = @test_spec.use('skipReason')
28
+ if req = @test_spec.use('runOnRequirements')
29
+ @reqs = req.map { |r| Mongo::CRUD::Requirement.new(r) }
30
+ end
31
+ if req = @spec['group_runOnRequirements']
32
+ @group_reqs = req.map { |r| Mongo::CRUD::Requirement.new(r) }
33
+ end
34
+ mongoses = @spec['createEntities'].select do |spec|
35
+ spec['client']
36
+ end.map do |spec|
37
+ spec['client']['useMultipleMongoses']
38
+ end.compact.uniq
39
+ if mongoses.length > 1
40
+ raise Error::InvalidTest, "Conflicting useMultipleMongoses values"
41
+ end
42
+ @multiple_mongoses = mongoses.first
43
+ @test_spec.freeze
44
+ @subscribers = {}
45
+ @options = opts
46
+ end
47
+
48
+ attr_reader :test_spec
49
+ attr_reader :description
50
+ attr_reader :outcome
51
+ attr_reader :skip_reason
52
+ attr_reader :reqs, :group_reqs
53
+ attr_reader :options
54
+
55
+ def skip?
56
+ !!@skip_reason
57
+ end
58
+
59
+ def require_multiple_mongoses?
60
+ @multiple_mongoses == true
61
+ end
62
+
63
+ def require_single_mongos?
64
+ @multiple_mongoses == false
65
+ end
66
+
67
+ attr_reader :entities
68
+
69
+ def create_entities
70
+ @spec['createEntities'].each do |entity_spec|
71
+ unless entity_spec.keys.length == 1
72
+ raise NotImplementedError, "Entity must have exactly one key"
73
+ end
74
+
75
+ type, spec = entity_spec.first
76
+ spec = UsingHash[spec]
77
+ id = spec.use!('id')
78
+
79
+ entity = case type
80
+ when 'client'
81
+ # Handled earlier
82
+ spec.delete('useMultipleMongoses')
83
+
84
+ if smc_opts = spec.use('uriOptions')
85
+ opts = Mongo::URI::OptionsMapper.new.smc_to_ruby(smc_opts)
86
+ else
87
+ opts = {}
88
+ end
89
+
90
+ if store_events = spec.use('storeEventsAsEntities')
91
+ store_event_names = {}
92
+ store_events.each do |spec|
93
+ entity_name = spec['id']
94
+ event_names = spec['events']
95
+ #event_name = event_name.gsub(/Event$/, '').gsub(/[A-Z]/) { |m| "_#{m}" }.upcase
96
+ #event_name = event_name.gsub(/Event$/, '').sub(/./) { |m| m.upcase }
97
+ event_names.each do |event_name|
98
+ store_event_names[event_name] = entity_name
99
+ end
100
+ end
101
+ store_event_names.values.uniq.each do |entity_name|
102
+ entities.set(:event_list, entity_name, [])
103
+ end
104
+ subscriber = StoringEventSubscriber.new do |payload|
105
+ if entity_name = store_event_names[payload['name']]
106
+ entities.get(:event_list, entity_name) << payload
107
+ end
108
+ end
109
+ opts[:sdam_proc] = lambda do |client|
110
+ client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
111
+ client.subscribe(Mongo::Monitoring::CONNECTION_POOL, subscriber)
112
+ end
113
+ end
114
+
115
+ if server_api = spec.use('serverApi')
116
+ server_api = ::Utils.underscore_hash(server_api)
117
+ opts[:server_api] = server_api
118
+ end
119
+
120
+ create_client(**opts).tap do |client|
121
+ if oe = spec.use('observeEvents')
122
+ oe.each do |event|
123
+ case event
124
+ when 'commandStartedEvent', 'commandSucceededEvent', 'commandFailedEvent'
125
+ subscriber = (@subscribers[client] ||= EventSubscriber.new)
126
+ unless client.send(:monitoring).subscribers[Mongo::Monitoring::COMMAND].include?(subscriber)
127
+ client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
128
+ end
129
+ kind = event.sub('command', '').sub('Event', '').downcase.to_sym
130
+ subscriber.add_wanted_events(kind)
131
+ if ignore_events = spec.use('ignoreCommandMonitoringEvents')
132
+ subscriber.ignore_commands(ignore_events)
133
+ end
134
+ else
135
+ raise NotImplementedError, "Unknown event #{event}"
136
+ end
137
+ end
138
+ end
139
+ end
140
+ when 'database'
141
+ client = entities.get(:client, spec.use!('client'))
142
+ client.use(spec.use!('databaseName')).database
143
+ when 'collection'
144
+ database = entities.get(:database, spec.use!('database'))
145
+ # TODO verify
146
+ opts = Utils.snakeize_hash(spec.use('collectionOptions') || {})
147
+ database[spec.use!('collectionName'), opts]
148
+ when 'bucket'
149
+ database = entities.get(:database, spec.use!('database'))
150
+ database.fs
151
+ when 'session'
152
+ client = entities.get(:client, spec.use!('client'))
153
+
154
+ if smc_opts = spec.use('sessionOptions')
155
+ opts = ::Utils.underscore_hash(smc_opts)
156
+ else
157
+ opts = {}
158
+ end
159
+
160
+ client.start_session(**opts)
161
+ else
162
+ raise NotImplementedError, "Unknown type #{type}"
163
+ end
164
+ unless spec.empty?
165
+ raise NotImplementedError, "Unhandled spec keys: #{spec}"
166
+ end
167
+ entities.set(type.to_sym, id, entity)
168
+ end
169
+ end
170
+
171
+ def set_initial_data
172
+ @spec['initialData']&.each do |entity_spec|
173
+ spec = UsingHash[entity_spec]
174
+ collection = root_authorized_client.use(spec.use!('databaseName'))[spec.use!('collectionName')]
175
+ collection.drop
176
+ docs = spec.use!('documents')
177
+ if docs.any?
178
+ collection.insert_many(docs)
179
+ else
180
+ begin
181
+ collection.create
182
+ rescue Mongo::Error => e
183
+ if Mongo::Error::OperationFailure === e && (
184
+ e.code == 48 || e.message =~ /collection already exists/
185
+ )
186
+ # Already exists
187
+ else
188
+ raise
189
+ end
190
+ end
191
+ end
192
+ unless spec.empty?
193
+ raise NotImplementedError, "Unhandled spec keys: #{spec}"
194
+ end
195
+ end
196
+ end
197
+
198
+ def run
199
+ kill_sessions
200
+
201
+ test_spec = UsingHash[self.test_spec]
202
+ ops = test_spec.use!('operations')
203
+ execute_operations(ops)
204
+ unless test_spec.empty?
205
+ raise NotImplementedError, "Unhandled spec keys: #{test_spec}"
206
+ end
207
+ ensure
208
+ disable_fail_points
209
+ end
210
+
211
+ def stop!
212
+ @stop = true
213
+ end
214
+
215
+ def stop?
216
+ !!@stop
217
+ end
218
+
219
+ def cleanup
220
+ if $kill_transactions || true
221
+ kill_sessions
222
+ $kill_transactions = nil
223
+ end
224
+
225
+ entities[:client]&.each do |id, client|
226
+ client.close
227
+ end
228
+ end
229
+
230
+ private
231
+
232
+ def execute_operations(ops)
233
+ ops.each do |op|
234
+ execute_operation(op)
235
+ end
236
+ end
237
+
238
+ def execute_operation(op)
239
+ use_all(op, 'operation', op) do |op|
240
+ name = Utils.underscore(op.use!('name'))
241
+ method_name = name
242
+ if name.to_s == 'loop'
243
+ method_name = "_#{name}"
244
+ end
245
+ if expected_error = op.use('expectError')
246
+ begin
247
+ send(method_name, op)
248
+ rescue Mongo::Error, BSON::String::IllegalKey => e
249
+ if expected_error.use('isClientError')
250
+ unless BSON::String::IllegalKey === e
251
+ raise Error::ErrorMismatch, "Expected client error but got #{e}"
252
+ end
253
+ end
254
+ if code = expected_error.use('errorCode')
255
+ unless e.code == code
256
+ raise Error::ErrorMismatch, "Expected #{code} code but had #{e.code}"
257
+ end
258
+ end
259
+ if code_name = expected_error.use('errorCodeName')
260
+ unless e.code_name == code_name
261
+ raise Error::ErrorMismatch, "Expected #{code_name} code name but had #{e.code_name}"
262
+ end
263
+ end
264
+ if text = expected_error.use('errorContains')
265
+ unless e.to_s.include?(text)
266
+ raise Error::ErrorMismatch, "Expected #{text} in the message but had #{e}"
267
+ end
268
+ end
269
+ if labels = expected_error.use('errorLabelsContain')
270
+ labels.each do |label|
271
+ unless e.label?(label)
272
+ raise Error::ErrorMismatch, "Expected error to contain label #{label} but it did not"
273
+ end
274
+ end
275
+ end
276
+ if omit_labels = expected_error.use('errorLabelsOmit')
277
+ omit_labels.each do |label|
278
+ if e.label?(label)
279
+ raise Error::ErrorMismatch, "Expected error to not contain label #{label} but it did"
280
+ end
281
+ end
282
+ end
283
+ if expected_result = expected_error.use('expectResult')
284
+ assert_result_matches(e.result, expected_result)
285
+ #expected_result.clear
286
+ # Important: this must be the last branch.
287
+ elsif expected_error.use('isError')
288
+ # Nothing but we consume the key.
289
+ end
290
+ unless expected_error.empty?
291
+ raise NotImplementedError, "Unhandled keys: #{expected_error}"
292
+ end
293
+ else
294
+ raise Error::ErrorMismatch, "Expected exception but none was raised"
295
+ end
296
+ else
297
+ result = send(method_name, op)
298
+ if expected_result = op.use('expectResult')
299
+ if result.nil? && !expected_result.empty?
300
+ raise Error::ResultMismatch, "Actual result nil but expected result #{expected_result}"
301
+ elsif Array === expected_result
302
+ assert_documents_match(result, expected_result)
303
+ else
304
+ assert_result_matches(result, expected_result)
305
+ end
306
+ #expected_result.clear
307
+ end
308
+ if save_entity = op.use('saveResultAsEntity')
309
+ entities.set(:result, save_entity, result)
310
+ end
311
+ end
312
+ end
313
+ end
314
+
315
+ def use_sub(hash, key, &block)
316
+ v = hash.use!(key)
317
+ use_all(hash, key, v, &block)
318
+ end
319
+
320
+ def use_all(hash, key, v)
321
+ orig_v = v.dup
322
+ (yield v).tap do
323
+ unless v.empty?
324
+ raise NotImplementedError, "Unconsumed items for #{key}: #{v}\nOriginal hash: #{orig_v}"
325
+ end
326
+ end
327
+ end
328
+
329
+ def use_arguments(op, &block)
330
+ if op.key?('arguments')
331
+ use_sub(op, 'arguments', &block)
332
+ else
333
+ yield UsingHash.new
334
+ end
335
+ end
336
+
337
+ def disable_fail_points
338
+ if $disable_fail_points
339
+ $disable_fail_points.each do |(fail_point_command, address)|
340
+ client = ClusterTools.instance.direct_client(address,
341
+ database: 'admin')
342
+ client.command(configureFailPoint: fail_point_command['configureFailPoint'],
343
+ mode: 'off')
344
+ end
345
+ $disable_fail_points = nil
346
+ end
347
+ end
348
+
349
+ def kill_sessions
350
+ if options[:kill_sessions] != false
351
+ begin
352
+ root_authorized_client.command(
353
+ killAllSessions: [],
354
+ )
355
+ rescue Mongo::Error::OperationFailure => e
356
+ if e.code == 11601
357
+ # operation was interrupted, ignore
358
+ elsif e.code == 59
359
+ # no such command (old server), ignore
360
+ else
361
+ raise
362
+ end
363
+ end
364
+ end
365
+ end
366
+
367
+ def root_authorized_client
368
+ @root_authorized_client ||= ClientRegistry.instance.global_client('root_authorized')
369
+ end
370
+
371
+ def create_client(**opts)
372
+ args = case v = options[:client_args]
373
+ when Array
374
+ unless v.length == 2
375
+ raise NotImplementedError, 'Client args array must have two elements'
376
+ end
377
+ [v.first, v.last.dup]
378
+ when String
379
+ [v, {}]
380
+ else
381
+ [
382
+ SpecConfig.instance.addresses,
383
+ SpecConfig.instance.all_test_options,
384
+ ]
385
+ end
386
+ args.last.update(
387
+ max_read_retries: 0,
388
+ max_write_retries: 0,
389
+ ).update(opts)
390
+ Mongo::Client.new(*args)
391
+ end
392
+ end
393
+ end
@@ -0,0 +1,28 @@
1
+ module Unified
2
+
3
+ class TestGroup
4
+ def initialize(path, **opts)
5
+ if String === path
6
+ data = YAML.load(File.read(path))
7
+ else
8
+ data = path
9
+ end
10
+ @spec = BSON::ExtJSON.parse_obj(data)
11
+ @options = opts
12
+ end
13
+
14
+ attr_reader :options
15
+
16
+ def tests
17
+ reqs = @spec['runOnRequirements']
18
+
19
+ @spec.fetch('tests').map do |test|
20
+ sub = @spec.dup
21
+ sub.delete('tests')
22
+ sub['test'] = test
23
+ sub['group_runOnRequirements'] = reqs
24
+ Test.new(sub, **options)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ module Unified
2
+
3
+ class UsingHash < Hash
4
+ def use(key)
5
+ wrap(self[key]).tap do
6
+ delete(key)
7
+ end
8
+ end
9
+
10
+ def use!(key)
11
+ wrap(fetch(key)).tap do
12
+ delete(key)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def wrap(v)
19
+ case v
20
+ when Hash
21
+ self.class[v]
22
+ when Array
23
+ v.map do |subv|
24
+ wrap(subv)
25
+ end
26
+ else
27
+ v
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,96 @@
1
+ require 'runners/unified/error'
2
+ require 'runners/unified/entity_map'
3
+ require 'runners/unified/event_subscriber'
4
+ require 'runners/unified/test'
5
+ require 'runners/unified/test_group'
6
+ require 'runners/unified/using_hash'
7
+
8
+ def define_unified_spec_tests(base_path, paths, expect_failure: false)
9
+ paths.each do |path|
10
+ basename = path[base_path.length+1...path.length]
11
+ context basename do
12
+ group = Unified::TestGroup.new(path)
13
+
14
+ if basename =~ /retryable|transaction/
15
+ require_wired_tiger
16
+ end
17
+
18
+ group.tests.each do |test|
19
+ context test.description do
20
+
21
+ if test.skip?
22
+ before do
23
+ skip test.skip_reason
24
+ end
25
+ end
26
+
27
+ before(:all) do
28
+ if SpecConfig.instance.retry_reads == false
29
+ skip "Tests are not applicable when legacy read retries are used"
30
+ end
31
+ if SpecConfig.instance.retry_writes == false
32
+ skip "Tests are not applicable when legacy write retries are used"
33
+ end
34
+
35
+ if ClusterConfig.instance.topology == :sharded
36
+ if test.require_multiple_mongoses? && SpecConfig.instance.addresses.length == 1
37
+ skip "Test requires multiple mongoses"
38
+ elsif test.require_single_mongos? && SpecConfig.instance.addresses.length > 1
39
+ # Many transaction spec tests that do not specifically deal with
40
+ # sharded transactions fail when run against a multi-mongos cluster
41
+ skip "Test requires single mongos"
42
+ end
43
+ end
44
+ end
45
+
46
+ if expect_failure
47
+ it 'fails as expected' do
48
+ if test.group_reqs
49
+ unless test.group_reqs.any? { |r| r.satisfied? }
50
+ skip "Group requirements not satisfied"
51
+ end
52
+ end
53
+ if test.reqs
54
+ unless test.reqs.any? { |r| r.satisfied? }
55
+ skip "Requirements not satisfied"
56
+ end
57
+ end
58
+ begin
59
+ test.create_entities
60
+ test.set_initial_data
61
+ lambda do
62
+ test.run
63
+ test.assert_outcome
64
+ test.assert_events
65
+ # HACK: other errors are possible and likely will need to
66
+ # be added here later as the tests evolve.
67
+ end.should raise_error(Mongo::Error::OperationFailure)
68
+ ensure
69
+ test.cleanup
70
+ end
71
+ end
72
+ else
73
+ it 'passes' do
74
+ if test.group_reqs
75
+ unless test.group_reqs.any? { |r| r.satisfied? }
76
+ skip "Group requirements not satisfied"
77
+ end
78
+ end
79
+ if test.reqs
80
+ unless test.reqs.any? { |r| r.satisfied? }
81
+ skip "Requirements not satisfied"
82
+ end
83
+ end
84
+ test.create_entities
85
+ test.set_initial_data
86
+ test.run
87
+ test.assert_outcome
88
+ test.assert_events
89
+ test.cleanup
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,6 +1,3 @@
1
- # frozen_string_literal: true
2
- # encoding: utf-8
3
-
4
1
  # ClusterConfig requires ClientRegistry class provided by the host project.
5
2
 
6
3
  require 'singleton'
@@ -1,6 +1,3 @@
1
- # frozen_string_literal: true
2
- # encoding: utf-8
3
-
4
1
  require 'optparse'
5
2
  require 'erb'
6
3
  autoload :Dotenv, 'dotenv'
@@ -171,21 +171,5 @@ module Mrss
171
171
  end
172
172
  end
173
173
  end
174
-
175
- def require_active_support
176
- before(:all) do
177
- if !SpecConfig.instance.active_support?
178
- skip 'This test requires ActiveSupport; set WITH_ACTIVE_SUPPORT=1 in environment'
179
- end
180
- end
181
- end
182
-
183
- def no_active_support
184
- before(:all) do
185
- if SpecConfig.instance.active_support?
186
- skip 'This test requires no ActiveSupport; unset WITH_ACTIVE_SUPPORT in environment'
187
- end
188
- end
189
- end
190
174
  end
191
175
  end
@@ -1,6 +1,3 @@
1
- # frozen_string_literal: true
2
- # encoding: utf-8
3
-
4
1
  autoload :JSON, 'json'
5
2
  require 'open-uri'
6
3
 
@@ -1,6 +1,3 @@
1
- # frozen_string_literal: true
2
- # encoding: utf-8
3
-
4
1
  autoload :JSON, 'json'
5
2
  autoload :FileUtils, 'fileutils'
6
3
  autoload :Find, 'find'
@@ -163,7 +163,7 @@ calculate_server_args() {
163
163
  args="$args --replicaset --name ruby-driver-rs --nodes 2 --arbiter"
164
164
  export HAVE_ARBITER=1
165
165
  elif test "$TOPOLOGY" = sharded-cluster; then
166
- args="$args --replicaset --nodes 2 --sharded 1 --name ruby-driver-rs"
166
+ args="$args --replicaset --nodes 1 --sharded 1 --name ruby-driver-rs"
167
167
  if test -z "$SINGLE_MONGOS"; then
168
168
  args="$args --mongos 2"
169
169
  fi
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,13 @@
1
1
  require 'lite_spec_helper'
2
2
 
3
3
  require 'mrss/constraints'
4
+ require 'mrss/cluster_config'
5
+
6
+ ClusterConfig = Mrss::ClusterConfig
7
+
4
8
  require 'support/constraints'
5
9
  require 'support/authorization'
6
10
  require 'support/primary_socket'
7
- require 'support/cluster_config'
8
11
  require 'support/cluster_tools'
9
12
  require 'rspec/retry'
10
13
  require 'support/monitoring_ext'
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ require 'runners/unified'
4
+
5
+ base = "#{CURRENT_PATH}/spec_tests/data/crud_unified"
6
+ CRUD_UNIFIED_TESTS = Dir.glob("#{base}/**/*.yml").sort
7
+
8
+ describe 'CRUD unified spec tests' do
9
+ define_unified_spec_tests(base, CRUD_UNIFIED_TESTS)
10
+ end
@@ -371,7 +371,6 @@ tests:
371
371
  db: *database_name
372
372
  coll: *collection_name
373
373
  updateDescription:
374
- removedFields: []
375
374
  updatedFields:
376
375
  x:
377
376
  $numberInt: "2"