190 lines
5.3 KiB
TypeScript
190 lines
5.3 KiB
TypeScript
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: ['*'],
|
|
},
|
|
}
|
|
}
|