1
0

feat: 扩展配置变量替换范围至 server/probes/targets,支持空默认值语法

This commit is contained in:
2026-05-21 18:42:42 +08:00
parent 79358ba50d
commit 6ca8b36542
6 changed files with 258 additions and 53 deletions

View File

@@ -150,36 +150,63 @@ describe("config variables", () => {
}
});
test("替换范围targets跳过 id 和 type 字段", () => {
test("替换范围覆盖 server、probes、targets跳过 variables 和 target 顶层 id/type", () => {
const result = resolveVariables({
defaults: { interval: "${interval}" },
server: { host: "${host}" },
probes: { execution: { maxConcurrentChecks: "${maxConcurrentChecks}" } },
server: { host: "${host}", logging: { level: "${logLevel}" }, storage: { retention: "${retention}" } },
targets: [
{
cmd: {
args: ["--host", "${host}"],
env: { TOKEN: "${token}" },
env: { "${TOKEN_KEY}": "${token}" },
exec: "echo",
},
expect: { rows: [{ id: { equals: "${expectedId}" }, type: { equals: "${expectedType}" } }] },
id: "${id}",
type: "${type}",
},
],
variables: { host: "localhost", id: "resolved", interval: "30s", token: "abc", type: "cmd" },
variables: {
expectedId: 1,
expectedType: "service",
host: "localhost",
id: "resolved",
interval: "30s",
logLevel: "debug",
maxConcurrentChecks: 20,
retention: "24h",
token: "abc",
TOKEN_KEY: "TOKEN",
type: "cmd",
},
});
expect(result.issues).toHaveLength(0);
const config = result.config as {
defaults: { interval: string };
server: { host: string };
targets: Array<{ cmd: { args: string[]; env: Record<string, string> }; id: string; type: string }>;
probes: { execution: { maxConcurrentChecks: number } };
server: { host: string; logging: { level: string }; storage: { retention: string } };
targets: Array<{
cmd: { args: string[]; env: Record<string, string> };
expect: { rows: Array<{ id: { equals: number }; type: { equals: string } }> };
id: string;
type: string;
}>;
variables: { host: string };
};
expect(config.server.host).toBe("${host}");
expect(config.server.host).toBe("localhost");
expect(config.server.logging.level).toBe("debug");
expect(config.server.storage.retention).toBe("24h");
expect(config.probes.execution.maxConcurrentChecks).toBe(20);
expect(config.variables.host).toBe("localhost");
expect(config.defaults.interval).toBe("${interval}");
expect(config.targets[0]!.id).toBe("${id}");
expect(config.targets[0]!.type).toBe("${type}");
expect(config.targets[0]!.cmd.args[1]).toBe("localhost");
expect(config.targets[0]!.cmd.env["TOKEN"]).toBe("abc");
expect(config.targets[0]!.cmd.env["${TOKEN_KEY}"]).toBe("abc");
expect(config.targets[0]!.expect.rows[0]!.id.equals).toBe(1);
expect(config.targets[0]!.expect.rows[0]!.type.equals).toBe("service");
});
test("默认值推断为 booleantrue/false", () => {
@@ -220,7 +247,39 @@ describe("config variables", () => {
expect(typeof http2["ignoreSSL"]).toBe("boolean");
});
test("server.storage 段不替换", () => {
test("空默认值和空环境变量保持字符串空值", () => {
const original = process.env["EMPTY_ENV_VALUE"];
process.env["EMPTY_ENV_VALUE"] = "";
try {
const result = resolveVariables({
server: { listen: { host: "${EMPTY_ENV_VALUE}" }, logging: { file: { path: "${OPTIONAL_LOG_PATH|}" } } },
targets: [
{
http: { body: "${OPTIONAL_BODY|}", url: "${HOST|localhost}" },
id: "empty-values",
type: "http",
},
],
});
expect(result.issues).toHaveLength(0);
const config = result.config as {
server: { listen: { host: unknown }; logging: { file: { path: unknown } } };
targets: Array<{ http: { body: unknown } }>;
};
expect(config.server.listen.host).toBe("");
expect(typeof config.server.listen.host).toBe("string");
expect(config.server.logging.file.path).toBe("");
expect(typeof config.server.logging.file.path).toBe("string");
expect(config.targets[0]!.http.body).toBe("");
expect(typeof config.targets[0]!.http.body).toBe("string");
} finally {
restoreEnv("EMPTY_ENV_VALUE", original);
}
});
test("server.storage 段支持替换", () => {
const result = resolveVariables({
server: { storage: { retention: "${retention}" } },
targets: [
@@ -235,10 +294,10 @@ describe("config variables", () => {
expect(result.issues).toHaveLength(0);
const config = result.config as { server: { storage: { retention: string } } };
expect(config.server.storage.retention).toBe("${retention}");
expect(config.server.storage.retention).toBe("24h");
});
test("probes 段替换", () => {
test("probes 段支持替换", () => {
const result = resolveVariables({
probes: { execution: { maxConcurrentChecks: "${maxConcurrentChecks}" } },
targets: [
@@ -248,15 +307,15 @@ describe("config variables", () => {
type: "http",
},
],
variables: { host: "https://example.com", maxConcurrentChecks: "20" },
variables: { host: "https://example.com", maxConcurrentChecks: 20 },
});
expect(result.issues).toHaveLength(0);
const config = result.config as { probes: { execution: { maxConcurrentChecks: string } } };
expect(config.probes.execution.maxConcurrentChecks).toBe("${maxConcurrentChecks}");
const config = result.config as { probes: { execution: { maxConcurrentChecks: number } } };
expect(config.probes.execution.maxConcurrentChecks).toBe(20);
});
test("server.logging 段替换", () => {
test("server.logging 段支持替换", () => {
const result = resolveVariables({
server: { logging: { level: "${logLevel}" } },
targets: [
@@ -271,7 +330,7 @@ describe("config variables", () => {
expect(result.issues).toHaveLength(0);
const config = result.config as { server: { logging: { level: string } } };
expect(config.server.logging.level).toBe("${logLevel}");
expect(config.server.logging.level).toBe("debug");
});
test("variables 段为非对象时报错", () => {