From 89a4bf28ce7436c633d6125ea970532a30c058ea Mon Sep 17 00:00:00 2001 From: agent_vscode Date: Mon, 6 Jul 2026 17:24:24 +0300 Subject: [PATCH] =?UTF-8?q?test(auth):=20de-flake=20verify-user-credential?= =?UTF-8?q?s.live.spec=20=E2=80=94=20hoist=20bcrypt=20hash=20+=2030s=20tim?= =?UTF-8?q?eout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under a fully parallel `pnpm -r test` run the suite computed five separate bcrypt cost-12 hashes (one per test, ~300ms idle, multi-second with all cores saturated) and tripped jest's default 5s per-test timeout ("DISABLED user" test, suite 31s under load vs 6s isolated). - compute the hash ONCE in a top-level beforeAll (covers both describe blocks) and share the read-only string across the five former call sites - jest.setTimeout(30_000) at module scope so the per-test bcrypt compares inside verifyUserCredentials get headroom under load too No change to test names, assertions, order, or the CREDENTIALS_MISMATCH contract semantics. Suite: 8/8 green, 4.8s isolated (was ~6s). Co-Authored-By: Claude Fable 5 --- .../verify-user-credentials.live.spec.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/server/src/core/auth/services/verify-user-credentials.live.spec.ts b/apps/server/src/core/auth/services/verify-user-credentials.live.spec.ts index 5504b3bd..1bd2f4d8 100644 --- a/apps/server/src/core/auth/services/verify-user-credentials.live.spec.ts +++ b/apps/server/src/core/auth/services/verify-user-credentials.live.spec.ts @@ -23,8 +23,21 @@ import { hashPassword } from '../../../common/helpers'; * unthrottled password-guessing oracle. */ +// bcrypt cost-12 hashing/compare takes ~300ms idle but multiple seconds when +// parallel jest workers saturate all CPU cores; the 5s default flakes. +jest.setTimeout(30_000); + const WORKSPACE_ID = 'ws-1'; +let passwordHash: string; + +// Hoist the expensive work: compute ONE bcrypt cost-12 hash shared by all +// tests instead of five. The hash is a read-only string and each test builds +// its own user object around it, so sharing is safe. +beforeAll(async () => { + passwordHash = await hashPassword('correct-horse'); +}, 30_000); + // Build an AuthService with the dependencies verifyUserCredentials/login touch // stubbed, and a userRepo whose findByEmail is overridable per test. Only the // collaborators actually reached on these paths need real behaviour; the rest @@ -95,7 +108,6 @@ describe('AuthService.verifyUserCredentials (live credentials-mismatch contract) it('DISABLED user -> throws exactly CREDENTIALS_MISMATCH_MESSAGE (no password oracle)', async () => { // A deactivated user must be indistinguishable from a wrong password: same // message, before any password comparison. - const passwordHash = await hashPassword('correct-horse'); const disabledUser = { id: 'u-1', email: 'disabled@example.com', @@ -117,7 +129,6 @@ describe('AuthService.verifyUserCredentials (live credentials-mismatch contract) }); it('WRONG password -> throws exactly CREDENTIALS_MISMATCH_MESSAGE', async () => { - const passwordHash = await hashPassword('correct-horse'); const user = { id: 'u-1', email: 'user@example.com', @@ -139,7 +150,6 @@ describe('AuthService.verifyUserCredentials (live credentials-mismatch contract) }); it('CORRECT credentials -> resolves the matched user (no side effects here)', async () => { - const passwordHash = await hashPassword('correct-horse'); const user = { id: 'u-1', email: 'user@example.com', @@ -179,7 +189,6 @@ describe('AuthService.login (live credentials-mismatch contract via verifyUserCr }); it('WRONG password -> login throws exactly CREDENTIALS_MISMATCH_MESSAGE', async () => { - const passwordHash = await hashPassword('correct-horse'); const user = { id: 'u-1', email: 'user@example.com', @@ -201,7 +210,6 @@ describe('AuthService.login (live credentials-mismatch contract via verifyUserCr }); it('CORRECT credentials -> login mints the session (the side-effecting path)', async () => { - const passwordHash = await hashPassword('correct-horse'); const user = { id: 'u-1', email: 'user@example.com',