Skip to content

Commit c303ab6

Browse files
RUBY-3242 Add spec tests for runCommand (mongodb#2776)
* RUBY-3242 Add spec tests for runCommand * Fix runner * Add a bit of a comment
1 parent 5598035 commit c303ab6

File tree

3 files changed

+348
-3
lines changed

3 files changed

+348
-3
lines changed

spec/runners/unified/assertions.rb

+16-3
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,11 @@ def assert_matches(actual, expected, msg)
251251
end
252252
when Hash
253253
if expected.keys == %w($$unsetOrMatches) && expected.values.first.keys == %w(insertedId)
254-
actual_v = actual.inserted_id
254+
actual_v = get_actual_value(actual, 'inserted_id')
255255
expected_v = expected.values.first.values.first
256256
assert_value_matches(actual_v, expected_v, 'inserted_id')
257257
elsif expected.keys == %w(insertedId)
258-
actual_v = actual.inserted_id
258+
actual_v = get_actual_value(actual, 'inserted_id')
259259
expected_v = expected.values.first
260260
assert_value_matches(actual_v, expected_v, 'inserted_id')
261261
else
@@ -270,7 +270,7 @@ def assert_matches(actual, expected, msg)
270270
if k.start_with?('$$')
271271
assert_value_matches(actual, expected, k)
272272
else
273-
actual_v = actual[k]
273+
actual_v = get_actual_value(actual, k)
274274
if Hash === expected_v && expected_v.length == 1 && expected_v.keys.first.start_with?('$$')
275275
assert_value_matches(actual_v, expected_v, k)
276276
else
@@ -290,6 +290,19 @@ def assert_matches(actual, expected, msg)
290290
end
291291
end
292292

293+
# The actual value may be of different types depending on the operation.
294+
# In order to avoid having to write a lot of code to handle the different
295+
# types, we use this method to get the actual value.
296+
def get_actual_value(actual, key)
297+
if Hash === actual
298+
actual[key]
299+
elsif Mongo::Operation::Result === actual && !actual.respond_to?(key.to_sym)
300+
actual.documents.first[key]
301+
else
302+
actual.send(key)
303+
end
304+
end
305+
293306
def assert_type(object, type)
294307
ok = [*type].reduce(false) { |acc, x| acc || type_matches?(object, x) }
295308

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
description: runCommand
2+
3+
schemaVersion: "1.3"
4+
5+
createEntities:
6+
- client:
7+
id: &client client
8+
useMultipleMongoses: false
9+
observeEvents: [commandStartedEvent]
10+
- database:
11+
id: &db db
12+
client: *client
13+
databaseName: *db
14+
- collection:
15+
id: &collection collection
16+
database: *db
17+
collectionName: *collection
18+
- database:
19+
id: &dbWithRC dbWithRC
20+
client: *client
21+
databaseName: *dbWithRC
22+
databaseOptions:
23+
readConcern: { level: 'local' }
24+
- database:
25+
id: &dbWithWC dbWithWC
26+
client: *client
27+
databaseName: *dbWithWC
28+
databaseOptions:
29+
writeConcern: { w: 0 }
30+
- session:
31+
id: &session session
32+
client: *client
33+
# Stable API test
34+
- client:
35+
id: &clientWithStableApi clientWithStableApi
36+
observeEvents: [commandStartedEvent]
37+
serverApi:
38+
version: "1"
39+
strict: true
40+
- database:
41+
id: &dbWithStableApi dbWithStableApi
42+
client: *clientWithStableApi
43+
databaseName: *dbWithStableApi
44+
45+
initialData:
46+
- collectionName: *collection
47+
databaseName: *db
48+
documents: []
49+
50+
tests:
51+
- description: always attaches $db and implicit lsid to given command and omits default readPreference
52+
operations:
53+
- name: runCommand
54+
object: *db
55+
arguments:
56+
commandName: ping
57+
command: { ping: 1 }
58+
expectResult: { ok: 1 }
59+
expectEvents:
60+
- client: *client
61+
events:
62+
- commandStartedEvent:
63+
command:
64+
ping: 1
65+
$db: *db
66+
lsid: { $$exists: true }
67+
$readPreference: { $$exists: false }
68+
commandName: ping
69+
70+
- description: always gossips the $clusterTime on the sent command
71+
runOnRequirements:
72+
# Only replicasets and sharded clusters have a $clusterTime
73+
- topologies: [ replicaset, sharded ]
74+
operations:
75+
# We have to run one command to obtain a clusterTime to gossip
76+
- name: runCommand
77+
object: *db
78+
arguments:
79+
commandName: ping
80+
command: { ping: 1 }
81+
expectResult: { ok: 1 }
82+
- name: runCommand
83+
object: *db
84+
arguments:
85+
commandName: ping
86+
command: { ping: 1 }
87+
expectResult: { ok: 1 }
88+
expectEvents:
89+
- client: *client
90+
events:
91+
- commandStartedEvent:
92+
commandName: ping
93+
# Only check the shape of the second ping which should have the $clusterTime received from the first operation
94+
- commandStartedEvent:
95+
command:
96+
ping: 1
97+
$clusterTime: { $$exists: true }
98+
commandName: ping
99+
100+
- description: attaches the provided session lsid to given command
101+
operations:
102+
- name: runCommand
103+
object: *db
104+
arguments:
105+
commandName: ping
106+
command: { ping: 1 }
107+
session: *session
108+
expectResult: { ok: 1 }
109+
expectEvents:
110+
- client: *client
111+
events:
112+
- commandStartedEvent:
113+
command:
114+
ping: 1
115+
lsid: { $$sessionLsid: *session }
116+
$db: *db
117+
commandName: ping
118+
119+
- description: attaches the provided $readPreference to given command
120+
runOnRequirements:
121+
# Exclude single topology, which is most likely a standalone server
122+
- topologies: [ replicaset, load-balanced, sharded ]
123+
operations:
124+
- name: runCommand
125+
object: *db
126+
arguments:
127+
commandName: ping
128+
command: { ping: 1 }
129+
readPreference: &readPreference { mode: 'nearest' }
130+
expectResult: { ok: 1 }
131+
expectEvents:
132+
- client: *client
133+
events:
134+
- commandStartedEvent:
135+
command:
136+
ping: 1
137+
$readPreference: *readPreference
138+
$db: *db
139+
commandName: ping
140+
141+
- description: does not attach $readPreference to given command on standalone
142+
runOnRequirements:
143+
# This test assumes that the single topology contains a standalone server;
144+
# however, it is possible for a single topology to contain a direct
145+
# connection to another server type.
146+
# See: https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#topology-type-single
147+
- topologies: [ single ]
148+
operations:
149+
- name: runCommand
150+
object: *db
151+
arguments:
152+
commandName: ping
153+
command: { ping: 1 }
154+
readPreference: { mode: 'nearest' }
155+
expectResult: { ok: 1 }
156+
expectEvents:
157+
- client: *client
158+
events:
159+
- commandStartedEvent:
160+
command:
161+
ping: 1
162+
$readPreference: { $$exists: false }
163+
$db: *db
164+
commandName: ping
165+
166+
- description: does not attach primary $readPreference to given command
167+
operations:
168+
- name: runCommand
169+
object: *db
170+
arguments:
171+
commandName: ping
172+
command: { ping: 1 }
173+
readPreference: { mode: 'primary' }
174+
expectResult: { ok: 1 }
175+
expectEvents:
176+
- client: *client
177+
events:
178+
- commandStartedEvent:
179+
command:
180+
ping: 1
181+
$readPreference: { $$exists: false }
182+
$db: *db
183+
commandName: ping
184+
185+
- description: does not inherit readConcern specified at the db level
186+
operations:
187+
- name: runCommand
188+
object: *dbWithRC
189+
# Test with a command that supports a readConcern option.
190+
# expectResult is intentionally omitted because some drivers
191+
# may automatically convert command responses into cursors.
192+
arguments:
193+
commandName: aggregate
194+
command: { aggregate: *collection, pipeline: [], cursor: {} }
195+
expectEvents:
196+
- client: *client
197+
events:
198+
- commandStartedEvent:
199+
command:
200+
aggregate: *collection
201+
readConcern: { $$exists: false }
202+
$db: *dbWithRC
203+
commandName: aggregate
204+
205+
- description: does not inherit writeConcern specified at the db level
206+
operations:
207+
- name: runCommand
208+
object: *dbWithWC
209+
arguments:
210+
commandName: insert
211+
command:
212+
insert: *collection
213+
documents: [ { foo: 'bar' } ]
214+
ordered: true
215+
expectResult: { ok: 1 }
216+
expectEvents:
217+
- client: *client
218+
events:
219+
- commandStartedEvent:
220+
command:
221+
insert: *collection
222+
writeConcern: { $$exists: false }
223+
$db: *dbWithWC
224+
commandName: insert
225+
226+
- description: does not retry retryable errors on given command
227+
runOnRequirements:
228+
- minServerVersion: "4.2"
229+
operations:
230+
- name: failPoint
231+
object: testRunner
232+
arguments:
233+
client: *client
234+
failPoint:
235+
configureFailPoint: failCommand
236+
mode: { times: 1 }
237+
data:
238+
failCommands: [ping]
239+
closeConnection: true
240+
- name: runCommand
241+
object: *db
242+
arguments:
243+
commandName: ping
244+
command: { ping: 1 }
245+
expectError:
246+
isClientError: true
247+
248+
- description: attaches transaction fields to given command
249+
runOnRequirements:
250+
- minServerVersion: "4.0"
251+
topologies: [ replicaset ]
252+
- minServerVersion: "4.2"
253+
topologies: [ sharded, load-balanced ]
254+
operations:
255+
- name: withTransaction
256+
object: *session
257+
arguments:
258+
callback:
259+
- name: runCommand
260+
object: *db
261+
arguments:
262+
session: *session
263+
commandName: insert
264+
command:
265+
insert: *collection
266+
documents: [ { foo: 'transaction' } ]
267+
ordered: true
268+
expectResult: { $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } }
269+
expectEvents:
270+
- client: *client
271+
events:
272+
- commandStartedEvent:
273+
command:
274+
insert: *collection
275+
documents: [ { foo: 'transaction' } ]
276+
ordered: true
277+
lsid: { $$sessionLsid: *session }
278+
txnNumber: 1
279+
startTransaction: true
280+
autocommit: false
281+
# omitted fields
282+
readConcern: { $$exists: false }
283+
writeConcern: { $$exists: false }
284+
commandName: insert
285+
databaseName: *db
286+
- commandStartedEvent:
287+
command:
288+
commitTransaction: 1
289+
lsid: { $$sessionLsid: *session }
290+
txnNumber: 1
291+
autocommit: false
292+
# omitted fields
293+
writeConcern: { $$exists: false }
294+
readConcern: { $$exists: false }
295+
commandName: commitTransaction
296+
databaseName: admin
297+
298+
- description: attaches apiVersion fields to given command when stableApi is configured on the client
299+
runOnRequirements:
300+
- minServerVersion: "5.0"
301+
operations:
302+
- name: runCommand
303+
object: *dbWithStableApi
304+
arguments:
305+
commandName: ping
306+
command:
307+
ping: 1
308+
expectResult: { ok: 1 }
309+
expectEvents:
310+
- client: *clientWithStableApi
311+
events:
312+
- commandStartedEvent:
313+
command:
314+
ping: 1
315+
$db: *dbWithStableApi
316+
apiVersion: "1"
317+
apiStrict: true
318+
apiDeprecationErrors: { $$unsetOrMatches: false }
319+
commandName: ping
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# frozen_string_literal: true
2+
# rubocop:todo all
3+
4+
require 'spec_helper'
5+
6+
require 'runners/unified'
7+
8+
base = "#{CURRENT_PATH}/spec_tests/data/run_command_unified"
9+
RUN_COMMAND_UNIFIED_TESTS = Dir.glob("#{base}/**/*.yml").sort
10+
11+
describe 'runCommand unified spec tests' do
12+
define_unified_spec_tests(base, RUN_COMMAND_UNIFIED_TESTS)
13+
end

0 commit comments

Comments
 (0)