From d9ad982373861cd05f23167f1e2e50eaebfb7bba Mon Sep 17 00:00:00 2001 From: Paul Nothaft <53005142+the-luap@users.noreply.github.com> Date: Wed, 29 Jul 2026 12:38:26 +0200 Subject: [PATCH] fix(tests): raise migration-boot hook timeout pins to the 120s default (#900) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 3.97.0-beta.0 release PR (#899) failed its backend Tests job on slideshowPublic.test.js: bootCrmDb's full migration chain crossed the suite's explicit 30s beforeAll timeout argument on a slow runner. #860 raised the config default and the jest.setTimeout pins to 120s, but hook-ARGUMENT pins override the config default and were left behind — same time-bomb, different syntax. Every beforeAll that boots the migration chain and pinned 30s/60s is raised to 120000 (16 suites). Untouched on purpose: the three suites whose pinned hooks don't run migrations (webhookDelivery, imageProcessor.storage, storageBackend) and publicQuotes' 30s pin on the rate-limit lockout test — neither grows with the migration chain. No test logic changed. Co-authored-by: Paul Nothaft --- backend/__tests__/integration/picpeakExport.test.js | 2 +- .../__tests__/integration/picpeakRoundtrip.test.js | 2 +- .../__tests__/integration/resetAdminMfaCli.test.js | 2 +- backend/__tests__/integration/setupService.test.js | 2 +- backend/__tests__/routes/adminCrmAuth.test.js | 2 +- backend/__tests__/routes/adminEventQr.test.js | 2 +- backend/__tests__/routes/adminMfa.test.js | 2 +- backend/__tests__/routes/publicContracts.test.js | 2 +- backend/__tests__/routes/publicPaymentCheck.test.js | 2 +- backend/__tests__/routes/publicQuotes.test.js | 2 +- backend/__tests__/routes/slideshowAdmin.test.js | 2 +- backend/__tests__/routes/slideshowPublic.test.js | 12 ++++++------ backend/__tests__/services/trackerFactory.test.js | 2 +- .../userManagementService.activateDelete.test.js | 2 +- .../__tests__/utils/feedbackPerGuestLimit.test.js | 2 +- .../__tests__/utils/feedbackRatingRemoval.test.js | 2 +- 16 files changed, 21 insertions(+), 21 deletions(-) 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 0b8414fb..f19fc528 100644 --- a/backend/__tests__/integration/resetAdminMfaCli.test.js +++ b/backend/__tests__/integration/resetAdminMfaCli.test.js @@ -20,7 +20,7 @@ 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__/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/adminEventQr.test.js b/backend/__tests__/routes/adminEventQr.test.js index cfec5359..33b21cc5 100644 --- a/backend/__tests__/routes/adminEventQr.test.js +++ b/backend/__tests__/routes/adminEventQr.test.js @@ -105,7 +105,7 @@ describe('admin event QR endpoints', () => { expect(res.status).toBe(200); expect(res.headers['content-type']).toBe('application/pdf'); expect(res.body.slice(0, 4).toString()).toBe('%PDF'); - }, 30000); + }, 120000); it('409s when the event has no share link', async () => { // events.share_link is NOT NULL — an empty string is the closest real-world diff --git a/backend/__tests__/routes/adminMfa.test.js b/backend/__tests__/routes/adminMfa.test.js index 973c6456..0cfaabb3 100644 --- a/backend/__tests__/routes/adminMfa.test.js +++ b/backend/__tests__/routes/adminMfa.test.js @@ -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 5a793922..af07d35b 100644 --- a/backend/__tests__/routes/slideshowPublic.test.js +++ b/backend/__tests__/routes/slideshowPublic.test.js @@ -67,11 +67,11 @@ 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. The + // chain keeps growing, and a 30s pin here blocked the 3.97.0-beta.0 + // release PR on a slow runner. Hook-argument timeouts OVERRIDE the 120s + // jest.config default (same trap as the jest.setTimeout pins raised in + // #860) — keep this at 120000, matching the config. beforeAll(async () => { ({ db, cleanup } = await bootCrmDb()); await seedMinimal(db); @@ -86,7 +86,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/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/__tests__/utils/feedbackRatingRemoval.test.js b/backend/__tests__/utils/feedbackRatingRemoval.test.js index 56722570..0e0774ba 100644 --- a/backend/__tests__/utils/feedbackRatingRemoval.test.js +++ b/backend/__tests__/utils/feedbackRatingRemoval.test.js @@ -87,7 +87,7 @@ beforeAll(async () => { uploaded_at: new Date().toISOString(), }).returning('id'); photoId = r[0]?.id ?? r[0]; -}, 30000); +}, 120000); afterAll(async () => { if (cleanup) await cleanup(); });