Files
Termix/plugins/automations/tests/backend/template.test.ts
T

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",
});
});
});