@@ -41,14 +41,6 @@ extension XCTestExpectation {
41
41
}
42
42
43
43
final class JobsTests : XCTestCase {
44
- func wait( for expectations: [ XCTestExpectation ] , timeout: TimeInterval ) async {
45
- #if (os(Linux) && swift(<5.9)) || swift(<5.8)
46
- super. wait ( for: expectations, timeout: timeout)
47
- #else
48
- await fulfillment ( of: expectations, timeout: timeout)
49
- #endif
50
- }
51
-
52
44
func createJobQueue(
53
45
numWorkers: Int ,
54
46
configuration: PostgresJobQueue . Configuration = . init( ) ,
@@ -157,57 +149,61 @@ final class JobsTests: XCTestCase {
157
149
}
158
150
159
151
func testBasic( ) async throws {
152
+ struct TestParameters : JobParameters {
153
+ static let jobName = " testBasic "
154
+ let value : Int
155
+ }
160
156
let expectation = XCTestExpectation ( description: " TestJob.execute was called " , expectedFulfillmentCount: 10 )
161
- let jobIdentifer = JobIdentifier < Int > ( #function)
162
157
try await self . testJobQueue ( numWorkers: 1 ) { jobQueue in
163
- jobQueue. registerJob ( id : jobIdentifer ) { parameters, context in
164
- context. logger. info ( " Parameters= \( parameters) " )
158
+ jobQueue. registerJob ( parameters : TestParameters . self ) { parameters, context in
159
+ context. logger. info ( " Parameters= \( parameters. value ) " )
165
160
try await Task . sleep ( for: . milliseconds( Int . random ( in: 10 ..< 50 ) ) )
166
161
expectation. fulfill ( )
167
162
}
168
- try await jobQueue. push ( id : jobIdentifer , parameters : 1 )
169
- try await jobQueue. push ( id : jobIdentifer , parameters : 2 )
170
- try await jobQueue. push ( id : jobIdentifer , parameters : 3 )
171
- try await jobQueue. push ( id : jobIdentifer , parameters : 4 )
172
- try await jobQueue. push ( id : jobIdentifer , parameters : 5 )
173
- try await jobQueue. push ( id : jobIdentifer , parameters : 6 )
174
- try await jobQueue. push ( id : jobIdentifer , parameters : 7 )
175
- try await jobQueue. push ( id : jobIdentifer , parameters : 8 )
176
- try await jobQueue. push ( id : jobIdentifer , parameters : 9 )
177
- try await jobQueue. push ( id : jobIdentifer , parameters : 10 )
178
-
179
- await self . wait ( for : [ expectation] , timeout: 5 )
163
+ try await jobQueue. push ( TestParameters ( value : 1 ) )
164
+ try await jobQueue. push ( TestParameters ( value : 2 ) )
165
+ try await jobQueue. push ( TestParameters ( value : 3 ) )
166
+ try await jobQueue. push ( TestParameters ( value : 4 ) )
167
+ try await jobQueue. push ( TestParameters ( value : 5 ) )
168
+ try await jobQueue. push ( TestParameters ( value : 6 ) )
169
+ try await jobQueue. push ( TestParameters ( value : 7 ) )
170
+ try await jobQueue. push ( TestParameters ( value : 8 ) )
171
+ try await jobQueue. push ( TestParameters ( value : 9 ) )
172
+ try await jobQueue. push ( TestParameters ( value : 10 ) )
173
+
174
+ await fulfillment ( of : [ expectation] , timeout: 5 )
180
175
}
181
176
}
182
177
183
178
func testDelayedJobs( ) async throws {
184
- let jobIdentifer = JobIdentifier < Int > ( #function)
185
- let jobIdentifer2 = JobIdentifier < Int > ( #function)
179
+ struct TestParameters : JobParameters {
180
+ static let jobName = " testDelayedJobs "
181
+ let value : Int
182
+ }
186
183
let expectation = XCTestExpectation ( description: " TestJob.execute was called " , expectedFulfillmentCount: 2 )
187
184
let jobExecutionSequence : NIOLockedValueBox < [ Int ] > = . init( [ ] )
188
185
189
186
try await self . testJobQueue ( numWorkers: 1 ) { jobQueue in
190
- jobQueue. registerJob ( id : jobIdentifer ) { parameters, context in
191
- context. logger. info ( " Parameters= \( parameters) " )
187
+ jobQueue. registerJob ( parameters : TestParameters . self ) { parameters, context in
188
+ context. logger. info ( " Parameters= \( parameters. value ) " )
192
189
jobExecutionSequence. withLockedValue {
193
- $0. append ( parameters)
190
+ $0. append ( parameters. value )
194
191
}
195
192
try await Task . sleep ( for: . milliseconds( Int . random ( in: 10 ..< 50 ) ) )
196
193
expectation. fulfill ( )
197
194
}
198
195
try await jobQueue. push (
199
- id: jobIdentifer,
200
- parameters: 1 ,
196
+ TestParameters ( value: 1 ) ,
201
197
options: . init(
202
198
delayUntil: Date . now. addingTimeInterval ( 1 )
203
199
)
204
200
)
205
- try await jobQueue. push ( id : jobIdentifer2 , parameters : 5 )
201
+ try await jobQueue. push ( TestParameters ( value : 5 ) )
206
202
207
203
let processingJobs = try await jobQueue. queue. getJobs ( withStatus: . pending)
208
204
XCTAssertEqual ( processingJobs. count, 2 )
209
205
210
- await self . wait ( for : [ expectation] , timeout: 10 )
206
+ await fulfillment ( of : [ expectation] , timeout: 10 )
211
207
212
208
let pendingJobs = try await jobQueue. queue. getJobs ( withStatus: . pending)
213
209
XCTAssertEqual ( pendingJobs. count, 0 )
@@ -216,13 +212,16 @@ final class JobsTests: XCTestCase {
216
212
}
217
213
218
214
func testMultipleWorkers( ) async throws {
219
- let jobIdentifer = JobIdentifier < Int > ( #function)
215
+ struct TestParameters : JobParameters {
216
+ static let jobName = " testMultipleWorkers "
217
+ let value : Int
218
+ }
220
219
let runningJobCounter = ManagedAtomic ( 0 )
221
220
let maxRunningJobCounter = ManagedAtomic ( 0 )
222
221
let expectation = XCTestExpectation ( description: " TestJob.execute was called " , expectedFulfillmentCount: 10 )
223
222
224
223
try await self . testJobQueue ( numWorkers: 4 ) { jobQueue in
225
- jobQueue. registerJob ( id : jobIdentifer ) { parameters, context in
224
+ jobQueue. registerJob ( parameters : TestParameters . self ) { parameters, context in
226
225
let runningJobs = runningJobCounter. wrappingIncrementThenLoad ( by: 1 , ordering: . relaxed)
227
226
if runningJobs > maxRunningJobCounter. load ( ordering: . relaxed) {
228
227
maxRunningJobCounter. store ( runningJobs, ordering: . relaxed)
@@ -233,36 +232,38 @@ final class JobsTests: XCTestCase {
233
232
runningJobCounter. wrappingDecrement ( by: 1 , ordering: . relaxed)
234
233
}
235
234
236
- try await jobQueue. push ( id : jobIdentifer , parameters : 1 )
237
- try await jobQueue. push ( id : jobIdentifer , parameters : 2 )
238
- try await jobQueue. push ( id : jobIdentifer , parameters : 3 )
239
- try await jobQueue. push ( id : jobIdentifer , parameters : 4 )
240
- try await jobQueue. push ( id : jobIdentifer , parameters : 5 )
241
- try await jobQueue. push ( id : jobIdentifer , parameters : 6 )
242
- try await jobQueue. push ( id : jobIdentifer , parameters : 7 )
243
- try await jobQueue. push ( id : jobIdentifer , parameters : 8 )
244
- try await jobQueue. push ( id : jobIdentifer , parameters : 9 )
245
- try await jobQueue. push ( id : jobIdentifer , parameters : 10 )
235
+ try await jobQueue. push ( TestParameters ( value : 1 ) )
236
+ try await jobQueue. push ( TestParameters ( value : 2 ) )
237
+ try await jobQueue. push ( TestParameters ( value : 3 ) )
238
+ try await jobQueue. push ( TestParameters ( value : 4 ) )
239
+ try await jobQueue. push ( TestParameters ( value : 5 ) )
240
+ try await jobQueue. push ( TestParameters ( value : 6 ) )
241
+ try await jobQueue. push ( TestParameters ( value : 7 ) )
242
+ try await jobQueue. push ( TestParameters ( value : 8 ) )
243
+ try await jobQueue. push ( TestParameters ( value : 9 ) )
244
+ try await jobQueue. push ( TestParameters ( value : 10 ) )
246
245
247
- await self . wait ( for : [ expectation] , timeout: 5 )
246
+ await fulfillment ( of : [ expectation] , timeout: 5 )
248
247
249
248
XCTAssertGreaterThan ( maxRunningJobCounter. load ( ordering: . relaxed) , 1 )
250
249
XCTAssertLessThanOrEqual ( maxRunningJobCounter. load ( ordering: . relaxed) , 4 )
251
250
}
252
251
}
253
252
254
253
func testErrorRetryCount( ) async throws {
255
- let jobIdentifer = JobIdentifier < Int > ( #function)
254
+ struct TestParameters : JobParameters {
255
+ static let jobName = " testErrorRetryCount "
256
+ }
256
257
let expectation = XCTestExpectation ( description: " TestJob.execute was called " , expectedFulfillmentCount: 4 )
257
258
struct FailedError : Error { }
258
259
try await self . testJobQueue ( numWorkers: 1 ) { jobQueue in
259
- jobQueue. registerJob ( id : jobIdentifer , maxRetryCount: 3 ) { _, _ in
260
+ jobQueue. registerJob ( parameters : TestParameters . self , maxRetryCount: 3 ) { _, _ in
260
261
expectation. fulfill ( )
261
262
throw FailedError ( )
262
263
}
263
- try await jobQueue. push ( id : jobIdentifer , parameters : 0 )
264
+ try await jobQueue. push ( TestParameters ( ) )
264
265
265
- await self . wait ( for : [ expectation] , timeout: 5 )
266
+ await fulfillment ( of : [ expectation] , timeout: 5 )
266
267
try await Task . sleep ( for: . milliseconds( 200 ) )
267
268
268
269
let failedJobs = try await jobQueue. queue. getJobs ( withStatus: . failed)
@@ -273,12 +274,14 @@ final class JobsTests: XCTestCase {
273
274
}
274
275
275
276
func testErrorRetryAndThenSucceed( ) async throws {
276
- let jobIdentifer = JobIdentifier < Int > ( #function)
277
+ struct TestParameters : JobParameters {
278
+ static let jobName = " testErrorRetryAndThenSucceed "
279
+ }
277
280
let expectation = XCTestExpectation ( description: " TestJob.execute was called " , expectedFulfillmentCount: 2 )
278
281
let currentJobTryCount : NIOLockedValueBox < Int > = . init( 0 )
279
282
struct FailedError : Error { }
280
283
try await self . testJobQueue ( numWorkers: 1 ) { jobQueue in
281
- jobQueue. registerJob ( id : jobIdentifer , maxRetryCount: 3 ) { _, _ in
284
+ jobQueue. registerJob ( parameters : TestParameters . self , maxRetryCount: 3 ) { _, _ in
282
285
defer {
283
286
currentJobTryCount. withLockedValue {
284
287
$0 += 1
@@ -289,9 +292,9 @@ final class JobsTests: XCTestCase {
289
292
throw FailedError ( )
290
293
}
291
294
}
292
- try await jobQueue. push ( id : jobIdentifer , parameters : 0 )
295
+ try await jobQueue. push ( TestParameters ( ) )
293
296
294
- await self . wait ( for : [ expectation] , timeout: 5 )
297
+ await fulfillment ( of : [ expectation] , timeout: 5 )
295
298
try await Task . sleep ( for: . milliseconds( 200 ) )
296
299
297
300
let failedJobs = try await jobQueue. queue. getJobs ( withStatus: . failed)
@@ -303,36 +306,38 @@ final class JobsTests: XCTestCase {
303
306
}
304
307
305
308
func testJobSerialization( ) async throws {
306
- struct TestJobParameters : Codable {
309
+ struct TestJobParameters : JobParameters {
310
+ static let jobName = " testJobSerialization "
307
311
let id : Int
308
312
let message : String
309
313
}
310
314
let expectation = XCTestExpectation ( description: " TestJob.execute was called " )
311
- let jobIdentifer = JobIdentifier < TestJobParameters > ( #function)
312
315
try await self . testJobQueue ( numWorkers: 1 ) { jobQueue in
313
- jobQueue. registerJob ( id : jobIdentifer ) { parameters, _ in
316
+ jobQueue. registerJob ( parameters : TestJobParameters . self ) { parameters, _ in
314
317
XCTAssertEqual ( parameters. id, 23 )
315
318
XCTAssertEqual ( parameters. message, " Hello! " )
316
319
expectation. fulfill ( )
317
320
}
318
- try await jobQueue. push ( id : jobIdentifer , parameters : . init ( id: 23 , message: " Hello! " ) )
321
+ try await jobQueue. push ( TestJobParameters ( id: 23 , message: " Hello! " ) )
319
322
320
- await self . wait ( for : [ expectation] , timeout: 5 )
323
+ await fulfillment ( of : [ expectation] , timeout: 5 )
321
324
}
322
325
}
323
326
324
327
/// Test job is cancelled on shutdown
325
328
func testShutdownJob( ) async throws {
326
- let jobIdentifer = JobIdentifier < Int > ( #function)
329
+ struct TestParameters : JobParameters {
330
+ static let jobName = " testShutdownJob "
331
+ }
327
332
let expectation = XCTestExpectation ( description: " TestJob.execute was called " , expectedFulfillmentCount: 1 )
328
333
329
334
try await self . testJobQueue ( numWorkers: 4 ) { jobQueue in
330
- jobQueue. registerJob ( id : jobIdentifer ) { _, _ in
335
+ jobQueue. registerJob ( parameters : TestParameters . self ) { _, _ in
331
336
expectation. fulfill ( )
332
337
try await Task . sleep ( for: . milliseconds( 1000 ) )
333
338
}
334
- try await jobQueue. push ( id : jobIdentifer , parameters : 0 )
335
- await self . wait ( for : [ expectation] , timeout: 5 )
339
+ try await jobQueue. push ( TestParameters ( ) )
340
+ await fulfillment ( of : [ expectation] , timeout: 5 )
336
341
337
342
let processingJobs = try await jobQueue. queue. getJobs ( withStatus: . processing)
338
343
XCTAssertEqual ( processingJobs. count, 1 )
@@ -344,19 +349,25 @@ final class JobsTests: XCTestCase {
344
349
345
350
/// test job fails to decode but queue continues to process
346
351
func testFailToDecode( ) async throws {
352
+ struct TestIntParameter : JobParameters {
353
+ static let jobName = " testFailToDecode "
354
+ let value : Int
355
+ }
356
+ struct TestStringParameter : JobParameters {
357
+ static let jobName = " testFailToDecode "
358
+ let value : String
359
+ }
347
360
let string : NIOLockedValueBox < String > = . init( " " )
348
- let jobIdentifer1 = JobIdentifier < Int > ( #function)
349
- let jobIdentifer2 = JobIdentifier < String > ( #function)
350
361
let expectation = XCTestExpectation ( description: " job was called " , expectedFulfillmentCount: 1 )
351
362
352
363
try await self . testJobQueue ( numWorkers: 4 ) { jobQueue in
353
- jobQueue. registerJob ( id : jobIdentifer2 ) { parameters, _ in
354
- string. withLockedValue { $0 = parameters }
364
+ jobQueue. registerJob ( parameters : TestStringParameter . self ) { parameters, _ in
365
+ string. withLockedValue { $0 = parameters. value }
355
366
expectation. fulfill ( )
356
367
}
357
- try await jobQueue. push ( id : jobIdentifer1 , parameters : 2 )
358
- try await jobQueue. push ( id : jobIdentifer2 , parameters : " test " )
359
- await self . wait ( for : [ expectation] , timeout: 5 )
368
+ try await jobQueue. push ( TestIntParameter ( value : 2 ) )
369
+ try await jobQueue. push ( TestStringParameter ( value : " test " ) )
370
+ await fulfillment ( of : [ expectation] , timeout: 5 )
360
371
}
361
372
string. withLockedValue {
362
373
XCTAssertEqual ( $0, " test " )
@@ -366,13 +377,15 @@ final class JobsTests: XCTestCase {
366
377
/// creates job that errors on first attempt, and is left on processing queue and
367
378
/// is then rerun on startup of new server
368
379
func testRerunAtStartup( ) async throws {
380
+ struct TestParameters : JobParameters {
381
+ static let jobName = " testRerunAtStartup "
382
+ }
369
383
struct RetryError : Error { }
370
- let jobIdentifer = JobIdentifier < Int > ( #function)
371
384
let firstTime = ManagedAtomic ( true )
372
385
let finished = ManagedAtomic ( false )
373
386
let failedExpectation = XCTestExpectation ( description: " TestJob failed " , expectedFulfillmentCount: 1 )
374
387
let succeededExpectation = XCTestExpectation ( description: " TestJob2 succeeded " , expectedFulfillmentCount: 1 )
375
- let job = JobDefinition ( id : jobIdentifer ) { _, _ in
388
+ let job = JobDefinition ( parameters : TestParameters . self ) { _, _ in
376
389
if firstTime. compareExchange ( expected: true , desired: false , ordering: . relaxed) . original {
377
390
failedExpectation. fulfill ( )
378
391
throw RetryError ( )
@@ -388,9 +401,9 @@ final class JobsTests: XCTestCase {
388
401
jobQueue: jobQueue,
389
402
revertMigrations: true
390
403
) { jobQueue in
391
- try await jobQueue. push ( id : jobIdentifer , parameters : 0 )
404
+ try await jobQueue. push ( TestParameters ( ) )
392
405
393
- await self . wait ( for : [ failedExpectation] , timeout: 10 )
406
+ await fulfillment ( of : [ failedExpectation] , timeout: 10 )
394
407
395
408
XCTAssertFalse ( firstTime. load ( ordering: . relaxed) )
396
409
XCTAssertFalse ( finished. load ( ordering: . relaxed) )
@@ -399,21 +412,24 @@ final class JobsTests: XCTestCase {
399
412
let jobQueue2 = try await createJobQueue ( numWorkers: 1 )
400
413
jobQueue2. registerJob ( job)
401
414
try await self . testJobQueue ( jobQueue: jobQueue2, failedJobsInitialization: . rerun) { _ in
402
- await self . wait ( for : [ succeededExpectation] , timeout: 10 )
415
+ await fulfillment ( of : [ succeededExpectation] , timeout: 10 )
403
416
XCTAssertTrue ( finished. load ( ordering: . relaxed) )
404
417
}
405
418
}
406
419
407
420
func testMultipleJobQueueHandlers( ) async throws {
408
- let jobIdentifer = JobIdentifier < Int > ( #function)
421
+ struct TestParameters : JobParameters {
422
+ static let jobName = " testMultipleJobQueueHandlers "
423
+ let value : Int
424
+ }
409
425
let expectation = XCTestExpectation ( description: " TestJob.execute was called " , expectedFulfillmentCount: 200 )
410
426
let logger = {
411
427
var logger = Logger ( label: " testMultipleJobQueueHandlers " )
412
428
logger. logLevel = . debug
413
429
return logger
414
430
} ( )
415
- let job = JobDefinition ( id : jobIdentifer ) { parameters, context in
416
- context. logger. info ( " Parameters= \( parameters) " )
431
+ let job = JobDefinition ( parameters : TestParameters . self ) { parameters, context in
432
+ context. logger. info ( " Parameters= \( parameters. value ) " )
417
433
try await Task . sleep ( for: . milliseconds( Int . random ( in: 10 ..< 50 ) ) )
418
434
expectation. fulfill ( )
419
435
}
@@ -463,11 +479,11 @@ final class JobsTests: XCTestCase {
463
479
try await jobQueue. queue. cleanup ( failedJobs: . remove, processingJobs: . remove)
464
480
try await jobQueue2. queue. cleanup ( failedJobs: . remove, processingJobs: . remove)
465
481
do {
466
- for i in 0 ..< 100 {
467
- try await jobQueue2 . push ( id : jobIdentifer , parameters : i )
468
- try await jobQueue . push ( id : jobIdentifer , parameters : i )
482
+ for i in 0 ..< 200 {
483
+ try await jobQueue . push ( TestParameters ( value : i ) )
484
+ try await jobQueue2 . push ( TestParameters ( value : i ) )
469
485
}
470
- await self . wait ( for : [ expectation] , timeout: 5 )
486
+ await fulfillment ( of : [ expectation] , timeout: 5 )
471
487
await serviceGroup. triggerGracefulShutdown ( )
472
488
} catch {
473
489
XCTFail ( " \( String ( reflecting: error) ) " )
0 commit comments