From 8007fac9471a5c137cebc3ec82d26f011877ff4a Mon Sep 17 00:00:00 2001 From: Saravana Achu Mac Date: Tue, 5 May 2026 09:42:20 -0700 Subject: [PATCH] test(config): cover production validation --- backend/src/lib/config.test.ts | 137 +++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 backend/src/lib/config.test.ts diff --git a/backend/src/lib/config.test.ts b/backend/src/lib/config.test.ts new file mode 100644 index 0000000..76031b2 --- /dev/null +++ b/backend/src/lib/config.test.ts @@ -0,0 +1,137 @@ +import { describe, expect, it } from 'vitest'; +import { DEFAULT_DEV_JWT_SECRET, envSchema, parseConfig } from './config.js'; + +const SAFE_PROD_ENV = { + NODE_ENV: 'production', + JWT_SECRET: 'prod-secret-at-least-32-characters', + DB_PROVIDER: 'cosmos', + COSMOS_ENDPOINT: 'https://cosmos.example.com', + COSMOS_KEY: 'cosmos-key', + COSMOS_DATABASE: 'bytelyst', + FIELD_ENCRYPT_ENABLED: 'true', + FIELD_ENCRYPT_KEY_PROVIDER: 'env', + FIELD_ENCRYPT_KEY: 'field-key-material', +} satisfies Record; + +function expectConfigIssue(env: Record, path: string, message: string): void { + const result = envSchema.safeParse(env); + expect(result.success).toBe(false); + if (result.success) return; + + expect(result.error.issues).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + path: [path], + message, + }), + ]) + ); +} + +describe('backend config', () => { + it('keeps development ergonomics for local memory mode and empty JWT env values', () => { + const config = parseConfig({ + NODE_ENV: 'development', + JWT_SECRET: '', + DB_PROVIDER: 'memory', + FIELD_ENCRYPT_KEY_PROVIDER: 'memory', + TELEMETRY_ENABLED: 'false', + FEATURE_FLAGS_ENABLED: '0', + }); + + expect(config.JWT_SECRET).toBe(DEFAULT_DEV_JWT_SECRET); + expect(config.DB_PROVIDER).toBe('memory'); + expect(config.TELEMETRY_ENABLED).toBe(false); + expect(config.FEATURE_FLAGS_ENABLED).toBe(false); + }); + + it('accepts a safe production configuration', () => { + const config = parseConfig(SAFE_PROD_ENV); + + expect(config.NODE_ENV).toBe('production'); + expect(config.DB_PROVIDER).toBe('cosmos'); + expect(config.FIELD_ENCRYPT_ENABLED).toBe(true); + }); + + it('rejects missing production JWT secret because it would fall back to the dev default', () => { + expectConfigIssue( + { ...SAFE_PROD_ENV, JWT_SECRET: undefined }, + 'JWT_SECRET', + 'Production JWT_SECRET must not use the development default' + ); + }); + + it('rejects the development JWT secret in production', () => { + expectConfigIssue( + { ...SAFE_PROD_ENV, JWT_SECRET: DEFAULT_DEV_JWT_SECRET }, + 'JWT_SECRET', + 'Production JWT_SECRET must not use the development default' + ); + }); + + it('rejects short production JWT secrets', () => { + expectConfigIssue( + { ...SAFE_PROD_ENV, JWT_SECRET: 'short-prod-secret' }, + 'JWT_SECRET', + 'Production JWT_SECRET must be at least 32 characters' + ); + }); + + it('rejects memory DB in production', () => { + expectConfigIssue( + { ...SAFE_PROD_ENV, DB_PROVIDER: 'memory' }, + 'DB_PROVIDER', + 'Production DB_PROVIDER must be cosmos' + ); + }); + + it('rejects missing production Cosmos credentials', () => { + expectConfigIssue( + { ...SAFE_PROD_ENV, COSMOS_ENDPOINT: '', COSMOS_KEY: '', COSMOS_DATABASE: '' }, + 'COSMOS_ENDPOINT', + 'COSMOS_ENDPOINT is required in production' + ); + expectConfigIssue( + { ...SAFE_PROD_ENV, COSMOS_ENDPOINT: '', COSMOS_KEY: '', COSMOS_DATABASE: '' }, + 'COSMOS_KEY', + 'COSMOS_KEY is required in production' + ); + expectConfigIssue( + { ...SAFE_PROD_ENV, COSMOS_ENDPOINT: '', COSMOS_KEY: '', COSMOS_DATABASE: '' }, + 'COSMOS_DATABASE', + 'COSMOS_DATABASE is required in production' + ); + }); + + it('rejects disabled field encryption in production', () => { + expectConfigIssue( + { ...SAFE_PROD_ENV, FIELD_ENCRYPT_ENABLED: 'false' }, + 'FIELD_ENCRYPT_ENABLED', + 'Field encryption must be enabled in production' + ); + }); + + it('rejects production memory encryption provider', () => { + expectConfigIssue( + { ...SAFE_PROD_ENV, FIELD_ENCRYPT_KEY_PROVIDER: 'memory', FIELD_ENCRYPT_KEY: '' }, + 'FIELD_ENCRYPT_KEY_PROVIDER', + 'Production FIELD_ENCRYPT_KEY_PROVIDER must be akv or env' + ); + }); + + it('requires key material when FIELD_ENCRYPT_KEY_PROVIDER=env', () => { + expectConfigIssue( + { ...SAFE_PROD_ENV, FIELD_ENCRYPT_KEY_PROVIDER: 'env', FIELD_ENCRYPT_KEY: '' }, + 'FIELD_ENCRYPT_KEY', + 'FIELD_ENCRYPT_KEY is required when FIELD_ENCRYPT_KEY_PROVIDER=env' + ); + }); + + it('requires a Key Vault URL when FIELD_ENCRYPT_KEY_PROVIDER=akv', () => { + expectConfigIssue( + { ...SAFE_PROD_ENV, FIELD_ENCRYPT_KEY_PROVIDER: 'akv', FIELD_ENCRYPT_KEY: '', AZURE_KEYVAULT_URL: '' }, + 'AZURE_KEYVAULT_URL', + 'AZURE_KEYVAULT_URL is required when FIELD_ENCRYPT_KEY_PROVIDER=akv' + ); + }); +});