test(12-02): isSetupLocked() real impl + RED-first setup route tests

- Implement real isSetupLocked() in setupGuard.ts: reads app_config.setup_complete
  (returns true if value==='true'); else checks member_credentials row + VAPID env
  for effective-config branch (D-10)
- Re-queries DB fresh every call — no module-level cache (D-10/Pitfall 8)
- Convert Wave-0 it.todo() scaffolds into real integration tests (17 tests RED)
- RED-first 423 guard test: POST /complete twice → first 200, second 423 (Pitfall 8)
- D-10 effective-config tests: 423 when credRow AND VAPID env; NOT 423 otherwise
- 2 'does NOT return 423' tests pass (404 ≠ 423); all others RED pending Task 2 router
This commit is contained in:
Lucas Berger
2026-06-15 13:53:53 -04:00
parent c6d0db0119
commit 4748d578e7
2 changed files with 499 additions and 93 deletions
+22 -4
View File
@@ -10,12 +10,30 @@
* immediately on the next call — even if two requests arrive within the same
* event-loop tick. The per-call freshness pattern mirrors db.select() in health.ts.
*
* Wave-0 stub (Plan 01): returns false (setup always appears incomplete).
* Two-branch lock logic (D-10):
* 1. Explicit: app_config.setup_complete === 'true'
* 2. Effective: a member_credentials row exists AND VAPID_PRIVATE_KEY + VAPID_PUBLIC_KEY env set
*
* Real implementation (Plan 02): reads app_config.setup_complete + checks
* member_credentials + VAPID env (effective-config branch, D-10).
*/
import { db } from '../db/client.js';
import { appConfig, memberCredentials } from '../db/schema.js';
import { eq } from 'drizzle-orm';
/** Returns true if the wizard is already locked. Re-evaluated fresh — NEVER cache at module level. */
export async function isSetupLocked(): Promise<boolean> {
// STUB — Wave-0 placeholder. Real impl: Plan 02.
// Re-evaluated fresh on every call — NEVER cache at module level (D-10).
return false;
// Check 1: explicit setup_complete flag in app_config
const [flagRow] = await db
.select({ value: appConfig.value })
.from(appConfig)
.where(eq(appConfig.key, 'setup_complete'))
.limit(1);
if (flagRow?.value === 'true') return true;
// Check 2: effective configuration — member_credentials row exists AND VAPID env set (D-10)
const [credRow] = await db.select({ id: memberCredentials.id }).from(memberCredentials).limit(1);
const vapidPresent = !!process.env.VAPID_PRIVATE_KEY && !!process.env.VAPID_PUBLIC_KEY;
return !!credRow && vapidPresent;
}