import { describe, expect, it } from 'vitest'; import { validateRuntimeEnvironment } from './runtime-environment.js'; const validAppEnvironment = { APP_ORIGIN: 'https://stage.shd.build', ORIGIN: 'https://stage.shd.build', PASSKEY_RP_ID: 'shd.build', SCHEDULER_TOKEN: 'scheduler-secret-with-32-characters', TDR_DATABASE_PATH: '/app/data/shd-build-staging.db', TDR_AVATAR_STORAGE_PATH: '/app/data/avatars', TDR_GAME_ICON_STORAGE_PATH: '/app/data/game-icons', }; describe('validateRuntimeEnvironment', () => { it('accepts the hosted app contract without optional providers', () => { expect(validateRuntimeEnvironment('app', validAppEnvironment)).toEqual([]); }); it('reports every missing required app value', () => { expect(validateRuntimeEnvironment('app', {})).toEqual([ 'APP_ORIGIN is required.', 'ORIGIN is required.', 'PASSKEY_RP_ID is required.', 'SCHEDULER_TOKEN is required.', 'TDR_DATABASE_PATH is required.', 'TDR_AVATAR_STORAGE_PATH is required.', 'TDR_GAME_ICON_STORAGE_PATH is required.', ]); }); it('validates canonical origins and passkey hostname scope', () => { expect( validateRuntimeEnvironment('app', { ...validAppEnvironment, APP_ORIGIN: 'https://stage.shd.build/sign-in', }), ).toContain('APP_ORIGIN must contain only a URL origin.'); expect( validateRuntimeEnvironment('app', { ...validAppEnvironment, PASSKEY_RP_ID: 'example.com', }), ).toContain( 'PASSKEY_RP_ID must equal APP_ORIGIN hostname or be its registrable parent domain.', ); }); it('rejects mismatched app and adapter hostnames', () => { expect( validateRuntimeEnvironment('app', { ...validAppEnvironment, ORIGIN: 'https://other.shd.build', }), ).toContain('APP_ORIGIN and ORIGIN must use the same hostname.'); }); it('requires optional integration credentials only as pairs', () => { expect( validateRuntimeEnvironment('app', { ...validAppEnvironment, DISCORD_CLIENT_ID: 'client-id', TURNSTILE_SECRET_KEY: 'secret', }), ).toEqual([ 'DISCORD_CLIENT_ID and DISCORD_CLIENT_SECRET must be configured together.', 'TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY must be configured together.', ]); }); it('allows SMTP defaults without enabling email and validates enabled SMTP', () => { expect( validateRuntimeEnvironment('app', { ...validAppEnvironment, SMTP_PORT: '587', SMTP_SECURE: 'false', }), ).toEqual([]); expect( validateRuntimeEnvironment('app', { ...validAppEnvironment, SMTP_HOST: 'smtp.example.com', SMTP_USER: 'agent', SMTP_PORT: '70000', }), ).toEqual([ 'SMTP_HOST and SMTP_FROM must be configured together.', 'SMTP_USER and SMTP_PASSWORD must be configured together.', 'SMTP_PORT must be an integer from 1 through 65535.', ]); }); it('validates optional redirect URLs when supplied', () => { expect( validateRuntimeEnvironment('app', { ...validAppEnvironment, DISCORD_REDIRECT_URI: 'ftp://stage.shd.build/callback', }), ).toContain('DISCORD_REDIRECT_URI must be a valid HTTP or HTTPS URL.'); expect( validateRuntimeEnvironment('app', { ...validAppEnvironment, DISCORD_REDIRECT_URI: 'https://dev.shd.build/auth/discord/callback', }), ).toContain( 'DISCORD_REDIRECT_URI must use APP_ORIGIN hostname and /auth/discord/callback.', ); }); it('validates hosted paths and SMTP TLS configuration', () => { expect( validateRuntimeEnvironment('app', { ...validAppEnvironment, SMTP_SECURE: 'yes', TDR_DATABASE_PATH: './data/staging.db', }), ).toEqual([ 'SMTP_SECURE must be true or false.', 'TDR_DATABASE_PATH must be an absolute hosted path.', ]); }); it('accepts a configured scheduler and rejects missing or invalid values', () => { expect( validateRuntimeEnvironment('scheduler', { SCHEDULER_URL: 'http://app:3000/api/internal/scheduler', SCHEDULER_TOKEN: 'scheduler-secret-with-32-characters', }), ).toEqual([]); expect( validateRuntimeEnvironment('scheduler', { SCHEDULER_URL: 'not a URL', }), ).toEqual([ 'SCHEDULER_TOKEN is required.', 'SCHEDULER_URL must be a valid HTTP or HTTPS URL.', 'SCHEDULER_URL must use the internal app scheduler endpoint.', ]); }); it('requires a strong scheduler token and fixed internal endpoint', () => { expect( validateRuntimeEnvironment('scheduler', { SCHEDULER_URL: 'https://example.com/api/internal/scheduler', SCHEDULER_TOKEN: 'too-short', }), ).toEqual([ 'SCHEDULER_URL must use the internal app scheduler endpoint.', 'SCHEDULER_TOKEN must contain at least 32 non-whitespace characters.', ]); }); it('rejects unsupported runtime modes', () => { expect(() => validateRuntimeEnvironment('worker' as 'app', validAppEnvironment), ).toThrow('Unsupported runtime mode: worker'); }); });