mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 22:25:24 +00:00
114 lines
3.5 KiB
TypeScript
114 lines
3.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { PluginCapabilityError } from "@termix/plugin-sdk/backend";
|
|
import { createMockCtx } from "@termix/plugin-sdk/testing";
|
|
import type { PluginManifest } from "@termix/plugin-sdk/manifest";
|
|
import manifestJson from "../../manifest.json";
|
|
import { activate } from "../../src/backend/index.js";
|
|
import { detectWarpgateRound } from "../../src/backend/detect.js";
|
|
import { hostImportNormalizer } from "../../src/backend/host-import.js";
|
|
|
|
const manifest = manifestJson as unknown as PluginManifest;
|
|
const HOST = { id: 1, ip: "10.0.0.1", port: 22, username: "root" };
|
|
|
|
async function register() {
|
|
const mock = createMockCtx({
|
|
pluginId: manifest.id,
|
|
manifest,
|
|
capabilities: manifest.capabilities,
|
|
});
|
|
await activate(mock.ctx);
|
|
return mock.auth.keyboardInteractiveHandlers[0];
|
|
}
|
|
|
|
describe("detectWarpgateRound", () => {
|
|
it("claims a Warpgate round with its URL and security key", () => {
|
|
expect(
|
|
detectWarpgateRound({
|
|
name: "Warpgate authentication",
|
|
instructions:
|
|
"Open https://gate.example/@warpgate#/login/abc to continue. Security key: a b c d",
|
|
prompts: [{ prompt: "Press Enter when done: " }],
|
|
}),
|
|
).toEqual({
|
|
kind: "browser",
|
|
url: "https://gate.example/@warpgate#/login/abc",
|
|
code: "a b c d",
|
|
instructions:
|
|
"Open https://gate.example/@warpgate#/login/abc to continue. Security key: a b c d",
|
|
});
|
|
});
|
|
|
|
it("finds the marker in a prompt too, and says N/A without a key", () => {
|
|
expect(
|
|
detectWarpgateRound({
|
|
name: "",
|
|
instructions: "",
|
|
prompts: [
|
|
{ prompt: "Warpgate authentication: visit https://gate.example/x" },
|
|
],
|
|
}),
|
|
).toMatchObject({ url: "https://gate.example/x", code: "N/A" });
|
|
});
|
|
|
|
it("leaves other rounds, and a Warpgate round without a URL, alone", () => {
|
|
expect(
|
|
detectWarpgateRound({
|
|
name: "",
|
|
instructions: "",
|
|
prompts: [{ prompt: "Verification code:" }],
|
|
}),
|
|
).toBeNull();
|
|
expect(
|
|
detectWarpgateRound({
|
|
name: "Warpgate authentication",
|
|
instructions: "",
|
|
prompts: [],
|
|
}),
|
|
).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("the warpgate handler", () => {
|
|
it("registers under its id and label", async () => {
|
|
const handler = await register();
|
|
expect(handler).toMatchObject({ id: "warpgate", label: "Warpgate" });
|
|
});
|
|
|
|
it("answers password prompts itself only on hosts with the setting on", async () => {
|
|
const handler = await register();
|
|
expect(handler.autoAnswerPasswords!(HOST, { useWarpgate: true })).toBe(
|
|
true,
|
|
);
|
|
expect(handler.autoAnswerPasswords!(HOST, { useWarpgate: false })).toBe(
|
|
false,
|
|
);
|
|
expect(handler.autoAnswerPasswords!(HOST, {})).toBe(false);
|
|
});
|
|
|
|
it("fails closed without auth:provide", async () => {
|
|
const mock = createMockCtx({
|
|
pluginId: manifest.id,
|
|
manifest,
|
|
capabilities: [],
|
|
});
|
|
await expect(activate(mock.ctx)).rejects.toThrow(PluginCapabilityError);
|
|
});
|
|
});
|
|
|
|
describe("hostImportNormalizer", () => {
|
|
it("reads the plugin's own settings first, then the 2.8 host field", () => {
|
|
expect(
|
|
hostImportNormalizer({
|
|
pluginSettings: { warpgate: { useWarpgate: true } },
|
|
}),
|
|
).toEqual({ useWarpgate: true });
|
|
expect(hostImportNormalizer({ useWarpgate: 1 })).toEqual({
|
|
useWarpgate: true,
|
|
});
|
|
expect(hostImportNormalizer({ useWarpgate: false })).toEqual({
|
|
useWarpgate: false,
|
|
});
|
|
expect(hostImportNormalizer({ name: "x" })).toBeNull();
|
|
});
|
|
});
|