feat(19-05): global-setup local_credentials seed + login.spec.ts + CI harness env

- global-setup.ts: TRUNCATE local_credentials + seed devuser/devpass (PHC scrypt inline)
- Create login.spec.ts: real-login-form e2e (gate redirect, wrong-password error, correct login)
- ci.yml: add LOCAL_SESSION_SECRET dev value + local_credentials seed step in harness job
- Fix all test mocks: add devSessionCookieMiddleware no-op to vi.mock(devBypass.js) blocks
  in admin/setup/push/lists/localAuth/authMode/requireAdmin tests (Rule 1 - Bug: missing export)
- Full API suite: 446/446 tests pass; pnpm typecheck: exit 0
This commit is contained in:
Lucas Berger
2026-06-17 17:21:05 -04:00
parent 82391874ee
commit 1f94dc5eb7
10 changed files with 233 additions and 0 deletions
+23
View File
@@ -21,6 +21,16 @@
*/
import mysql from 'mysql2/promise';
import { scryptSync, randomBytes } from 'node:crypto';
// ── Inline scrypt PHC hashPassword ───────────────────────────────────────────
// global-setup is plain Node.js (no @playwright/test, cannot import compiled TS).
// Copy of apps/api/src/auth/localCredentials.ts hashPassword (Pitfall 11).
function hashPasswordInline(password: string): string {
const salt = randomBytes(16);
const hash = scryptSync(password, salt, 32, { N: 16384, r: 8, p: 1 });
return ['scrypt', 16384, 8, 1, salt.toString('base64url'), hash.toString('base64url')].join('$');
}
export default async function globalSetup(): Promise<void> {
// ── Step 0: Fail-closed environment guard (CR-01 — data-loss prevention) ─────
@@ -107,6 +117,7 @@ export default async function globalSetup(): Promise<void> {
await conn.execute('TRUNCATE TABLE list_shares');
await conn.execute('TRUNCATE TABLE lists');
await conn.execute('TRUNCATE TABLE calendar_events');
await conn.execute('TRUNCATE TABLE local_credentials');
await conn.execute('SET FOREIGN_KEY_CHECKS=1');
// Phase 18: clear any stored household timezone so the timezone spec always
@@ -146,6 +157,18 @@ export default async function globalSetup(): Promise<void> {
ON DUPLICATE KEY UPDATE fastmail_email='dev@e2e.local'`,
);
// Phase 19 — Option C (AUTH-LOCAL-16): seed local_credentials for the dev user (id=1).
// devSessionCookieMiddleware issues a local-session cookie so the PWA login gate skips
// /login and the existing harness specs still reach the authed app unchanged.
// A dedicated login.spec.ts clears the cookie to test the real login form.
// hashPasswordInline is inlined (Pitfall 11 — plain Node.js, cannot import compiled TS).
await conn.execute(
`INSERT INTO local_credentials (user_id, username, password_hash)
VALUES (1, 'devuser', ?)
ON DUPLICATE KEY UPDATE password_hash = VALUES(password_hash)`,
[hashPasswordInline('devpass')],
);
// CI guard (Pitfall 4): ensure calendar row id=10 exists before inserting events.
// INSERT IGNORE is a no-op if the row already exists (dev DB), creates it if not (CI fresh DB).
await conn.execute(