Skip to content

Commit 5e31917

Browse files
committed
RUBY-3566 Sync retryable writes unified spec tests from specifications repo
Adds 22 new YAML spec files and updates 3 existing ones from the MongoDB specifications repository (DRIVERS-943), converting prose tests for retryable write command construction to unified spec format. Also fixes the unified spec runner to handle returnDocument in findOneAndReplace, adds sort support to findOneAndReplace and findOneAndDelete, skips unsupported clientBulkWrite operations, and handles camelCase-to-snake_case key mapping in result assertions.
1 parent a8d41fc commit 5e31917

31 files changed

Lines changed: 3878 additions & 8 deletions

spec/runners/unified/assertions.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,13 @@ def assert_matches(actual, expected, msg)
296296
def get_actual_value(actual, key)
297297
if actual.is_a?(Hash)
298298
actual[key]
299-
elsif actual.is_a?(Mongo::Operation::Result) && !actual.respond_to?(key.to_sym)
300-
actual.documents.first[key]
299+
elsif actual.is_a?(Mongo::Operation::Result)
300+
snake_key = Utils.underscore(key)
301+
if actual.respond_to?(snake_key)
302+
actual.send(snake_key)
303+
else
304+
actual.documents.first[key]
305+
end
301306
else
302307
actual.send(key)
303308
end

spec/runners/unified/crud_operations.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,13 @@ def find_one_and_replace(op)
121121
let: args.use('let'),
122122
comment: args.use('comment'),
123123
hint: args.use('hint'),
124+
sort: args.use('sort'),
124125
timeout_ms: args.use('timeoutMS'),
125126
max_time_ms: args.use('maxTimeMS')
126127
}
128+
if return_document = args.use('returnDocument')
129+
opts[:return_document] = return_document.downcase.to_sym
130+
end
127131
if session = args.use('session')
128132
opts[:session] = entities.get(:session, session)
129133
end
@@ -139,6 +143,7 @@ def find_one_and_delete(op)
139143
let: args.use('let'),
140144
comment: args.use('comment'),
141145
hint: args.use('hint'),
146+
sort: args.use('sort'),
142147
timeout_ms: args.use('timeoutMS'),
143148
max_time_ms: args.use('maxTimeMS')
144149
}

spec/runners/unified/test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ def execute_operation(op, propagate_errors: false)
471471
method_name = name
472472
method_name = "_#{name}" if name.to_s == 'loop'
473473

474-
skip "Mongo Ruby Driver does not support #{name}" if %w[modify_collection list_index_names].include?(name.to_s)
474+
skip "Mongo Ruby Driver does not support #{name}" if %w[modify_collection list_index_names client_bulk_write].include?(name.to_s)
475475

476476
if expected_error = op.use('expectError')
477477
begin
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
description: "aggregate with $out/$merge does not set txnNumber"
2+
3+
schemaVersion: "1.4"
4+
5+
runOnRequirements:
6+
- minServerVersion: "3.6"
7+
topologies:
8+
- replicaset
9+
- sharded
10+
- load-balanced
11+
12+
createEntities:
13+
- client:
14+
id: &client0 client0
15+
observeEvents: [ commandStartedEvent ]
16+
- database:
17+
id: &database0 database0
18+
client: *client0
19+
databaseName: &database0Name retryable-writes-tests
20+
- collection:
21+
id: &collection0 collection0
22+
database: *database0
23+
collectionName: &collection0Name coll0
24+
25+
initialData:
26+
# The output collection must already exist for $merge on a sharded cluster
27+
- collectionName: &mergeCollection mergeCollection
28+
databaseName: *database0Name
29+
documents: []
30+
31+
tests:
32+
- description: "aggregate with $out does not set txnNumber"
33+
runOnRequirements:
34+
- serverless: forbid # $out is not supported on serverless
35+
operations:
36+
- object: *collection0
37+
name: aggregate
38+
arguments:
39+
pipeline:
40+
- { $sort: { x: 1 } }
41+
- { $match: { _id: { $gt: 1 } } }
42+
- { $out: outCollection }
43+
expectEvents:
44+
- client: client0
45+
events:
46+
- commandStartedEvent:
47+
commandName: aggregate
48+
command:
49+
txnNumber: { $$exists: false }
50+
- description: "aggregate with $merge does not set txnNumber"
51+
runOnRequirements:
52+
- minServerVersion: "4.1.11"
53+
operations:
54+
- object: *collection0
55+
name: aggregate
56+
arguments:
57+
pipeline:
58+
- { $sort: { x: 1 } }
59+
- { $match: { _id: { $gt: 1 } } }
60+
- { $merge: { into: *mergeCollection } }
61+
expectEvents:
62+
- client: client0
63+
events:
64+
- commandStartedEvent:
65+
commandName: aggregate
66+
command:
67+
txnNumber: { $$exists: false }
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
description: bulkWrite-errorLabels
2+
3+
schemaVersion: '1.3'
4+
5+
runOnRequirements:
6+
-
7+
minServerVersion: 4.3.1 # failCommand errorLabels option
8+
topologies: [ replicaset, sharded, load-balanced ]
9+
10+
createEntities:
11+
-
12+
client:
13+
id: &client0 client0
14+
useMultipleMongoses: false
15+
-
16+
database:
17+
id: &database0 database0
18+
client: *client0
19+
databaseName: &database_name retryable-writes-tests
20+
-
21+
collection:
22+
id: &collection0 collection0
23+
database: *database0
24+
collectionName: &collection_name coll
25+
26+
initialData:
27+
-
28+
collectionName: *collection_name
29+
databaseName: *database_name
30+
documents:
31+
- { _id: 1, x: 11 }
32+
- { _id: 2, x: 22 }
33+
34+
tests:
35+
-
36+
description: 'BulkWrite succeeds with RetryableWriteError from server'
37+
operations:
38+
-
39+
name: failPoint
40+
object: testRunner
41+
arguments:
42+
client: *client0
43+
failPoint:
44+
configureFailPoint: failCommand
45+
mode: { times: 1 }
46+
data:
47+
failCommands: [ update ]
48+
errorCode: 112 # WriteConflict, not a retryable error code
49+
# Override server behavior: send RetryableWriteError label with non-retryable error code
50+
errorLabels:
51+
- RetryableWriteError
52+
-
53+
object: *collection0
54+
name: bulkWrite
55+
arguments:
56+
requests:
57+
-
58+
deleteOne:
59+
filter: { _id: 1 }
60+
-
61+
insertOne:
62+
document: { _id: 3, x: 33 }
63+
-
64+
updateOne:
65+
filter: { _id: 2 }
66+
update: { $inc: { x: 1 } }
67+
ordered: true
68+
# Driver retries operation and it succeeds
69+
expectResult:
70+
deletedCount: 1
71+
insertedCount: 1
72+
insertedIds: { $$unsetOrMatches: { '1': 3 } }
73+
matchedCount: 1
74+
modifiedCount: 1
75+
upsertedCount: 0
76+
upsertedIds: { }
77+
outcome:
78+
-
79+
collectionName: *collection_name
80+
databaseName: *database_name
81+
documents:
82+
- { _id: 2, x: 23 }
83+
- { _id: 3, x: 33 }
84+
-
85+
description: 'BulkWrite fails if server does not return RetryableWriteError'
86+
operations:
87+
-
88+
name: failPoint
89+
object: testRunner
90+
arguments:
91+
client: *client0
92+
failPoint:
93+
configureFailPoint: failCommand
94+
mode: { times: 1 }
95+
data:
96+
failCommands: [ update ]
97+
errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code
98+
errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code
99+
-
100+
object: *collection0
101+
name: bulkWrite
102+
arguments:
103+
requests:
104+
-
105+
deleteOne:
106+
filter: { _id: 1 }
107+
-
108+
insertOne:
109+
document: { _id: 3, x: 33 }
110+
-
111+
updateOne:
112+
filter: { _id: 2 }
113+
update: { $inc: { x: 1 } }
114+
ordered: true
115+
# Driver does not retry operation because there was no RetryableWriteError label on response
116+
expectError:
117+
isError: true
118+
errorLabelsOmit:
119+
- RetryableWriteError
120+
outcome:
121+
-
122+
collectionName: *collection_name
123+
databaseName: *database_name
124+
documents:
125+
- { _id: 2, x: 22 }
126+
- { _id: 3, x: 33 }
127+
-
128+
description: 'BulkWrite succeeds after PrimarySteppedDown'
129+
operations:
130+
-
131+
name: failPoint
132+
object: testRunner
133+
arguments:
134+
client: *client0
135+
failPoint:
136+
configureFailPoint: failCommand
137+
mode: { times: 1 }
138+
data:
139+
failCommands: [ update ]
140+
errorCode: 189
141+
errorLabels:
142+
- RetryableWriteError
143+
-
144+
object: *collection0
145+
name: bulkWrite
146+
arguments:
147+
requests:
148+
-
149+
deleteOne:
150+
filter: { _id: 1 }
151+
-
152+
insertOne:
153+
document: { _id: 3, x: 33 }
154+
-
155+
updateOne:
156+
filter: { _id: 2 }
157+
update: { $inc: { x: 1 } }
158+
ordered: true
159+
expectResult:
160+
deletedCount: 1
161+
insertedCount: 1
162+
insertedIds: { $$unsetOrMatches: { '1': 3 } }
163+
matchedCount: 1
164+
modifiedCount: 1
165+
upsertedCount: 0
166+
upsertedIds: { }
167+
outcome:
168+
-
169+
collectionName: *collection_name
170+
databaseName: *database_name
171+
documents:
172+
- { _id: 2, x: 23 }
173+
- { _id: 3, x: 33 }
174+
-
175+
description: 'BulkWrite succeeds after WriteConcernError ShutdownInProgress'
176+
operations:
177+
-
178+
name: failPoint
179+
object: testRunner
180+
arguments:
181+
client: *client0
182+
failPoint:
183+
configureFailPoint: failCommand
184+
mode: { times: 1 }
185+
data:
186+
failCommands: [ insert ]
187+
errorLabels:
188+
- RetryableWriteError
189+
writeConcernError:
190+
code: 91
191+
errmsg: 'Replication is being shut down'
192+
-
193+
object: *collection0
194+
name: bulkWrite
195+
arguments:
196+
requests:
197+
-
198+
deleteOne:
199+
filter: { _id: 1 }
200+
-
201+
insertOne:
202+
document: { _id: 3, x: 33 }
203+
-
204+
updateOne:
205+
filter: { _id: 2 }
206+
update: { $inc: { x: 1 } }
207+
ordered: true
208+
expectResult:
209+
deletedCount: 1
210+
insertedCount: 1
211+
insertedIds: { $$unsetOrMatches: { '1': 3 } }
212+
matchedCount: 1
213+
modifiedCount: 1
214+
upsertedCount: 0
215+
upsertedIds: { }
216+
outcome:
217+
-
218+
collectionName: *collection_name
219+
databaseName: *database_name
220+
documents:
221+
- { _id: 2, x: 23 }
222+
- { _id: 3, x: 33 }

0 commit comments

Comments
 (0)