import test from 'node:test' import assert from 'node:assert/strict' import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' import { ENV_OVERRIDES } from './env-overrides.js' const CURRENT_DIR = path.dirname(fileURLToPath(import.meta.url)) const BACKEND_ROOT = path.resolve(CURRENT_DIR, '../..') const WORKSPACE_ROOT = process.env.WORKSPACE_ROOT || path.resolve(BACKEND_ROOT, '../..') const SPECIAL_RUNTIME_ENV_NAMES = ['APP_BASE_URL', 'CLAIM_BASE_URL'] const DEPLOYMENT_ONLY_ENV_NAMES = [ 'APP_DOMAIN', 'BACKEND_PORT', 'CADDY_SITE_ADDR', 'CHOKIDAR_USEPOLLING', 'DB_BACKUP_KEEP', 'DB_BACKUP_OSS_PREFIX', 'KUASHOU_INDUSTRY_RATE_LIMIT_MAX', 'MINIO_API_PORT', 'MINIO_CONSOLE_PORT', 'MINIO_ROOT_PASSWORD', 'MINIO_ROOT_USER', 'NPM_CONFIG_REGISTRY', 'POSTGRES_DB', 'POSTGRES_LOG_MIN_DURATION_STATEMENT_MS', 'POSTGRES_MEM_LIMIT', 'POSTGRES_CPUS', 'POSTGRES_SHARED_BUFFERS', 'POSTGRES_EFFECTIVE_CACHE_SIZE', 'POSTGRES_WORK_MEM', 'POSTGRES_MAINTENANCE_WORK_MEM', 'POSTGRES_MAX_PARALLEL_WORKERS_PER_GATHER', 'POSTGRES_CHECKPOINT_TIMEOUT', 'POSTGRES_MAX_WAL_SIZE', 'POSTGRES_LOG_AUTOVACUUM_MIN_DURATION_MS', 'BACKEND_MEM_LIMIT', 'BACKEND_CPUS', 'BACKEND_NODE_OPTIONS', 'NODE_OPTIONS', 'WEB_MEM_LIMIT', 'WEB_CPUS', 'POSTGRES_PASSWORD', 'POSTGRES_PORT', 'POSTGRES_USER', 'TZ', 'VITE_API_TARGET', 'VITE_ALLOWED_HOSTS', 'VITE_WORKER_SITE_URL', 'WORKSPACE_ROOT', 'WORKER_SITE_ADDR', ] const KNOWN_ENV_NAMES = new Set([ ...ENV_OVERRIDES.map((override) => override.env), ...SPECIAL_RUNTIME_ENV_NAMES, ...DEPLOYMENT_ONLY_ENV_NAMES, ]) test('env example files only document known runtime or deployment variables', () => { const envFiles = [ path.join(WORKSPACE_ROOT, '.env.server.example'), path.join(WORKSPACE_ROOT, '.env.mac-docker.example'), ] const unknownNames = envFiles.flatMap((filePath) => { return extractExampleEnvNames(filePath) .filter((name) => !KNOWN_ENV_NAMES.has(name)) .map((name) => `${path.basename(filePath)}:${name}`) }) assert.deepEqual(unknownNames, []) }) test('compose environment keys only use known runtime or deployment variables', () => { const composeFiles = [ path.join(WORKSPACE_ROOT, 'docker-compose.yml'), path.join(WORKSPACE_ROOT, 'docker-compose.dev.yml'), ] const unknownNames = composeFiles.flatMap((filePath) => { return extractComposeEnvironmentKeys(filePath) .filter((name) => !KNOWN_ENV_NAMES.has(name)) .map((name) => `${path.basename(filePath)}:${name}`) }) assert.deepEqual(unknownNames, []) }) function extractExampleEnvNames(filePath: string): string[] { const text = fs.readFileSync(filePath, 'utf8') return [...text.matchAll(/^([A-Z][A-Z0-9_]*)=/gm)].map((match) => match[1]) } function extractComposeEnvironmentKeys(filePath: string): string[] { const text = fs.readFileSync(filePath, 'utf8') return [...text.matchAll(/^\s{6}([A-Z][A-Z0-9_]*):/gm)].map((match) => match[1]) }