@@ -218,6 +218,110 @@ describe('Mongoose Adapter', () => {
218218 const job2 = await queue . mongooseAdapter . reserveJob ( 60 ) ;
219219 expect ( job2 ) . toBeDefined ( ) ;
220220 } ) ;
221+
222+
223+ it ( 'should handle job failures and retries through the queue system' , async ( ) => {
224+ const queue = createMongooseQueue < {
225+ 'failing-job' : { attemptNumber : number } ;
226+ 'success-job' : { data : string } ;
227+ } > ( 'test-queue' , testModel ) ;
228+
229+ let attempts : number [ ] = [ ] ;
230+ let successfulJobs : string [ ] = [ ] ;
231+
232+ queue . setHandlers ( {
233+ 'failing-job' : async ( job ) => {
234+ attempts . push ( job . payload . attemptNumber ) ;
235+
236+ if ( job . payload . attemptNumber <= 2 ) {
237+ throw new Error ( `Intentional failure on attempt ${ job . payload . attemptNumber } ` ) ;
238+ }
239+
240+ // Success on attempt 3
241+ } ,
242+ 'success-job' : async ( job ) => {
243+ successfulJobs . push ( job . payload . data ) ;
244+ }
245+ } ) ;
246+
247+ // Add jobs
248+ const failingJob1 = await queue . addJob ( 'failing-job' , { payload : { attemptNumber : 1 } } ) ;
249+ const failingJob2 = await queue . addJob ( 'failing-job' , { payload : { attemptNumber : 2 } } ) ;
250+ const failingJob3 = await queue . addJob ( 'failing-job' , { payload : { attemptNumber : 3 } } ) ;
251+ const successJob = await queue . addJob ( 'success-job' , { payload : { data : 'test-data' } } ) ;
252+
253+ // Process all jobs
254+ await queue . run ( ) ;
255+
256+ // Check results
257+ expect ( attempts ) . toEqual ( [ 1 , 2 , 3 ] ) ;
258+ expect ( successfulJobs ) . toEqual ( [ 'test-data' ] ) ;
259+
260+ // Check final job statuses
261+ const job1Doc = await testModel . findById ( failingJob1 ) ;
262+ const job2Doc = await testModel . findById ( failingJob2 ) ;
263+ const job3Doc = await testModel . findById ( failingJob3 ) ;
264+ const successDoc = await testModel . findById ( successJob ) ;
265+
266+ expect ( job1Doc ?. status ) . toBe ( 'failed' ) ;
267+ expect ( job2Doc ?. status ) . toBe ( 'failed' ) ;
268+ expect ( job3Doc ?. status ) . toBe ( 'done' ) ;
269+ expect ( successDoc ?. status ) . toBe ( 'done' ) ;
270+
271+ // Check error messages
272+ expect ( job1Doc ?. errorMessage ) . toContain ( 'Intentional failure on attempt 1' ) ;
273+ expect ( job2Doc ?. errorMessage ) . toContain ( 'Intentional failure on attempt 2' ) ;
274+ } ) ;
275+
276+ it ( 'should handle TTR timeout and job recovery in real processing' , async ( ) => {
277+ const queue = createMongooseQueue < {
278+ 'long-job' : { duration : number } ;
279+ 'quick-job' : { data : string } ;
280+ } > ( 'test-queue' , testModel ) ;
281+
282+ let jobExecutions : string [ ] = [ ] ;
283+
284+ queue . setHandlers ( {
285+ 'long-job' : async ( job ) => {
286+ jobExecutions . push ( `long-job-start-${ job . payload . duration } ` ) ;
287+ // Simulate a job that takes longer than its TTR
288+ await new Promise ( resolve => setTimeout ( resolve , job . payload . duration ) ) ;
289+ jobExecutions . push ( `long-job-end-${ job . payload . duration } ` ) ;
290+ } ,
291+ 'quick-job' : async ( job ) => {
292+ jobExecutions . push ( `quick-job-${ job . payload . data } ` ) ;
293+ }
294+ } ) ;
295+
296+ // Add a job with very short TTR that will timeout
297+ const longJobId = await queue . addJob ( 'long-job' , {
298+ payload : { duration : 2000 } , // 2 seconds
299+ ttr : 1 // 1 second TTR - will timeout
300+ } ) ;
301+
302+ // Add a quick job
303+ const quickJobId = await queue . addJob ( 'quick-job' , {
304+ payload : { data : 'test' }
305+ } ) ;
306+
307+ // Process queue - long job will timeout, quick job should complete
308+ await queue . run ( ) ;
309+
310+ // Verify states after first run
311+ let longJobDoc = await testModel . findById ( longJobId ) ;
312+ let quickJobDoc = await testModel . findById ( quickJobId ) ;
313+
314+ // Quick job should be done, long job should be in some intermediate state
315+ expect ( quickJobDoc ?. status ) . toBe ( 'done' ) ;
316+
317+ // Check what actually got executed
318+ expect ( jobExecutions ) . toContain ( 'quick-job-test' ) ;
319+ expect ( jobExecutions ) . toContain ( 'long-job-start-2000' ) ;
320+
321+ // The long job may or may not have finished depending on timing
322+ // but it should have been processed at least once
323+ expect ( longJobDoc ) . toBeDefined ( ) ;
324+ } , 10000 ) ; // Longer timeout for this test
221325 } ) ;
222326
223327 describe ( 'createQueueModel' , ( ) => {
0 commit comments