mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
107 lines
4.5 KiB
TypeScript
107 lines
4.5 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
/**
|
|
* This codebase enumerates host columns by hand in a lot of places, and
|
|
* "added a column, missed one update path" is its most repeated bug -- during
|
|
* the first attempt at this feature a field was missed at one of these points
|
|
* six separate times, each surfacing as a different mystery (endpoints that
|
|
* saved but never appeared, config that vanished on reload).
|
|
*
|
|
* Every one of these files already enumerates the Docker pair, which is the
|
|
* exact shape the web endpoint pair copies. So "mentions enableDocker but not
|
|
* enableWebUi" is a reliable proxy for "was not updated", and it fails loudly
|
|
* the moment someone adds the tenth enumeration point without this one.
|
|
*
|
|
* This is a coarse guard on purpose: it cannot tell whether the field is used
|
|
* CORRECTLY, only that the file knows it exists. Behavioural coverage lives in
|
|
* the route tests.
|
|
*/
|
|
// Repo-root relative, resolved from this file rather than cwd: the plugin
|
|
// suite runs with the plugin directory as cwd.
|
|
const REPO_ROOT = path.resolve(
|
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
"../../../..",
|
|
);
|
|
|
|
function source(relative: string): string {
|
|
return readFileSync(path.resolve(REPO_ROOT, relative), "utf8");
|
|
}
|
|
|
|
const ENUMERATION_POINTS = [
|
|
"src/backend/database/database.ts",
|
|
"src/backend/database/db/index.ts",
|
|
"src/backend/database/routes/host-normalizers.ts",
|
|
"src/backend/database/routes/host.ts",
|
|
"src/backend/database/routes/host-bulk-routes.ts",
|
|
"src/ui/sidebar/host-export-payload.ts",
|
|
"src/ui/sidebar/HostManagerData.ts",
|
|
"src/ui/sidebar/HostEditorData.ts",
|
|
"src/ui/lib/host-to-ssh-host.ts",
|
|
];
|
|
|
|
describe("web endpoint column enumeration", () => {
|
|
it.each(ENUMERATION_POINTS)(
|
|
"%s enumerates the enable flag alongside enableDocker",
|
|
(file) => {
|
|
const text = source(file);
|
|
// Guards the guard: if the Docker anchor ever disappears from a file,
|
|
// this test would otherwise pass vacuously forever.
|
|
expect(text).toMatch(/enableDocker|enable_docker/);
|
|
expect(text).toMatch(/enableWebUi|enable_web_ui/);
|
|
},
|
|
);
|
|
|
|
it.each(ENUMERATION_POINTS)(
|
|
"%s enumerates the config column alongside dockerConfig",
|
|
(file) => {
|
|
const text = source(file);
|
|
expect(text).toMatch(/dockerConfig|docker_config/);
|
|
expect(text).toMatch(/webUiConfig|web_ui_config/);
|
|
},
|
|
);
|
|
|
|
it("parses webUiConfig through the guarded parser, not a bare JSON.parse", () => {
|
|
// dockerConfig uses a bare JSON.parse, so one malformed value takes out
|
|
// the whole host listing. Do not copy that.
|
|
const text = source("src/backend/database/routes/host-normalizers.ts");
|
|
expect(text).toContain("parseWebUiConfig");
|
|
expect(text).not.toMatch(/JSON\.parse\(\s*host\.webUiConfig/);
|
|
});
|
|
|
|
it("clears webUiConfig on disable in both host.ts write paths", () => {
|
|
// Three write paths disagreed on this during the first attempt. The export
|
|
// payload ships webUiConfig unconditionally, so a host disabled without
|
|
// clearing still exports its endpoint list -- internal hostnames, ports and
|
|
// paths -- while the UI reads as off everywhere, and re-enabling silently
|
|
// resurrects stale endpoints.
|
|
const text = source("src/backend/database/routes/host.ts");
|
|
// Create and update. Anchored on the exact conditional, not a loose
|
|
// pattern: a looser regex here passed even with the guard deleted.
|
|
const occurrences = text.split("webUiConfig: enableWebUi").length - 1;
|
|
expect(occurrences).toBe(2);
|
|
});
|
|
|
|
it("clears webUiConfig on a bulk disable", () => {
|
|
const text = source("src/backend/database/routes/host-bulk-routes.ts");
|
|
expect(text).toContain(
|
|
"if (!updates.enableWebUi) simpleUpdates.webUiConfig = null;",
|
|
);
|
|
});
|
|
|
|
it("shares webUiConfig with connect-level recipients", () => {
|
|
// A connect-level recipient is ALREADY authorized to open these tunnels
|
|
// (the open route gates on canAccessHost(..., "connect")), so withholding
|
|
// the config only breaks discovery while the flag advertises the feature.
|
|
// The dockerConfig precedent does not transfer: Docker's tab works without
|
|
// its config, whereas a web endpoint IS its config.
|
|
const text = source("src/backend/database/routes/host-normalizers.ts");
|
|
const block = text.slice(text.indexOf("CONNECT_LEVEL_FIELDS"));
|
|
const list = block.slice(0, block.indexOf("]"));
|
|
expect(list).toContain("enableWebUi");
|
|
expect(list).toContain("webUiConfig");
|
|
});
|
|
});
|