mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 22:25:24 +00:00
100 lines
2.9 KiB
TypeScript
100 lines
2.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
hasUnresolvedTokens,
|
|
redactSecrets,
|
|
renderRecord,
|
|
renderTemplate,
|
|
} from "../../src/backend/template.js";
|
|
|
|
const context = {
|
|
host: { id: 7, name: "Zeus", ip: "10.0.0.5", username: "root", port: 22 },
|
|
trigger: { value: 93.4, mount: "/data" },
|
|
steps: { check: { stdout: "ok\n", stderr: "", code: 0 } },
|
|
vars: { target: "/var/log" },
|
|
};
|
|
|
|
describe("renderTemplate", () => {
|
|
it("substitutes host, trigger, step and var tokens", () => {
|
|
expect(renderTemplate("{{host.name}} at {{trigger.value}}%", context)).toBe(
|
|
"Zeus at 93.4%",
|
|
);
|
|
expect(renderTemplate("{{steps.check.stdout}}", context)).toBe("ok\n");
|
|
expect(renderTemplate("{{vars.target}}", context)).toBe("/var/log");
|
|
});
|
|
|
|
it("tolerates whitespace inside the braces", () => {
|
|
expect(renderTemplate("{{ host.name }}", context)).toBe("Zeus");
|
|
});
|
|
|
|
it("leaves unknown tokens in place so typos are visible", () => {
|
|
expect(renderTemplate("{{host.nope}}", context)).toBe("{{host.nope}}");
|
|
expect(hasUnresolvedTokens(renderTemplate("{{host.nope}}", context))).toBe(
|
|
true,
|
|
);
|
|
});
|
|
|
|
it("returns the input untouched when there is nothing to render", () => {
|
|
expect(renderTemplate("plain text", context)).toBe("plain text");
|
|
expect(renderTemplate("", context)).toBe("");
|
|
});
|
|
|
|
it("does not escape or alter shell metacharacters", () => {
|
|
// Quoting is the caller's job; this keeps that boundary explicit.
|
|
const rendered = renderTemplate("{{vars.payload}}", {
|
|
vars: { payload: "; rm -rf /" },
|
|
});
|
|
expect(rendered).toBe("; rm -rf /");
|
|
});
|
|
|
|
it("does not recursively expand a value that looks like a token", () => {
|
|
const rendered = renderTemplate("{{vars.a}}", {
|
|
vars: { a: "{{vars.b}}", b: "gotcha" },
|
|
});
|
|
expect(rendered).toBe("{{vars.b}}");
|
|
});
|
|
|
|
it("serializes objects rather than printing [object Object]", () => {
|
|
expect(renderTemplate("{{trigger}}", { trigger: { a: 1 } })).toBe(
|
|
'{"a":1}',
|
|
);
|
|
});
|
|
|
|
it("renders missing branches as the literal token", () => {
|
|
expect(renderTemplate("{{steps.other.stdout}}", context)).toBe(
|
|
"{{steps.other.stdout}}",
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("renderRecord", () => {
|
|
it("renders values and leaves keys alone", () => {
|
|
expect(renderRecord({ "X-Host": "{{host.name}}" }, context)).toEqual({
|
|
"X-Host": "Zeus",
|
|
});
|
|
});
|
|
|
|
it("passes undefined through", () => {
|
|
expect(renderRecord(undefined, context)).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("redactSecrets", () => {
|
|
it("masks credential-shaped keys", () => {
|
|
expect(
|
|
redactSecrets({
|
|
Authorization: "Bearer abc",
|
|
"X-Api-Key": "k",
|
|
token: "t",
|
|
password: "p",
|
|
Accept: "application/json",
|
|
}),
|
|
).toEqual({
|
|
Authorization: "***",
|
|
"X-Api-Key": "***",
|
|
token: "***",
|
|
password: "***",
|
|
Accept: "application/json",
|
|
});
|
|
});
|
|
});
|