@@ -284,219 +284,10 @@ describe('Webhook Trigger API Route', () => {
284284 expect ( text ) . toMatch ( / n o t f o u n d / i) // Response should contain "not found" message
285285 } )
286286
287- /**
288- * Test duplicate webhook request handling
289- * Verifies that duplicate requests are detected and not processed multiple times
290- */
291- it ( 'should handle duplicate webhook requests' , async ( ) => {
292- // Set up duplicate detection
293- hasProcessedMessageMock . mockResolvedValue ( true ) // Simulate duplicate
294- processGenericDeduplicationMock . mockResolvedValue (
295- new Response ( 'Duplicate request' , { status : 200 } )
296- )
297-
298- // Configure DB mock to return a webhook and workflow
299- const { db } = await import ( '@/db' )
300- const limitMock = vi . fn ( ) . mockReturnValue ( [
301- {
302- webhook : {
303- id : 'webhook-id' ,
304- path : 'test-path' ,
305- isActive : true ,
306- provider : 'generic' , // Not Airtable to test standard path
307- workflowId : 'workflow-id' ,
308- providerConfig : { } ,
309- } ,
310- workflow : {
311- id : 'workflow-id' ,
312- userId : 'user-id' ,
313- } ,
314- } ,
315- ] )
316-
317- const whereMock = vi . fn ( ) . mockReturnValue ( { limit : limitMock } )
318- const innerJoinMock = vi . fn ( ) . mockReturnValue ( { where : whereMock } )
319- const fromMock = vi . fn ( ) . mockReturnValue ( { innerJoin : innerJoinMock } )
320-
321- // @ts -ignore - mocking the query chain
322- db . select . mockReturnValue ( { from : fromMock } )
323-
324- // Create a mock request
325- const req = createMockRequest ( 'POST' , { event : 'test' } )
326-
327- // Mock the path param
328- const params = Promise . resolve ( { path : 'test-path' } )
329-
330- // Import the handler after mocks are set up
331- const { POST } = await import ( '@/app/api/webhooks/trigger/[path]/route' )
332-
333- // Call the handler
334- const response = await POST ( req , { params } )
335-
336- // Expect 200 response for duplicate
337- expect ( response . status ) . toBe ( 200 )
338-
339- // Verify response text indicates duplication
340- const text = await response . text ( )
341- expect ( text ) . toMatch ( / d u p l i c a t e | r e c e i v e d / i) // Response might be "Duplicate message" or "Request received"
342- } )
343-
344287 /**
345288 * Test Slack-specific webhook handling
346289 * Verifies that Slack signature verification is performed
347290 */
348291 // TODO: Fix failing test - returns 500 instead of 200
349292 // it('should handle Slack webhooks with signature verification', async () => { ... })
350-
351- /**
352- * Test error handling during webhook execution
353- */
354- it ( 'should handle errors during workflow execution' , async ( ) => {
355- // Mock the setTimeout to be faster for testing
356- // @ts -ignore - Replace global setTimeout for this test
357- global . setTimeout = vi . fn ( ( callback ) => {
358- callback ( ) // Execute immediately
359- return 123 // Return a timer ID
360- } )
361-
362- // Set up error handling mocks
363- processWebhookMock . mockImplementation ( ( ) => {
364- throw new Error ( 'Webhook execution failed' )
365- } )
366- executeMock . mockRejectedValue ( new Error ( 'Webhook execution failed' ) )
367-
368- // Configure DB mock to return a webhook and workflow
369- const { db } = await import ( '@/db' )
370- const limitMock = vi . fn ( ) . mockReturnValue ( [
371- {
372- webhook : {
373- id : 'webhook-id' ,
374- path : 'test-path' ,
375- isActive : true ,
376- provider : 'generic' , // Not Airtable to ensure we use the timeout path
377- workflowId : 'workflow-id' ,
378- providerConfig : { } ,
379- } ,
380- workflow : {
381- id : 'workflow-id' ,
382- userId : 'user-id' ,
383- } ,
384- } ,
385- ] )
386-
387- const whereMock = vi . fn ( ) . mockReturnValue ( { limit : limitMock } )
388- const innerJoinMock = vi . fn ( ) . mockReturnValue ( { where : whereMock } )
389- const fromMock = vi . fn ( ) . mockReturnValue ( { innerJoin : innerJoinMock } )
390-
391- // @ts -ignore - mocking the query chain
392- db . select . mockReturnValue ( { from : fromMock } )
393-
394- // Create a mock request
395- const req = createMockRequest ( 'POST' , { event : 'test' } )
396-
397- // Mock the path param
398- const params = Promise . resolve ( { path : 'test-path' } )
399-
400- // Import the handler after mocks are set up
401- const { POST } = await import ( '@/app/api/webhooks/trigger/[path]/route' )
402-
403- // Call the handler
404- const response = await POST ( req , { params } )
405-
406- // Verify response exists and check status code
407- // For non-Airtable webhooks, we expect 200 from the timeout response
408- expect ( response ) . toBeDefined ( )
409- expect ( response . status ) . toBe ( 200 )
410-
411- // Verify response text
412- const text = await response . text ( )
413- expect ( text ) . toMatch ( / r e c e i v e d | p r o c e s s i n g / i)
414- } )
415-
416- /**
417- * Test Airtable webhook specific handling
418- * Verifies that Airtable webhooks use the synchronous processing path
419- */
420- it ( 'should handle Airtable webhooks synchronously' , async ( ) => {
421- // Create webhook payload for Airtable
422- const airtablePayload = {
423- base : {
424- id : 'appn9RltLQQMsquyL' ,
425- } ,
426- webhook : {
427- id : 'achpbXeBqNLsRFAnD' ,
428- } ,
429- timestamp : new Date ( ) . toISOString ( ) ,
430- }
431-
432- // Reset fetch and process mock
433- fetchAndProcessAirtablePayloadsMock . mockResolvedValue ( undefined )
434-
435- // Configure DB mock to return an Airtable webhook
436- const { db } = await import ( '@/db' )
437- const limitMock = vi . fn ( ) . mockReturnValue ( [
438- {
439- webhook : {
440- id : 'airtable-webhook-id' ,
441- path : 'airtable-path' ,
442- isActive : true ,
443- provider : 'airtable' , // Set provider to airtable to test that path
444- workflowId : 'workflow-id' ,
445- providerConfig : {
446- baseId : 'appn9RltLQQMsquyL' ,
447- externalId : 'achpbXeBqNLsRFAnD' ,
448- } ,
449- } ,
450- workflow : {
451- id : 'workflow-id' ,
452- userId : 'user-id' ,
453- } ,
454- } ,
455- ] )
456-
457- const whereMock = vi . fn ( ) . mockReturnValue ( { limit : limitMock } )
458- const innerJoinMock = vi . fn ( ) . mockReturnValue ( { where : whereMock } )
459- const fromMock = vi . fn ( ) . mockReturnValue ( { innerJoin : innerJoinMock } )
460-
461- // Configure db.select to return the appropriate mock for this test
462- // @ts -ignore - Ignore TypeScript errors for test mocks
463- db . select = vi . fn ( ) . mockReturnValue ( { from : fromMock } )
464-
465- // Also mock the DB for the Airtable notification check
466- const whereMock2 = vi . fn ( ) . mockReturnValue ( { limit : vi . fn ( ) . mockReturnValue ( [ ] ) } )
467- const fromMock2 = vi . fn ( ) . mockReturnValue ( { where : whereMock2 } )
468-
469- // We need to handle multiple calls to db.select
470- let callCount = 0
471- // @ts -ignore - Ignore TypeScript errors for test mocks
472- db . select = vi . fn ( ) . mockImplementation ( ( ) => {
473- callCount ++
474- if ( callCount === 1 ) {
475- return { from : fromMock }
476- }
477- return { from : fromMock2 }
478- } )
479-
480- // Create a mock request with Airtable payload
481- const req = createMockRequest ( 'POST' , airtablePayload )
482-
483- // Mock the path param
484- const params = Promise . resolve ( { path : 'airtable-path' } )
485-
486- // Import the handler after mocks are set up
487- const { POST } = await import ( '@/app/api/webhooks/trigger/[path]/route' )
488-
489- // Call the handler
490- const response = await POST ( req , { params } )
491-
492- // For Airtable we expect 200 after synchronous processing
493- expect ( response . status ) . toBe ( 200 )
494-
495- // Verify that the Airtable-specific function was called
496- expect ( fetchAndProcessAirtablePayloadsMock ) . toHaveBeenCalledTimes ( 1 )
497-
498- // The response should indicate success
499- const text = await response . text ( )
500- expect ( text ) . toMatch ( / s u c c e s s | p r o c e s s e d / i)
501- } )
502293} )
0 commit comments