import test from 'node:test' import assert from 'node:assert/strict' import { RuntimeConfigValidationError, assertRuntimeConfigValid, validateRuntimeConfig, } from './runtime-validation.js' import { deepMerge } from './runtime-merge.js' import type { RuntimeConfig } from '../types/runtime-config.js' test('validateRuntimeConfig allows missing admin credentials outside production', () => { const issues = validateRuntimeConfig( createRuntimeConfig({ admin: { sessionSecret: '', defaultUsers: [], }, }), { env: { NODE_ENV: 'development' }, }, ) assert.deepEqual(issues, []) }) test('validateRuntimeConfig requires admin credentials in production', () => { const issues = validateRuntimeConfig( createRuntimeConfig({ admin: { sessionSecret: '', defaultUsers: [], }, }), { env: { NODE_ENV: 'production' }, }, ) assert.deepEqual( issues.map((issue) => issue.path), ['admin.sessionSecret', 'admin.defaultUsers'], ) }) test('validateRuntimeConfig rejects unsafe production placeholders', () => { const issues = validateRuntimeConfig( createRuntimeConfig({ admin: { sessionSecret: 'change-me-long-random-session-secret', defaultUsers: [ { username: 'admin', password: 'change-me-admin-password', role: 'admin', }, ], }, }), { env: { NODE_ENV: 'production' }, }, ) assert.deepEqual( issues.map((issue) => issue.path), ['admin.sessionSecret', 'admin.defaultUsers[0].password'], ) }) test('validateRuntimeConfig accepts complete production config', () => { const issues = validateRuntimeConfig(createRuntimeConfig(), { env: { NODE_ENV: 'production' }, }) assert.deepEqual(issues, []) }) test('validateRuntimeConfig rejects wildcard CORS in production', () => { const issues = validateRuntimeConfig(createRuntimeConfig({ cors: { allowedOrigins: ['*'] } }), { env: { NODE_ENV: 'production' }, }) assert.deepEqual( issues.map((issue) => issue.path), ['cors.allowedOrigins'], ) }) test('validateRuntimeConfig catches invalid numeric and url values', () => { const issues = validateRuntimeConfig( createRuntimeConfig({ server: { port: 70000, }, database: { maxConnections: 0, }, orders: { claimBaseUrl: 'not-a-url', tokenTtlHours: -1, }, }), { env: { NODE_ENV: 'development' }, }, ) assert.deepEqual( issues.map((issue) => issue.path), ['server.port', 'database.maxConnections', 'orders.tokenTtlHours', 'orders.claimBaseUrl'], ) }) test('validateRuntimeConfig accepts zero claim token TTL for permanent links', () => { const issues = validateRuntimeConfig( createRuntimeConfig({ orders: { tokenTtlHours: 0, }, }), { env: { NODE_ENV: 'development' } }, ) assert.deepEqual(issues, []) }) test('validateRuntimeConfig rejects unknown semi-auto connector mode', () => { const issues = validateRuntimeConfig( createRuntimeConfig({ semiAuto: { defaultConnectorMode: 'remote', }, }), { env: { NODE_ENV: 'development' } }, ) assert.deepEqual( issues.map((issue) => issue.path), ['semiAuto.defaultConnectorMode'], ) }) test('assertRuntimeConfigValid throws a readable validation error', () => { assert.throws( () => { assertRuntimeConfigValid( createRuntimeConfig({ server: { port: 0, }, }), ) }, (error) => { assert.equal(error instanceof RuntimeConfigValidationError, true) assert.match((error as Error).message, /运行时配置校验失败/) assert.match((error as Error).message, /server\.port/) return true }, ) }) function createRuntimeConfig(overrides: Record = {}): RuntimeConfig { return deepMerge( { server: { port: 3000, }, data: { root: '/tmp/order-site', }, logging: { level: 'info', integrationLevel: 'warn', }, retention: { costExceptionDays: 60, }, database: { url: 'postgres://postgres:postgres@postgres:5432/order_site', ssl: false, maxConnections: 10, }, storage: { mode: 'minio', endpoint: 'http://minio:9000', bucket: 'order-site', accessKeyId: 'minioadmin', secretAccessKey: 'minioadmin', region: 'us-east-1', maxUploadSizeMb: 10, oss: { endpoint: '', bucket: '', accessKeyId: '', secretAccessKey: '', region: 'oss-cn-hangzhou', }, }, orders: { claimBaseUrl: 'https://order.example.test/#/claim', tokenTtlHours: 24, }, semiAuto: { defaultConnectorMode: 'embedded', connectorBaseUrl: '', connectorApiKey: '', connectorTimeoutMs: 15000, huya: { bindActId: 9271, outerActId: '17096', roomPid: '1199650619883', }, }, admin: { sessionSecret: '0123456789abcdef0123456789abcdef', sessionIdleTtlHours: 24, sessionAbsoluteTtlHours: 168, defaultUsers: [ { username: 'admin', password: 'Qp7xT2mN9vR4', role: 'admin', }, ], }, platforms: { ninetyone: { userId: '', secret: '', version: '1.0', shopId: '91kaquan', shopName: '91卡券', timestampToleranceSeconds: 600, cardsEncoding: 'aes-256-ecb-base64', }, kuaishouIndustry: { appKey: '', appSecret: '', signSecret: '', messageSecret: '', provider: 'kuaishou-industry', platform: 'kuaishou', shopId: 'kuaishou-industry', shopName: '快手行业电子凭证', version: '1', }, affiliateDash: { enabled: true, baseUrl: '', appKey: '', appSecret: '', callbackSecret: '', timeoutMs: 10000, notifyUrl: '', timestampToleranceSeconds: 300, preferredMatchEnabled: true, skuMapping: {}, }, }, worker: { sms: { provider: 'mock', accessKeyId: '', accessKeySecret: '', signName: '', templateCode: '', codeTtlSeconds: 300, cooldownSeconds: 60, dailyLimit: 10, }, orderTimelineVisible: false, sessionIdleTtlHours: 168, sessionAbsoluteTtlHours: 720, }, }, overrides, ) as RuntimeConfig }