import test from 'node:test' import assert from 'node:assert/strict' import path from 'node:path' import { applyEnvOverrides, ENV_OVERRIDES } from './env-overrides.js' import type { RuntimeConfig } from '../types/runtime-config.js' test('applyEnvOverrides maps typed environment values without mutating base config', () => { const baseConfig = createRuntimeConfig() const config = applyEnvOverrides(baseConfig, { PORT: '4000', DATA_ROOT: './tmp/backend-data', DATABASE_SSL: 'yes', LOG_RETENTION_DAYS: '45', LOG_MAX_FILE_SIZE_MB: '12', LOG_MAX_FILES: '4', ADMIN_DEFAULT_USERS_JSON: '[{"username":"admin","password":"secret","role":"admin"}]', KAQUAN91_USER_ID: 'kaquan-user', KAQUAN91_SECRET: 'kaquan-secret', }) assert.equal(config.server.port, 4000) assert.equal(config.data.root, path.resolve('./tmp/backend-data')) assert.equal(config.database.ssl, true) assert.equal(config.logging.retentionDays, 45) assert.equal(config.logging.maxFileSizeMb, 12) assert.equal(config.logging.maxFiles, 4) assert.deepEqual(config.admin.defaultUsers, [ { username: 'admin', password: 'secret', role: 'admin' }, ]) assert.equal(config.platforms.ninetyone.userId, 'kaquan-user') assert.equal(config.platforms.ninetyone.secret, 'kaquan-secret') assert.equal(baseConfig.server.port, 3000) assert.deepEqual(baseConfig.admin.defaultUsers, []) }) test('applyEnvOverrides derives claim base url from app base url when explicit claim url is absent', () => { const config = applyEnvOverrides(createRuntimeConfig(), { APP_BASE_URL: 'https://example.test///', }) assert.equal(config.orders.claimBaseUrl, 'https://example.test/h5') }) test('CLAIM_TOKEN_TTL_HOURS=0 表示领取链接长期有效', () => { const config = applyEnvOverrides(createRuntimeConfig(), { CLAIM_TOKEN_TTL_HOURS: '0', }) assert.equal(config.orders.tokenTtlHours, 0) }) test('applyEnvOverrides prefers explicit claim base url over app base url', () => { const config = applyEnvOverrides(createRuntimeConfig(), { APP_BASE_URL: 'https://example.test', CLAIM_BASE_URL: 'https://claim.example.test/custom', }) assert.equal(config.orders.claimBaseUrl, 'https://claim.example.test/custom') }) test('applyEnvOverrides ignores empty or invalid values', () => { const config = applyEnvOverrides(createRuntimeConfig(), { PORT: 'not-a-number', DATABASE_SSL: 'maybe', ADMIN_DEFAULT_USERS_JSON: '{"username":"admin"}', LOG_LEVEL: ' ', LOG_RETENTION_DAYS: 'not-a-number', }) assert.equal(config.server.port, 3000) assert.equal(config.database.ssl, false) assert.deepEqual(config.admin.defaultUsers, []) assert.equal(config.logging.level, 'info') assert.equal(config.logging.retentionDays, 30) }) test('ENV_OVERRIDES keeps environment variable names unique', () => { const names = ENV_OVERRIDES.map((override) => override.env) assert.equal(new Set(names).size, names.length) }) function createRuntimeConfig(): RuntimeConfig { return { server: { port: 3000, }, data: { root: '/tmp/order-site', }, logging: { level: 'info', integrationLevel: 'warn', retentionDays: 30, }, retention: { rawPayloadDays: 180, taskEventDays: 180, webhookEventDays: 180, auditLogDays: 365, costExceptionDays: 60, }, database: { url: 'postgres://postgres:postgres@127.0.0.1:5432/order_site', ssl: false, maxConnections: 10, }, storage: { mode: 'minio', endpoint: 'http://127.0.0.1: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: 'http://127.0.0.1:5173/#/claim', tokenTtlHours: 24, }, admin: { sessionSecret: '', sessionIdleTtlHours: 24, sessionAbsoluteTtlHours: 168, defaultUsers: [], }, 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, }, cors: { allowedOrigins: ['*'], }, } }