@@ -428,4 +428,131 @@ describe("SqliteQueue", () => {
428428 failed : 0 ,
429429 } ) ;
430430 } ) ;
431+
432+ test ( "availableAt scheduling" , async ( ) => {
433+ const queue = new SqliteQueue < Work > (
434+ "available-at-queue" ,
435+ buildDBClient ( ":memory:" , { runMigrations : true } ) ,
436+ {
437+ defaultJobArgs : {
438+ numRetries : 0 ,
439+ } ,
440+ keepFailedJobs : false ,
441+ } ,
442+ ) ;
443+
444+ // Enqueue job with availableAt in the future
445+ const job = await queue . enqueue ( { increment : 1 } , { delayMs : 5000 } ) ;
446+
447+ // Job should not be available for dequeue yet
448+ const dequeuedJob = await queue . attemptDequeue ( { timeoutSecs : 1 } ) ;
449+ expect ( dequeuedJob ) . toBeNull ( ) ;
450+
451+ {
452+ // Enqueue a new job available now
453+ const enqueuedJob = await queue . enqueue ( { increment : 2 } ) ;
454+ // Should be able to dequeue the available job
455+ const availableJob = await queue . attemptDequeue ( { timeoutSecs : 1 } ) ;
456+ expect ( availableJob ) . not . toBeNull ( ) ;
457+ expect ( enqueuedJob ! . id ) . toBe ( availableJob ! . id ) ;
458+ }
459+ } ) ;
460+
461+ test ( "availableAt jobs become available after the scheduled time" , async ( ) => {
462+ const queue = new SqliteQueue < Work > (
463+ "available-later-queue" ,
464+ buildDBClient ( ":memory:" , { runMigrations : true } ) ,
465+ {
466+ defaultJobArgs : {
467+ numRetries : 0 ,
468+ } ,
469+ keepFailedJobs : false ,
470+ } ,
471+ ) ;
472+
473+ // Enqueue job with availableAt very soon
474+ const enqueuedJob = await queue . enqueue (
475+ { increment : 1 } ,
476+ { delayMs : 1000 } ,
477+ ) ;
478+
479+ {
480+ // Shouldn't be able to dequeue the job yet
481+ const dequeuedJob = await queue . attemptDequeue ( { timeoutSecs : 0 } ) ;
482+ expect ( dequeuedJob ) . toBeNull ( ) ;
483+ }
484+
485+ // Wait for the time to pass
486+ await new Promise ( ( resolve ) => setTimeout ( resolve , 1500 ) ) ;
487+
488+ // Should now be able to dequeue the job
489+ const dequeuedJob = await queue . attemptDequeue ( { timeoutSecs : 0 } ) ;
490+ expect ( dequeuedJob ) . not . toBeNull ( ) ;
491+ expect ( enqueuedJob ! . id ) . toBe ( dequeuedJob ! . id ) ;
492+ } ) ;
493+
494+ test ( "finalize with refund retry increments numRunsLeft" , async ( ) => {
495+ const queue = new SqliteQueue < Work > (
496+ "refund-retry-queue" ,
497+ buildDBClient ( ":memory:" , { runMigrations : true } ) ,
498+ {
499+ defaultJobArgs : {
500+ numRetries : 1 ,
501+ } ,
502+ keepFailedJobs : true ,
503+ } ,
504+ ) ;
505+
506+ // Enqueue a job that will have 3 total attempts (initial + 2 retries)
507+ const job = await queue . enqueue ( { increment : 1 } ) ;
508+ expect ( job ) . not . toBeUndefined ( ) ;
509+
510+ // Dequeue the job
511+ const dequeuedJob = await queue . attemptDequeue ( { timeoutSecs : 30 } ) ;
512+ expect ( dequeuedJob ) . not . toBeNull ( ) ;
513+ expect ( dequeuedJob ! . maxNumRuns ) . toBe ( 2 ) ; // 1 initial + 1 retries
514+ expect ( dequeuedJob ! . numRunsLeft ) . toBe ( 1 ) ;
515+
516+ // Mark as pending_retry without refund - should consume one retry
517+ await queue . finalize (
518+ dequeuedJob ! . id ,
519+ dequeuedJob ! . allocationId ,
520+ "pending_retry" ,
521+ new Date ( ) ,
522+ false , // refundRetry = false
523+ ) ;
524+
525+ // Dequeue again to check numRunsLeft
526+ const dequeuedJob2 = await queue . attemptDequeue ( { timeoutSecs : 30 } ) ;
527+ expect ( dequeuedJob2 ) . not . toBeNull ( ) ;
528+ expect ( dequeuedJob2 ! . numRunsLeft ) . toBe ( 0 ) ; // One retry consumed
529+
530+ // Mark as pending_retry WITH refund - should NOT consume a retry
531+ await queue . finalize (
532+ dequeuedJob2 ! . id ,
533+ dequeuedJob2 ! . allocationId ,
534+ "pending_retry" ,
535+ new Date ( ) ,
536+ true , // refundRetry = true
537+ ) ;
538+
539+ // Dequeue again to check numRunsLeft
540+ const dequeuedJob3 = await queue . attemptDequeue ( { timeoutSecs : 30 } ) ;
541+ expect ( dequeuedJob3 ) . not . toBeNull ( ) ;
542+ expect ( dequeuedJob3 ! . numRunsLeft ) . toBe ( 0 ) ; // Same as before, retry was refunded
543+
544+ // Complete the job
545+ await queue . finalize (
546+ dequeuedJob3 ! . id ,
547+ dequeuedJob3 ! . allocationId ,
548+ "completed" ,
549+ ) ;
550+
551+ expect ( await queue . stats ( ) ) . toEqual ( {
552+ pending : 0 ,
553+ running : 0 ,
554+ pending_retry : 0 ,
555+ failed : 0 ,
556+ } ) ;
557+ } ) ;
431558} ) ;
0 commit comments