From 652dd6943b8d3e14b2db88409366814b031adab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 20 Oct 2026 20:56:08 +0200 Subject: [PATCH] test(auth): read the password hash off the stored row in proof tests The proof gates now read whether an account has a password from its own row rather than from the per-request user, so the suites that drive them mock that read instead of carrying the hash on the session user. --- .../grants/__tests__/invite-recent-proof.test.ts | 7 +++++-- .../api/dashboard/snapshot/__tests__/route.test.ts | 4 ++-- .../fhir/__tests__/capability-and-everything.test.ts | 2 ++ src/app/api/mcp/oauth/__tests__/authorize.test.ts | 2 ++ src/app/api/mcp/tokens/__tests__/route.test.ts | 2 ++ src/app/api/share-links/__tests__/route.test.ts | 2 ++ src/lib/__tests__/require-fresh-mfa.test.ts | 11 ++++++++++- src/lib/__tests__/require-recent-proof.test.ts | 6 +++++- 8 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/app/api/account/grants/__tests__/invite-recent-proof.test.ts b/src/app/api/account/grants/__tests__/invite-recent-proof.test.ts index e4c987074..f450f87e6 100644 --- a/src/app/api/account/grants/__tests__/invite-recent-proof.test.ts +++ b/src/app/api/account/grants/__tests__/invite-recent-proof.test.ts @@ -14,7 +14,11 @@ vi.mock("@/lib/db", () => ({ session: { findUnique: vi.fn() }, webauthnMfaCredential: { count: vi.fn(async () => 0) }, passkey: { count: vi.fn(async () => 0) }, - user: { findFirst: vi.fn() }, + user: { + findFirst: vi.fn(), + // The stored row: the password hash is not on the session user. + findUnique: vi.fn(async () => ({ passwordHash: "argon" })), + }, accountGrant: { create: vi.fn(), findFirst: vi.fn() }, }, })); @@ -50,7 +54,6 @@ const OWNER = { role: "USER", username: "owner", totpConfirmedAt: null, - passwordHash: "argon", }; function invite(): NextRequest { diff --git a/src/app/api/dashboard/snapshot/__tests__/route.test.ts b/src/app/api/dashboard/snapshot/__tests__/route.test.ts index a2226125c..e161146af 100644 --- a/src/app/api/dashboard/snapshot/__tests__/route.test.ts +++ b/src/app/api/dashboard/snapshot/__tests__/route.test.ts @@ -17,7 +17,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextRequest } from "next/server"; vi.mock("@/lib/db", () => ({ - prisma: {}, + // The cached briefing text is read off its own row, not the session user. + prisma: { user: { findUnique: async () => null } }, })); vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn(), @@ -71,7 +72,6 @@ const SESSION_OK = { glucoseUnit: "mg/dL", onboardingTourCompleted: true, disableCoach: false, - insightsCachedText: null, insightsCachedAt: null, dashboardWidgetsJson: null, }, diff --git a/src/app/api/fhir/__tests__/capability-and-everything.test.ts b/src/app/api/fhir/__tests__/capability-and-everything.test.ts index c402cf539..d3709b9dd 100644 --- a/src/app/api/fhir/__tests__/capability-and-everything.test.ts +++ b/src/app/api/fhir/__tests__/capability-and-everything.test.ts @@ -28,6 +28,8 @@ vi.mock("@/lib/db", () => ({ }, webauthnMfaCredential: { count: vi.fn(async () => 0) }, passkey: { count: vi.fn(async () => 0) }, + // The password hash is read off the stored row; none here. + user: { findUnique: vi.fn(async () => null) }, }, })); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); diff --git a/src/app/api/mcp/oauth/__tests__/authorize.test.ts b/src/app/api/mcp/oauth/__tests__/authorize.test.ts index 164da251e..24f12bb88 100644 --- a/src/app/api/mcp/oauth/__tests__/authorize.test.ts +++ b/src/app/api/mcp/oauth/__tests__/authorize.test.ts @@ -35,6 +35,8 @@ vi.mock("@/lib/db", () => ({ }, webauthnMfaCredential: { count: vi.fn(async () => 0) }, passkey: { count: vi.fn(async () => 0) }, + // The password hash is read off the stored row; none here. + user: { findUnique: vi.fn(async () => null) }, }, })); vi.mock("@/lib/app-settings", () => ({ diff --git a/src/app/api/mcp/tokens/__tests__/route.test.ts b/src/app/api/mcp/tokens/__tests__/route.test.ts index e389130e3..98c3e136e 100644 --- a/src/app/api/mcp/tokens/__tests__/route.test.ts +++ b/src/app/api/mcp/tokens/__tests__/route.test.ts @@ -19,6 +19,8 @@ vi.mock("@/lib/db", () => ({ }, webauthnMfaCredential: { count: vi.fn(async () => 0) }, passkey: { count: vi.fn(async () => 0) }, + // The password hash is read off the stored row; none here. + user: { findUnique: vi.fn(async () => null) }, }, })); vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); diff --git a/src/app/api/share-links/__tests__/route.test.ts b/src/app/api/share-links/__tests__/route.test.ts index 33509c3c9..bc5e3ad24 100644 --- a/src/app/api/share-links/__tests__/route.test.ts +++ b/src/app/api/share-links/__tests__/route.test.ts @@ -28,6 +28,8 @@ vi.mock("@/lib/db", () => ({ }, webauthnMfaCredential: { count: vi.fn(async () => 0) }, passkey: { count: vi.fn(async () => 0) }, + // The password hash is read off the stored row; none here. + user: { findUnique: vi.fn(async () => null) }, }, })); diff --git a/src/lib/__tests__/require-fresh-mfa.test.ts b/src/lib/__tests__/require-fresh-mfa.test.ts index 28104c009..90001aea4 100644 --- a/src/lib/__tests__/require-fresh-mfa.test.ts +++ b/src/lib/__tests__/require-fresh-mfa.test.ts @@ -5,6 +5,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; vi.mock("@/lib/db", () => ({ prisma: { session: { findUnique: vi.fn() }, + user: { findUnique: vi.fn() }, webauthnMfaCredential: { count: vi.fn() }, passkey: { count: vi.fn() }, }, @@ -50,7 +51,15 @@ beforeEach(() => { vi.resetAllMocks(); }); -function mockSession(user: unknown) { +function mockSession(user: { + id: string; + totpConfirmedAt: Date | null; + passwordHash?: string | null; +}) { + // The password hash is read off the stored row, not the session user. + vi.mocked(prisma.user.findUnique).mockResolvedValue({ + passwordHash: user.passwordHash ?? null, + } as never); vi.mocked(getSession).mockResolvedValue({ session: { id: "sess-1", expiresAt: new Date(Date.now() + 1e6) }, user, diff --git a/src/lib/__tests__/require-recent-proof.test.ts b/src/lib/__tests__/require-recent-proof.test.ts index 8c0d09fd1..136797db7 100644 --- a/src/lib/__tests__/require-recent-proof.test.ts +++ b/src/lib/__tests__/require-recent-proof.test.ts @@ -72,13 +72,17 @@ beforeEach(() => { }); function cookie( - user: unknown, + user: { id: string; passwordHash?: string | null }, stamps: { mfaVerifiedAt?: Date | null; createdAt?: Date; reproofAt?: Date | null; }, ) { + // The password hash is read off the stored row, not the session user. + vi.mocked(prisma.user.findUnique).mockResolvedValue({ + passwordHash: user.passwordHash ?? null, + } as never); vi.mocked(getSession).mockResolvedValue({ session: { id: "sess-1", expiresAt: new Date(Date.now() + 1e6) }, user,