diff --git a/backend/__tests__/integration/adminBackupCoverage.test.js b/backend/__tests__/integration/adminBackupCoverage.test.js index 18fe862a..0cc5bbe0 100644 --- a/backend/__tests__/integration/adminBackupCoverage.test.js +++ b/backend/__tests__/integration/adminBackupCoverage.test.js @@ -40,7 +40,7 @@ jest.mock('../../src/middleware/permissions', () => ({ requirePermission: () => (_req, _res, next) => next(), })); -jest.setTimeout(30000); +jest.setTimeout(120000); describe('GET /api/admin/system-health/backup-coverage', () => { let db; diff --git a/backend/__tests__/integration/adminBackupIntegrity.test.js b/backend/__tests__/integration/adminBackupIntegrity.test.js index 9a046cc8..641764cf 100644 --- a/backend/__tests__/integration/adminBackupIntegrity.test.js +++ b/backend/__tests__/integration/adminBackupIntegrity.test.js @@ -29,7 +29,7 @@ jest.mock('../../src/middleware/permissions', () => ({ requirePermission: () => (_req, _res, next) => next(), })); -jest.setTimeout(30000); +jest.setTimeout(120000); describe('GET /api/admin/system-health/backup-integrity', () => { let cleanup; diff --git a/backend/__tests__/integration/backupService.configurableWalker.test.js b/backend/__tests__/integration/backupService.configurableWalker.test.js index 80c71a56..e2814c0d 100644 --- a/backend/__tests__/integration/backupService.configurableWalker.test.js +++ b/backend/__tests__/integration/backupService.configurableWalker.test.js @@ -23,7 +23,7 @@ const path = require('path'); const { bootCrmDb } = require('./helpers/crmDb'); -jest.setTimeout(30000); +jest.setTimeout(120000); describe('backupService — configurable walker (backup_paths)', () => { let db; diff --git a/backend/__tests__/integration/backupService.inlineDbDump.test.js b/backend/__tests__/integration/backupService.inlineDbDump.test.js index 431d6dc4..d24ce204 100644 --- a/backend/__tests__/integration/backupService.inlineDbDump.test.js +++ b/backend/__tests__/integration/backupService.inlineDbDump.test.js @@ -34,7 +34,7 @@ jest.mock('../../src/services/databaseBackup', () => ({ DatabaseBackupService: class {}, })); -jest.setTimeout(30000); +jest.setTimeout(120000); describe('backupService — inline DB dump + fail-loud guard', () => { let db; diff --git a/backend/__tests__/integration/backupService.perPathStats.test.js b/backend/__tests__/integration/backupService.perPathStats.test.js index 3e65ca12..562ec97d 100644 --- a/backend/__tests__/integration/backupService.perPathStats.test.js +++ b/backend/__tests__/integration/backupService.perPathStats.test.js @@ -23,7 +23,7 @@ const path = require('path'); const { bootCrmDb } = require('./helpers/crmDb'); -jest.setTimeout(30000); +jest.setTimeout(120000); describe('backupService — per-Stage-B-path statistics', () => { let db; diff --git a/backend/__tests__/integration/backupService.smoke.test.js b/backend/__tests__/integration/backupService.smoke.test.js index e1f90fb6..7c47a124 100644 --- a/backend/__tests__/integration/backupService.smoke.test.js +++ b/backend/__tests__/integration/backupService.smoke.test.js @@ -14,7 +14,7 @@ const path = require('path'); const { bootCrmDb } = require('./helpers/crmDb'); -jest.setTimeout(30000); +jest.setTimeout(120000); describe('backupService — config + file collection + manifest (smoke)', () => { let db; diff --git a/backend/__tests__/integration/bookingCutover.test.js b/backend/__tests__/integration/bookingCutover.test.js index 563d4fd6..34759e74 100644 --- a/backend/__tests__/integration/bookingCutover.test.js +++ b/backend/__tests__/integration/bookingCutover.test.js @@ -7,7 +7,7 @@ const crypto = require('crypto'); const { bootCrmDb, seedMinimal } = require('./helpers/crmDb'); -jest.setTimeout(30000); +jest.setTimeout(120000); describe('booking cutover — draft invoices on hold', () => { let db; let cleanup; let adminId; let customerId; let quoteService; diff --git a/backend/__tests__/integration/discountLineItems.test.js b/backend/__tests__/integration/discountLineItems.test.js index 6b222d36..2a041eb3 100644 --- a/backend/__tests__/integration/discountLineItems.test.js +++ b/backend/__tests__/integration/discountLineItems.test.js @@ -14,7 +14,7 @@ const { bootCrmDb, seedMinimal } = require('./helpers/crmDb'); // Service-level CRM calls cold-require heavy modules (pdfService, // nodemailer, etc.) on first use; the global 5 s per-test budget is // too tight for that. Bump it for this file only. -jest.setTimeout(30000); +jest.setTimeout(120000); describe('discount line items (negative unit_price_minor)', () => { let db; diff --git a/backend/__tests__/integration/eventTypeRename.test.js b/backend/__tests__/integration/eventTypeRename.test.js index caf41597..93c2ab66 100644 --- a/backend/__tests__/integration/eventTypeRename.test.js +++ b/backend/__tests__/integration/eventTypeRename.test.js @@ -6,7 +6,7 @@ const { bootCrmDb, seedMinimal } = require('./helpers/crmDb'); // bootCrmDb runs the full core-migration set in beforeAll. -jest.setTimeout(30000); +jest.setTimeout(120000); describe('event type slug rename cascade', () => { let db; diff --git a/backend/__tests__/integration/galleryShortUrlRoute.test.js b/backend/__tests__/integration/galleryShortUrlRoute.test.js index fd157c84..ced0c279 100644 --- a/backend/__tests__/integration/galleryShortUrlRoute.test.js +++ b/backend/__tests__/integration/galleryShortUrlRoute.test.js @@ -17,7 +17,7 @@ const request = require('supertest'); const { bootCrmDb } = require('./helpers/crmDb'); -jest.setTimeout(60000); +jest.setTimeout(120000); let db; let cleanup; let service; let app; diff --git a/backend/__tests__/integration/galleryShortUrls.test.js b/backend/__tests__/integration/galleryShortUrls.test.js index cc3b40ec..c8ae58e4 100644 --- a/backend/__tests__/integration/galleryShortUrls.test.js +++ b/backend/__tests__/integration/galleryShortUrls.test.js @@ -19,7 +19,7 @@ */ const { bootCrmDb } = require('./helpers/crmDb'); -jest.setTimeout(60000); +jest.setTimeout(120000); let db; let cleanup; let service; let adminId; diff --git a/backend/__tests__/integration/incomingInvoiceRebill.test.js b/backend/__tests__/integration/incomingInvoiceRebill.test.js index 9d9312c7..0af12ed6 100644 --- a/backend/__tests__/integration/incomingInvoiceRebill.test.js +++ b/backend/__tests__/integration/incomingInvoiceRebill.test.js @@ -13,7 +13,7 @@ const { bootCrmDb, seedMinimal } = require('./helpers/crmDb'); // Service-level CRM calls cold-require heavy modules (pdfService, nodemailer) // on first use; bump the budget for this file. -jest.setTimeout(60000); +jest.setTimeout(120000); describe('incoming-invoice categorise / re-bill chain', () => { let db; diff --git a/backend/__tests__/integration/installFromBackupBoot.test.js b/backend/__tests__/integration/installFromBackupBoot.test.js index 976def16..328c0295 100644 --- a/backend/__tests__/integration/installFromBackupBoot.test.js +++ b/backend/__tests__/integration/installFromBackupBoot.test.js @@ -32,7 +32,7 @@ jest.mock('../../src/services/restoreService', () => ({ }, })); -jest.setTimeout(30000); +jest.setTimeout(120000); describe('installFromBackupBoot', () => { let db; diff --git a/backend/__tests__/integration/invoiceDunning.test.js b/backend/__tests__/integration/invoiceDunning.test.js index 8b4194a3..ce069b1b 100644 --- a/backend/__tests__/integration/invoiceDunning.test.js +++ b/backend/__tests__/integration/invoiceDunning.test.js @@ -13,7 +13,7 @@ const { bootCrmDb, seedMinimal } = require('./helpers/crmDb'); // bootCrmDb runs the full core-migration set in beforeAll; under full-suite // parallel load on a small CI runner that can exceed the 5s default. Match the // other migration-heavy CRM suites (discountLineItems, incomingInvoiceRebill). -jest.setTimeout(30000); +jest.setTimeout(120000); let db; let cleanup; diff --git a/backend/__tests__/integration/picpeakExport.test.js b/backend/__tests__/integration/picpeakExport.test.js index bb443d6c..70fa9f83 100644 --- a/backend/__tests__/integration/picpeakExport.test.js +++ b/backend/__tests__/integration/picpeakExport.test.js @@ -21,7 +21,7 @@ beforeAll(async () => { ({ db, cleanup, tmpDir } = await bootCrmDb()); process.env.STORAGE_PATH = tmpDir; // isolate file collection to the temp dir ({ createPicpeak } = require('../../src/services/picpeakExportService')); -}, 60000); +}, 120000); afterAll(async () => { await cleanup(); diff --git a/backend/__tests__/integration/picpeakRoundtrip.test.js b/backend/__tests__/integration/picpeakRoundtrip.test.js index 8e26de07..fcc6e7c6 100644 --- a/backend/__tests__/integration/picpeakRoundtrip.test.js +++ b/backend/__tests__/integration/picpeakRoundtrip.test.js @@ -28,7 +28,7 @@ beforeAll(async () => { ({ importFromPicpeak, validateManifest } = require('../../src/services/picpeakImportService')); const role = await db('roles').where({ name: 'super_admin' }).first(); superAdminRoleId = role.id; -}, 60000); +}, 120000); afterAll(async () => { await cleanup(); diff --git a/backend/__tests__/integration/resetAdminMfaCli.test.js b/backend/__tests__/integration/resetAdminMfaCli.test.js index 3aa3f2bb..f19fc528 100644 --- a/backend/__tests__/integration/resetAdminMfaCli.test.js +++ b/backend/__tests__/integration/resetAdminMfaCli.test.js @@ -13,14 +13,14 @@ const { execFileSync } = require('child_process'); const { bootCrmDb } = require('./helpers/crmDb'); -jest.setTimeout(60000); +jest.setTimeout(120000); let db; let cleanup; beforeAll(async () => { ({ db, cleanup } = await bootCrmDb()); -}, 60000); +}, 120000); afterAll(async () => { if (cleanup) await cleanup(); diff --git a/backend/__tests__/integration/setupService.test.js b/backend/__tests__/integration/setupService.test.js index 0f48f48d..7e4f234a 100644 --- a/backend/__tests__/integration/setupService.test.js +++ b/backend/__tests__/integration/setupService.test.js @@ -27,7 +27,7 @@ beforeAll(async () => { setupService = require('../../src/services/setupService'); ({ getAppSetting, upsertAppSetting } = require('../../src/utils/appSettings')); app = buildRouteApp('/api/setup', require('../../src/routes/setup')); -}, 60000); +}, 120000); afterAll(async () => { await cleanup(); diff --git a/backend/__tests__/integration/workflowEngine.test.js b/backend/__tests__/integration/workflowEngine.test.js index 5881d90e..83072f72 100644 --- a/backend/__tests__/integration/workflowEngine.test.js +++ b/backend/__tests__/integration/workflowEngine.test.js @@ -10,7 +10,7 @@ const { bootCrmDb } = require('./helpers/crmDb'); // bootCrmDb runs the full core-migration set in beforeAll; under full-suite // parallel load on a small CI runner that can exceed the 5s default. Match the // other migration-heavy CRM suites (discountLineItems, incomingInvoiceRebill). -jest.setTimeout(30000); +jest.setTimeout(120000); let db; let cleanup; diff --git a/backend/__tests__/integration/workflowRoutes.test.js b/backend/__tests__/integration/workflowRoutes.test.js index c5126c84..5f4916c9 100644 --- a/backend/__tests__/integration/workflowRoutes.test.js +++ b/backend/__tests__/integration/workflowRoutes.test.js @@ -9,7 +9,7 @@ const { // bootCrmDb runs the full core-migration set in beforeAll; under full-suite // parallel load on a small CI runner that can exceed the 5s default. Match the // other migration-heavy CRM suites (discountLineItems, incomingInvoiceRebill). -jest.setTimeout(30000); +jest.setTimeout(120000); let db; let cleanup; diff --git a/backend/__tests__/routes/adminCrmAuth.test.js b/backend/__tests__/routes/adminCrmAuth.test.js index 3365994c..ef432827 100644 --- a/backend/__tests__/routes/adminCrmAuth.test.js +++ b/backend/__tests__/routes/adminCrmAuth.test.js @@ -83,7 +83,7 @@ describe('admin CRM routes — auth + permission gate', () => { // Invalid: signed with a different secret. adminAuth must reject. const jwt = require('jsonwebtoken'); invalidToken = jwt.sign({ id: adminId, type: 'admin' }, 'WRONG-SECRET', { issuer: 'picpeak-auth' }); - }, 60000); + }, 120000); afterAll(async () => { if (cleanup) await cleanup(); diff --git a/backend/__tests__/routes/adminMfa.test.js b/backend/__tests__/routes/adminMfa.test.js index 352f2a6e..0cfaabb3 100644 --- a/backend/__tests__/routes/adminMfa.test.js +++ b/backend/__tests__/routes/adminMfa.test.js @@ -39,7 +39,7 @@ const { bootCrmDb, mintAdminToken, buildRouteApp, } = require('../integration/helpers/crmDb'); -jest.setTimeout(60000); +jest.setTimeout(120000); let db; let cleanup; @@ -95,7 +95,7 @@ beforeAll(async () => { ({ db, cleanup } = await bootCrmDb()); adminApp = buildRouteApp('/api/admin/auth', require('../../src/routes/adminAuth')); authApp = buildRouteApp('/api/auth', require('../../src/routes/auth')); -}, 60000); +}, 120000); afterAll(async () => { if (cleanup) await cleanup(); diff --git a/backend/__tests__/routes/publicContracts.test.js b/backend/__tests__/routes/publicContracts.test.js index a1ed781c..c258f296 100644 --- a/backend/__tests__/routes/publicContracts.test.js +++ b/backend/__tests__/routes/publicContracts.test.js @@ -51,7 +51,7 @@ describe('publicContracts routes', () => { contractId = inserted[0]?.id ?? inserted[0]; app = buildRouteApp('/api/public/contracts', require('../../src/routes/publicContracts')); - }, 60000); + }, 120000); afterAll(async () => { if (cleanup) await cleanup(); diff --git a/backend/__tests__/routes/publicPaymentCheck.test.js b/backend/__tests__/routes/publicPaymentCheck.test.js index ea5c0ee8..62a59023 100644 --- a/backend/__tests__/routes/publicPaymentCheck.test.js +++ b/backend/__tests__/routes/publicPaymentCheck.test.js @@ -33,7 +33,7 @@ describe('publicPaymentCheck routes', () => { ({ db, cleanup } = await bootCrmDb()); await seedMinimal(db); app = buildRouteApp('/api/public/payment-check', require('../../src/routes/publicPaymentCheck')); - }, 60000); + }, 120000); afterAll(async () => { if (cleanup) await cleanup(); diff --git a/backend/__tests__/routes/publicQuotes.test.js b/backend/__tests__/routes/publicQuotes.test.js index f5234f71..0012bc60 100644 --- a/backend/__tests__/routes/publicQuotes.test.js +++ b/backend/__tests__/routes/publicQuotes.test.js @@ -60,7 +60,7 @@ describe('publicQuotes routes', () => { quoteId = inserted[0]?.id ?? inserted[0]; app = buildRouteApp('/api/public/quotes', require('../../src/routes/publicQuotes')); - }, 60000); + }, 120000); afterAll(async () => { if (cleanup) await cleanup(); diff --git a/backend/__tests__/routes/slideshowAdmin.test.js b/backend/__tests__/routes/slideshowAdmin.test.js index ca277004..d8c9d876 100644 --- a/backend/__tests__/routes/slideshowAdmin.test.js +++ b/backend/__tests__/routes/slideshowAdmin.test.js @@ -75,7 +75,7 @@ describe('admin Live Slideshow endpoints', () => { app.use((err, req, res, next) => { res.status(err.statusCode || err.status || 500).json({ error: err.message, code: err.code }); }); - }, 30000); + }, 120000); afterAll(async () => { await cleanup(); }); diff --git a/backend/__tests__/routes/slideshowPublic.test.js b/backend/__tests__/routes/slideshowPublic.test.js index 5bb6633a..a2080f2e 100644 --- a/backend/__tests__/routes/slideshowPublic.test.js +++ b/backend/__tests__/routes/slideshowPublic.test.js @@ -67,11 +67,10 @@ async function insertEvent(db, over = {}) { describe('public Live Slideshow routes', () => { let db; let cleanup; let app; - // bootCrmDb runs the full migration set against a fresh SQLite file, which - // takes <2s locally but has been observed to exceed Jest's default 5s - // `beforeAll` timeout on slower GitHub Actions runners (~5.4s — runner-to- - // runner I/O variance). Raise the hook timeout so this doesn't intermittently - // block PRs on CI; doesn't affect happy-path local runs. + // bootCrmDb runs the full migration set against a fresh SQLite file and the + // chain keeps growing via backports. Hook-argument timeouts OVERRIDE the + // 120s jest.config default (same trap as the jest.setTimeout pins) — keep + // this at 120000, matching the config. beforeAll(async () => { ({ db, cleanup } = await bootCrmDb()); await seedMinimal(db); @@ -86,7 +85,7 @@ describe('public Live Slideshow routes', () => { app.use((err, req, res, next) => { res.status(err.statusCode || err.status || 500).json({ error: err.message, code: err.code }); }); - }, 30000); + }, 120000); afterAll(async () => { await cleanup(); }); diff --git a/backend/__tests__/services/backupIntegrityService.test.js b/backend/__tests__/services/backupIntegrityService.test.js index 52e2c6bf..0fb1fb99 100644 --- a/backend/__tests__/services/backupIntegrityService.test.js +++ b/backend/__tests__/services/backupIntegrityService.test.js @@ -22,7 +22,7 @@ const crypto = require('crypto'); const { bootCrmDb, seedMinimal } = require('../integration/helpers/crmDb'); -jest.setTimeout(30000); +jest.setTimeout(120000); describe('backupIntegrityService.verifyDocumentArtefacts', () => { let db; diff --git a/backend/__tests__/services/trackerFactory.test.js b/backend/__tests__/services/trackerFactory.test.js index 9bebee90..9b983350 100644 --- a/backend/__tests__/services/trackerFactory.test.js +++ b/backend/__tests__/services/trackerFactory.test.js @@ -27,7 +27,7 @@ let db; let cleanup; beforeAll(async () => { ({ db, cleanup } = await bootCrmDb()); -}, 30000); +}, 120000); afterAll(async () => { if (cleanup) await cleanup(); }); diff --git a/backend/__tests__/services/userManagementService.activateDelete.test.js b/backend/__tests__/services/userManagementService.activateDelete.test.js index 0c450afc..137b97d3 100644 --- a/backend/__tests__/services/userManagementService.activateDelete.test.js +++ b/backend/__tests__/services/userManagementService.activateDelete.test.js @@ -48,7 +48,7 @@ describe('userManagementService — activate + delete (#574 follow-up)', () => { is_active: 1, created_at: new Date(), }).returning('id'); targetId = targetInsert[0]?.id ?? targetInsert[0]; - }, 60000); + }, 120000); afterAll(async () => { if (cleanup) await cleanup(); diff --git a/backend/__tests__/utils/feedbackPerGuestLimit.test.js b/backend/__tests__/utils/feedbackPerGuestLimit.test.js index 40826e2d..dc2cfae0 100644 --- a/backend/__tests__/utils/feedbackPerGuestLimit.test.js +++ b/backend/__tests__/utils/feedbackPerGuestLimit.test.js @@ -115,7 +115,7 @@ beforeAll(async () => { }).returning('id'); photoIds.push(r[0]?.id ?? r[0]); } -}, 30000); +}, 120000); afterAll(async () => { if (cleanup) await cleanup(); }); diff --git a/backend/jest.config.js b/backend/jest.config.js index 2c28c3db..8e945f17 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -1,5 +1,9 @@ module.exports = { testEnvironment: 'node', + // bootCrmDb() runs EVERY core migration in beforeAll and the chain keeps + // growing (134 migrations and counting via backports). 120s matches the + // beta-branch convention from #860. + testTimeout: 120000, coverageDirectory: 'coverage', collectCoverageFrom: [ 'src/**/*.js',