aps-agent/scripts/config.test.mjs

84 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** 配置加载(ADR-0011 / 决策 D15、D16)的用例。 */
import assert from 'node:assert/strict';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { test } from 'node:test';
import { configFilePath, isEngineConfigured, loadConfig, redactConfig } from '@aps/server/config';
async function withConfigFile(content, run) {
const dir = await mkdtemp(join(tmpdir(), 'aps-config-'));
const file = join(dir, 'config.json');
try {
if (content !== undefined) await writeFile(file, content, 'utf8');
return await run({ dir, file });
} finally {
await rm(dir, { recursive: true, force: true });
}
}
test('没有配置文件与环境变量时用默认值:DeepSeek,密钥为空,引擎不可用', async () => {
await withConfigFile(undefined, async ({ dir, file }) => {
const config = loadConfig({ env: { APS_CONFIG_FILE: file }, homeDir: dir });
assert.equal(config.llm.provider, 'deepseek');
assert.equal(config.llm.baseUrl, 'https://api.deepseek.com/v1');
assert.equal(config.llm.model, 'deepseek-chat');
assert.equal(config.llm.apiKey, '');
assert.equal(isEngineConfigured(config), false, '没有密钥时引擎不应被视为可用');
assert.equal(config.database.path, join(dir, '.aps', 'aps.sqlite'), '默认库在用户目录下');
assert.equal(config.server.port, 5199);
});
});
test('配置文件提供端点与密钥', async () => {
const content = JSON.stringify({
llm: { provider: 'compatible', baseUrl: 'http://gw.internal/v1', model: 'internal-chat', apiKey: 'sk-file-123' },
database: { path: 'D:/data/aps.sqlite' },
server: { port: 6000 },
});
await withConfigFile(content, async ({ dir, file }) => {
const config = loadConfig({ env: { APS_CONFIG_FILE: file }, homeDir: dir });
assert.equal(config.llm.provider, 'compatible');
assert.equal(config.llm.baseUrl, 'http://gw.internal/v1');
assert.equal(config.llm.model, 'internal-chat');
assert.equal(config.llm.apiKey, 'sk-file-123');
assert.equal(isEngineConfigured(config), true);
assert.equal(config.database.path, 'D:/data/aps.sqlite');
assert.equal(config.server.port, 6000);
});
});
test('环境变量优先于配置文件', async () => {
const content = JSON.stringify({ llm: { model: 'from-file', apiKey: 'sk-file' }, server: { port: 6000 } });
await withConfigFile(content, async ({ dir, file }) => {
const config = loadConfig({
env: { APS_CONFIG_FILE: file, APS_LLM_MODEL: 'from-env', APS_PORT: '7000' },
homeDir: dir,
});
assert.equal(config.llm.model, 'from-env');
assert.equal(config.llm.apiKey, 'sk-file', '未覆盖的字段仍取配置文件');
assert.equal(config.server.port, 7000);
});
});
test('脱敏视图不含密钥明文', async () => {
const content = JSON.stringify({ llm: { apiKey: 'sk-secret-abcdef' } });
await withConfigFile(content, async ({ dir, file }) => {
const config = loadConfig({ env: { APS_CONFIG_FILE: file }, homeDir: dir });
const redacted = JSON.stringify(redactConfig(config));
assert.ok(!redacted.includes('sk-secret-abcdef'), `脱敏后仍能看到密钥:${redacted}`);
assert.ok(redacted.includes('已配置'), redacted);
});
});
test('配置文件损坏时报错,不静默降级', async () => {
await withConfigFile('{ 这不是 JSON', async ({ dir, file }) => {
assert.throws(() => loadConfig({ env: { APS_CONFIG_FILE: file }, homeDir: dir }), /配置文件解析失败/);
});
});
test('配置文件路径:默认在用户目录,可用 APS_CONFIG_FILE 覆盖', () => {
assert.equal(configFilePath({ env: {}, homeDir: 'C:/Users/test' }).replace(/\\/g, '/'), 'C:/Users/test/.aps/config.json');
assert.equal(configFilePath({ env: { APS_CONFIG_FILE: 'D:/cfg/aps.json' }, homeDir: 'C:/Users/test' }), 'D:/cfg/aps.json');
});