219 lines
7.1 KiB
TypeScript
219 lines
7.1 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { getServerConfig } from '../src/config/server-config';
|
|
|
|
const env = {
|
|
JWT_SECRET: 'test-configuration-secret-12345678901234567890',
|
|
DATABASE_URL:
|
|
'postgresql://test:test@127.0.0.1:1/doodle_test?schema=public',
|
|
WECHAT_MINI_APP_ID: 'wx0123456789abcdef',
|
|
WECHAT_MINI_APP_SECRET: 'test-only-wechat-secret',
|
|
};
|
|
|
|
test('启动配置必须提供有效 JWT 密钥,不使用共享默认密钥', () => {
|
|
for (const secret of [
|
|
undefined,
|
|
'',
|
|
'short',
|
|
'change-me-123456789012345678901234567890',
|
|
]) {
|
|
assert.throws(
|
|
() =>
|
|
getServerConfig(
|
|
secret === undefined ? {} : { JWT_SECRET: secret },
|
|
),
|
|
/JWT_SECRET/,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('Swagger 开发默认开启,生产默认关闭,支持显式开关', () => {
|
|
assert.equal(getServerConfig(env).swaggerEnabled, true);
|
|
assert.equal(
|
|
getServerConfig({ ...env, NODE_ENV: 'production' }).swaggerEnabled,
|
|
false,
|
|
);
|
|
assert.equal(
|
|
getServerConfig({ ...env, SWAGGER_ENABLED: 'false' }).swaggerEnabled,
|
|
false,
|
|
);
|
|
});
|
|
|
|
test('真实登录接入后微信配置必须完整,缺省或留空都拒绝启动', () => {
|
|
for (const entry of [
|
|
{ WECHAT_MINI_APP_ID: undefined, WECHAT_MINI_APP_SECRET: undefined },
|
|
{ WECHAT_MINI_APP_ID: '', WECHAT_MINI_APP_SECRET: '' },
|
|
{ WECHAT_MINI_APP_ID: ' ', WECHAT_MINI_APP_SECRET: '\t' },
|
|
]) {
|
|
assert.throws(
|
|
() => getServerConfig({ ...env, ...entry }),
|
|
/WECHAT_MINI_APP/,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('成对读取微信配置并去除首尾空白', () => {
|
|
const config = getServerConfig({
|
|
...env,
|
|
WECHAT_MINI_APP_ID: ' wx0123456789abcdef ',
|
|
WECHAT_MINI_APP_SECRET: ' test-only-wechat-secret ',
|
|
});
|
|
assert.equal(config.wechatMiniAppId, 'wx0123456789abcdef');
|
|
assert.equal(config.wechatMiniAppSecret, 'test-only-wechat-secret');
|
|
});
|
|
|
|
test('拒绝不完整或含内部空白的微信配置,错误不回显配置值', () => {
|
|
for (const entry of [
|
|
{
|
|
WECHAT_MINI_APP_ID: 'wx0123456789abcdef',
|
|
WECHAT_MINI_APP_SECRET: undefined,
|
|
},
|
|
{
|
|
WECHAT_MINI_APP_ID: undefined,
|
|
WECHAT_MINI_APP_SECRET: 'test-only-wechat-secret',
|
|
},
|
|
{
|
|
WECHAT_MINI_APP_ID: 'wx0123456789abcdef',
|
|
WECHAT_MINI_APP_SECRET: ' ',
|
|
},
|
|
{
|
|
WECHAT_MINI_APP_ID: 'wx0123 456789abcdef',
|
|
WECHAT_MINI_APP_SECRET: 'test-only-wechat-secret',
|
|
},
|
|
{
|
|
WECHAT_MINI_APP_ID: 'wx0123456789abcdef',
|
|
WECHAT_MINI_APP_SECRET: 'test-only\nwechat-secret',
|
|
},
|
|
]) {
|
|
assert.throws(
|
|
() => getServerConfig({ ...env, ...entry }),
|
|
(error: unknown) => {
|
|
assert.ok(error instanceof Error);
|
|
assert.match(error.message, /WECHAT_MINI_APP/);
|
|
for (const value of Object.values(entry)) {
|
|
if (value?.trim())
|
|
assert.ok(!error.message.includes(value));
|
|
}
|
|
return true;
|
|
},
|
|
);
|
|
}
|
|
});
|
|
|
|
test('数据库连接必须为 PostgreSQL public schema,校验不回显含密码的 URL', () => {
|
|
for (const databaseUrl of [
|
|
'',
|
|
'mysql://user:private-password@localhost/db',
|
|
'postgresql:///db',
|
|
'postgresql://localhost',
|
|
'postgresql://localhost/db?schema=other',
|
|
])
|
|
assert.throws(
|
|
() => getServerConfig({ ...env, DATABASE_URL: databaseUrl }),
|
|
/DATABASE_URL/,
|
|
);
|
|
assert.equal(getServerConfig(env).databaseUrl, env.DATABASE_URL);
|
|
});
|
|
|
|
test('拒绝非法 body、JWT 有效期、日志级别、布尔开关和跨域配置', () => {
|
|
for (const entry of [
|
|
{ API_BODY_LIMIT_BYTES: '0' },
|
|
{ API_BODY_LIMIT_BYTES: '1e6' },
|
|
{ JWT_EXPIRES_IN_SECONDS: '0' },
|
|
{ SWAGGER_ENABLED: 'yes' },
|
|
{ LOG_LEVEL: 'all' },
|
|
{ NODE_ENV: 'staging' },
|
|
{ JWT_ISSUER: ' ' },
|
|
{ CORS_ORIGINS: '*' },
|
|
{ CORS_ORIGINS: 'https://example.com/path' },
|
|
{ TRUSTED_PROXY_CIDRS: 'true' },
|
|
])
|
|
assert.throws(() => getServerConfig({ ...env, ...entry }));
|
|
assert.deepEqual(
|
|
getServerConfig({
|
|
...env,
|
|
CORS_ORIGINS: 'https://example.com,http://localhost:3001',
|
|
}).corsOrigins,
|
|
['https://example.com', 'http://localhost:3001'],
|
|
);
|
|
});
|
|
|
|
test('题型新品窗口与公共资源域名校验,缺省域名不构造七牛地址', () => {
|
|
assert.equal(getServerConfig(env).worksheetNewWindowDays, 30);
|
|
assert.equal(getServerConfig(env).qiniuPublicDomain, null);
|
|
assert.equal(
|
|
getServerConfig({
|
|
...env,
|
|
QINIU_PUBLIC_DOMAIN: 'https://assets.example.test/',
|
|
}).qiniuPublicDomain,
|
|
'https://assets.example.test',
|
|
);
|
|
for (const value of ['0', '366', '1e2', ''])
|
|
assert.throws(() =>
|
|
getServerConfig({ ...env, WORKSHEET_NEW_WINDOW_DAYS: value }),
|
|
);
|
|
for (const value of [
|
|
'http://assets.example.test',
|
|
'https://user:secret@example.test',
|
|
'https://example.test/path',
|
|
'https://example.test?sign=private',
|
|
'https://example.test#hash',
|
|
])
|
|
assert.throws(
|
|
() => getServerConfig({ ...env, QINIU_PUBLIC_DOMAIN: value }),
|
|
/QINIU_PUBLIC_DOMAIN/,
|
|
);
|
|
});
|
|
|
|
test('七牛空间与路径前缀校验,允许空前缀且不回显非法配置', () => {
|
|
const config = getServerConfig({
|
|
...env,
|
|
QINIU_KEY_PREFIX: 'doodle-assets///',
|
|
QINIU_BUCKET: 'doodle-cn',
|
|
});
|
|
assert.equal(config.qiniuKeyPrefix, 'doodle-assets/');
|
|
assert.equal(config.qiniuBucket, 'doodle-cn');
|
|
assert.equal(
|
|
getServerConfig({ ...env, QINIU_KEY_PREFIX: '' }).qiniuKeyPrefix,
|
|
'',
|
|
);
|
|
for (const value of [
|
|
'/absolute',
|
|
'/',
|
|
'../escape',
|
|
'a/../b',
|
|
'a//b',
|
|
'a\\b',
|
|
'a\n',
|
|
])
|
|
assert.throws(
|
|
() => getServerConfig({ ...env, QINIU_KEY_PREFIX: value }),
|
|
/QINIU_KEY_PREFIX/,
|
|
);
|
|
assert.throws(
|
|
() => getServerConfig({ ...env, QINIU_BUCKET: 'bad/bucket' }),
|
|
/QINIU_BUCKET/,
|
|
);
|
|
});
|
|
|
|
test('七牛上传凭据成对校验且不泄漏值,刷新小时限制为 UTC 0~23', () => {
|
|
assert.equal(getServerConfig(env).qiniuAk, '');
|
|
for (const fields of [
|
|
{ QINIU_AK: 'private-ak' },
|
|
{ QINIU_SK: 'private-sk' },
|
|
{ QINIU_AK: 'a b', QINIU_SK: 'private-sk' },
|
|
{ QINIU_REGION: '../escape' },
|
|
{ SITE_REFRESH_HOUR_UTC: '24' },
|
|
])
|
|
assert.throws(
|
|
() => getServerConfig({ ...env, ...fields }),
|
|
(error: unknown) =>
|
|
error instanceof Error && !error.message.includes('private-'),
|
|
);
|
|
assert.equal(
|
|
getServerConfig({ ...env, SITE_REFRESH_HOUR_UTC: '8' })
|
|
.siteRefreshHourUtc,
|
|
8,
|
|
);
|
|
});
|