/** * GET /api/login — tests for the top-level-nav login entry point. * * Covers: * 1. DEV_AUTH_BYPASS=true: GET /api/login returns 302 → '/'. * Guard is not mounted; handler fires directly. * 2. Without DEV_AUTH_BYPASS: oidcAuthMiddleware passthrough mock lets the * request through; handler returns 302 → '/' (mirroring me.test.ts OIDC path). * * Flow (production): * Unauthenticated top-level nav → guard 302 → Authelia → /callback → `continue` * cookie returns browser to /api/login (now authenticated) → handler 302 → /. * Under DEV_AUTH_BYPASS, guard is not mounted, so /api/login reaches the handler * directly and redirects to '/'. */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' // --------------------------------------------------------------------------- // Shared mock: DB — avoids real DB connections across all tests in this file. // Hoisted so it 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. // --------------------------------------------------------------------------- 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. // --------------------------------------------------------------------------- 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/login — dev-auth bypass (DEV_AUTH_BYPASS=true)', () => { beforeEach(() => { process.env.NODE_ENV = 'test' process.env.DEV_AUTH_BYPASS = 'true' }) it('returns 302 with location "/" (bypass active, guard not mounted)', async () => { const { app } = await import('../../src/index.js') const res = await app.request('/api/login') expect(res.status).toBe(302) expect(res.headers.get('location')).toBe('/') }) it('does not invoke oidcAuthMiddleware when bypass is active', async () => { const { app } = await import('../../src/index.js') await app.request('/api/login') expect(oidcMiddlewareSpy).not.toHaveBeenCalled() }) }) describe('GET /api/login — OIDC path (no DEV_AUTH_BYPASS)', () => { beforeEach(() => { process.env.NODE_ENV = 'test' delete process.env.DEV_AUTH_BYPASS }) it('returns 302 with location "/" when OIDC passthrough allows the request', async () => { const { app } = await import('../../src/index.js') // oidcAuthMiddleware is mocked as a passthrough — request reaches the handler. const res = await app.request('/api/login') expect(res.status).toBe(302) expect(res.headers.get('location')).toBe('/') }) })