/** * GET /api/me — regression tests for dev-auth bypass path. * * Covers: * 1. DEV_AUTH_BYPASS=true (non-production): GET /api/me returns 200 with the injected * DEV_USER identity (id=1, displayName='Dev User', color='#4A90D9'). * The OIDC guard must NOT be enforced — no Authelia env vars needed. * 2. Without DEV_AUTH_BYPASS: the OIDC middleware is still wired on /api/*. * Verified structurally by asserting oidcAuthMiddleware is called during app init * (the mock intercepts it and acts as a passthrough, confirming the mount path). * * Architecture note: * devAuthBypass() and devBypassActive in index.ts both evaluate env vars at module * load time. Tests must set process.env BEFORE importing the app module. * vitest.resetModules() ensures each test gets a fresh module registry. */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' // --------------------------------------------------------------------------- // Shared mock: DB — avoids real DB connections across all tests in this file. // This mock is hoisted by Vitest and applies to every dynamic import below. // --------------------------------------------------------------------------- vi.mock('../../src/db/client.js', () => ({ db: { execute: vi.fn().mockResolvedValue([[{ '1': 1 }]]), select: vi.fn().mockReturnValue({ from: vi.fn().mockReturnValue({ where: vi.fn().mockResolvedValue([]), innerJoin: vi.fn().mockReturnValue({ innerJoin: vi.fn().mockReturnValue({ where: vi.fn().mockResolvedValue([]), }), }), }), }), }, })) // --------------------------------------------------------------------------- // Track whether oidcAuthMiddleware was registered on the app. // The spy is set up fresh per test via beforeEach/afterEach. // --------------------------------------------------------------------------- const oidcMiddlewareSpy = vi.fn( () => async (_c: unknown, next: () => Promise) => next(), ) vi.mock('@hono/oidc-auth', () => ({ oidcAuthMiddleware: () => oidcMiddlewareSpy(), processOAuthCallback: () => async (c: { json: (v: unknown) => unknown }) => c.json({ ok: true }), getAuth: vi.fn().mockResolvedValue(null), })) // --------------------------------------------------------------------------- // Env snapshot — restored after each test to avoid cross-test pollution. // --------------------------------------------------------------------------- const originalNodeEnv = process.env.NODE_ENV const originalBypassFlag = process.env.DEV_AUTH_BYPASS afterEach(() => { process.env.NODE_ENV = originalNodeEnv if (originalBypassFlag === undefined) { delete process.env.DEV_AUTH_BYPASS } else { process.env.DEV_AUTH_BYPASS = originalBypassFlag } vi.resetModules() oidcMiddlewareSpy.mockClear() }) // --------------------------------------------------------------------------- describe('GET /api/me — dev-auth bypass (DEV_AUTH_BYPASS=true)', () => { beforeEach(() => { process.env.NODE_ENV = 'test' process.env.DEV_AUTH_BYPASS = 'true' }) it('returns 200 with the injected dev user identity', async () => { // Import AFTER setting env — index.ts reads env at module load time. const { app } = await import('../../src/index.js') const { DEV_USER } = await import('../../src/auth/devBypass.js') const res = await app.request('/api/me') expect(res.status).toBe(200) const body = await res.json() as { user: { id: number; displayName: string; color: string } } expect(body).toHaveProperty('user') expect(body.user.id).toBe(DEV_USER.id) expect(body.user.displayName).toBe(DEV_USER.displayName) expect(body.user.color).toBe(DEV_USER.color) }) it('returns id=1 and color=#4A90D9 (first palette slot)', async () => { const { app } = await import('../../src/index.js') const res = await app.request('/api/me') expect(res.status).toBe(200) const body = await res.json() as { user: { id: number; color: string } } expect(body.user.id).toBe(1) expect(body.user.color).toBe('#4A90D9') }) it('does not invoke oidcAuthMiddleware on /api/* when bypass is active', async () => { const { app } = await import('../../src/index.js') // Hit any /api/* route to trigger the middleware stack. await app.request('/api/me') // oidcAuthMiddleware() factory must NOT have been called — index.ts skips it. expect(oidcMiddlewareSpy).not.toHaveBeenCalled() }) }) describe('GET /api/me — OIDC path (no DEV_AUTH_BYPASS)', () => { beforeEach(() => { process.env.NODE_ENV = 'test' delete process.env.DEV_AUTH_BYPASS }) it('wires oidcAuthMiddleware on /api/* when bypass is not active', async () => { // Import app — devBypassActive will be false, so oidcAuthMiddleware() is called // during app construction (index.ts registers it via app.use('/api/*', ...)). await import('../../src/index.js') // The spy wraps the oidcAuthMiddleware() factory call in index.ts. // It must have been called exactly once (one app.use registration). expect(oidcMiddlewareSpy).toHaveBeenCalledTimes(1) }) it('returns 401 when no OIDC session is present (getAuth returns null)', async () => { const { app } = await import('../../src/index.js') // oidcAuthMiddleware is mocked as a passthrough; getAuth is mocked to return null. // me.ts falls through to the getAuth path and returns 401. const res = await app.request('/api/me') expect(res.status).toBe(401) const body = await res.json() as { error: string } expect(body.error).toBe('Unauthorized') }) })