Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
255 lines
8.9 KiB
TypeScript
255 lines
8.9 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import {
|
|
applySnippetVariables,
|
|
parseSnippetVariables,
|
|
previewSnippetCommand,
|
|
snippetHasVariables,
|
|
} from "./snippetVariables.ts";
|
|
|
|
test("parseSnippetVariables finds all vars after snippetHasVariables (shared-regex lastIndex)", () => {
|
|
const command = "echo '{{test}}'\necho '{{test2}}'";
|
|
assert.equal(snippetHasVariables(command), true);
|
|
assert.deepEqual(parseSnippetVariables(command).map((v) => v.name), ["test", "test2"]);
|
|
});
|
|
|
|
test("parseSnippetVariables returns empty for plain command", () => {
|
|
assert.deepEqual(parseSnippetVariables("ls -la"), []);
|
|
assert.equal(snippetHasVariables("ls -la"), false);
|
|
});
|
|
|
|
test("parseSnippetVariables dedupes by first occurrence order", () => {
|
|
assert.deepEqual(parseSnippetVariables("echo {{a}} and {{b}} and {{a}}"), [
|
|
{ name: "a" },
|
|
{ name: "b" },
|
|
]);
|
|
});
|
|
|
|
test("parseSnippetVariables reads default after colon", () => {
|
|
assert.deepEqual(parseSnippetVariables("fallocate -l {{内存大小:4}}G"), [
|
|
{ name: "内存大小", defaultValue: "4" },
|
|
]);
|
|
});
|
|
|
|
test("parseSnippetVariables ignores Docker Go template field literals", () => {
|
|
const command = "ls -lh $(docker inspect --format='{{.LogPath}}' moviepilot)";
|
|
|
|
assert.equal(snippetHasVariables(command), false);
|
|
assert.deepEqual(parseSnippetVariables(command), []);
|
|
});
|
|
|
|
test("parseSnippetVariables ignores Docker Go template field literals with trim markers", () => {
|
|
const command = "ls -lh $(docker inspect --format='{{- .LogPath -}}' moviepilot)";
|
|
|
|
assert.equal(snippetHasVariables(command), false);
|
|
assert.deepEqual(parseSnippetVariables(command), []);
|
|
});
|
|
|
|
test("applySnippetVariables preserves Docker Go template field literals", () => {
|
|
const command = "ls -lh $(docker inspect --format='{{.LogPath}}' moviepilot)";
|
|
const result = applySnippetVariables(command, {});
|
|
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) assert.equal(result.command, command);
|
|
});
|
|
|
|
test("applySnippetVariables preserves Docker Go template field literals with trim markers", () => {
|
|
const command = "ls -lh $(docker inspect --format='{{- .LogPath -}}' moviepilot)";
|
|
const result = applySnippetVariables(command, {});
|
|
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) assert.equal(result.command, command);
|
|
});
|
|
|
|
test("applySnippetVariables preserves Docker Go template action blocks", () => {
|
|
const command = "docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' moviepilot";
|
|
const result = applySnippetVariables(command, {});
|
|
|
|
assert.equal(snippetHasVariables(command), false);
|
|
assert.deepEqual(parseSnippetVariables(command), []);
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) assert.equal(result.command, command);
|
|
});
|
|
|
|
test("applySnippetVariables preserves Docker Go template assignment action blocks", () => {
|
|
const command = "docker inspect --format='{{range $k, $v := .NetworkSettings.Networks}}{{$v.IPAddress}}{{end}}' moviepilot";
|
|
const result = applySnippetVariables(command, {});
|
|
|
|
assert.equal(snippetHasVariables(command), false);
|
|
assert.deepEqual(parseSnippetVariables(command), []);
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) assert.equal(result.command, command);
|
|
});
|
|
|
|
test("applySnippetVariables preserves Docker Go template assigned variables", () => {
|
|
const cases = [
|
|
"docker inspect --format='{{$p := .LogPath}}{{$p}}' moviepilot",
|
|
"docker inspect --format='{{$p := .LogPath}}{{printf \"%s\" $p}}' moviepilot",
|
|
"docker inspect --format='{{$p := .LogPath}}{{if $p}}{{$p}}{{end}}' moviepilot",
|
|
"docker inspect --format='{{range $k, $v := .NetworkSettings.Networks}}{{$k}}={{$v.IPAddress}}{{end}}' moviepilot",
|
|
];
|
|
|
|
for (const command of cases) {
|
|
const result = applySnippetVariables(command, {});
|
|
|
|
assert.equal(snippetHasVariables(command), false);
|
|
assert.deepEqual(parseSnippetVariables(command), []);
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) assert.equal(result.command, command);
|
|
}
|
|
});
|
|
|
|
test("applySnippetVariables preserves Docker Go template helper actions", () => {
|
|
const cases = [
|
|
"docker inspect --format='{{json .}}' moviepilot",
|
|
"docker inspect --format='{{printf \"%s\" .}}' moviepilot",
|
|
"docker inspect --format='{{println .}}' moviepilot",
|
|
];
|
|
|
|
for (const command of cases) {
|
|
const result = applySnippetVariables(command, {});
|
|
|
|
assert.equal(snippetHasVariables(command), false);
|
|
assert.deepEqual(parseSnippetVariables(command), []);
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) assert.equal(result.command, command);
|
|
}
|
|
});
|
|
|
|
test("applySnippetVariables can mix script variables with Docker Go template field literals", () => {
|
|
const result = applySnippetVariables(
|
|
"ls -lh $(docker inspect --format='{{.LogPath}}' {{container:moviepilot}})",
|
|
{},
|
|
);
|
|
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) {
|
|
assert.equal(
|
|
result.command,
|
|
"ls -lh $(docker inspect --format='{{.LogPath}}' moviepilot)",
|
|
);
|
|
}
|
|
});
|
|
|
|
test("applySnippetVariables can mix script variables with Docker Go template action blocks", () => {
|
|
const result = applySnippetVariables(
|
|
"docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' {{container:moviepilot}}",
|
|
{},
|
|
);
|
|
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) {
|
|
assert.equal(
|
|
result.command,
|
|
"docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' moviepilot",
|
|
);
|
|
}
|
|
});
|
|
|
|
test("snippetHasVariables detects script variables after Docker Go template field literals", () => {
|
|
assert.equal(
|
|
snippetHasVariables("docker inspect --format='{{.LogPath}}' {{container}}"),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("parseSnippetVariables keeps regular variables named like Go template controls outside Go template context", () => {
|
|
const result = applySnippetVariables("echo {{end}}", { end: "done" });
|
|
|
|
assert.equal(snippetHasVariables("echo {{end}}"), true);
|
|
assert.deepEqual(parseSnippetVariables("echo {{end}}"), [{ name: "end" }]);
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) assert.equal(result.command, "echo done");
|
|
});
|
|
|
|
test("applySnippetVariables keeps regular variables named like Go template controls near Go template context", () => {
|
|
const result = applySnippetVariables(
|
|
"docker inspect --format='{{.LogPath}}' {{end:done}}",
|
|
{},
|
|
);
|
|
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) {
|
|
assert.equal(result.command, "docker inspect --format='{{.LogPath}}' done");
|
|
}
|
|
});
|
|
|
|
test("applySnippetVariables keeps required variables named like Go template controls near Go template context", () => {
|
|
const result = applySnippetVariables(
|
|
"docker inspect --format='{{.LogPath}}' {{end}}",
|
|
{ end: "done" },
|
|
);
|
|
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) {
|
|
assert.equal(result.command, "docker inspect --format='{{.LogPath}}' done");
|
|
}
|
|
});
|
|
|
|
test("applySnippetVariables does not replace Go template actions when a script variable has the same name", () => {
|
|
const command = "docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' {{end:done}}";
|
|
const result = applySnippetVariables(command, {});
|
|
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) {
|
|
assert.equal(
|
|
result.command,
|
|
"docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' done",
|
|
);
|
|
}
|
|
});
|
|
|
|
test("previewSnippetCommand does not replace Go template actions when a script variable has the same name", () => {
|
|
assert.equal(
|
|
previewSnippetCommand(
|
|
"docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' {{end:done}}",
|
|
{},
|
|
),
|
|
"docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' done",
|
|
);
|
|
});
|
|
|
|
test("applySnippetVariables replaces all occurrences", () => {
|
|
const result = applySnippetVariables(
|
|
"fallocate -l {{内存大小:4}}G\nswapon {{内存大小:4}}",
|
|
{ 内存大小: "8" },
|
|
);
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) {
|
|
assert.equal(result.command, "fallocate -l 8G\nswapon 8");
|
|
}
|
|
});
|
|
|
|
test("applySnippetVariables uses default when value empty", () => {
|
|
const result = applySnippetVariables("size {{n:2}}", { n: "" });
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) assert.equal(result.command, "size 2");
|
|
});
|
|
|
|
test("applySnippetVariables reports missing required vars", () => {
|
|
const result = applySnippetVariables("echo {{name}}", {});
|
|
assert.equal(result.ok, false);
|
|
if (!result.ok) assert.deepEqual(result.missing, ["name"]);
|
|
});
|
|
|
|
test("applySnippetVariables passes through command without variables", () => {
|
|
const result = applySnippetVariables("uptime", { x: "1" });
|
|
assert.equal(result.ok, true);
|
|
if (result.ok) assert.equal(result.command, "uptime");
|
|
});
|
|
|
|
test("previewSnippetCommand keeps placeholder for unfilled required", () => {
|
|
assert.equal(
|
|
previewSnippetCommand("echo {{a}}", {}),
|
|
"echo {{a}}",
|
|
);
|
|
});
|
|
|
|
test("previewSnippetCommand shows resolved values", () => {
|
|
assert.equal(
|
|
previewSnippetCommand("echo {{a:hi}}", {}),
|
|
"echo hi",
|
|
);
|
|
});
|