Files
Termix/plugins/ssh-terminal/tests/backend/routes.test.ts
T

204 lines
6.8 KiB
TypeScript

import { afterEach, describe, expect, it } from "vitest";
import { startServer, type TestServer } from "./helpers";
let server: TestServer | null = null;
afterEach(async () => {
await server?.close();
server = null;
});
const history = (s: TestServer) =>
s.db.sqlite
.prepare(
"SELECT user_id, host_id, command FROM p_ssh_terminal_command_history ORDER BY id",
)
.all();
describe("command history routes", () => {
it("saves, lists newest first without duplicates, deletes and clears", async () => {
server = await startServer();
for (const command of ["ls", "pwd", "ls"]) {
const saved = await server.request("POST", "/command-history", {
body: { hostId: 1, command },
});
expect(saved.status).toBe(201);
}
await server.request("POST", "/command-history", {
user: "user-2",
body: { hostId: 1, command: "whoami" },
});
const listed = await server.request("GET", "/command-history/1");
expect(listed.body).toEqual(["ls", "pwd"]);
await server.request("POST", "/command-history/delete", {
body: { hostId: 1, command: "pwd" },
});
expect((await server.request("GET", "/command-history/1")).body).toEqual([
"ls",
]);
await server.request("DELETE", "/command-history/1");
expect(history(server)).toEqual([
{ user_id: "user-2", host_id: 1, command: "whoami" },
]);
expect(server.db.persisted).toBeGreaterThan(0);
});
it("acknowledges but never stores a command that looks like a secret", async () => {
server = await startServer();
const saved = await server.request("POST", "/command-history", {
body: { hostId: 1, command: "export API_TOKEN=abc" },
});
expect(saved.status).toBe(201);
expect(history(server)).toEqual([]);
});
it("stores nothing while command history is off globally", async () => {
server = await startServer({ settings: { commandHistoryEnabled: false } });
await server.request("POST", "/command-history", {
body: { hostId: 1, command: "ls" },
});
expect(history(server)).toEqual([]);
});
it("stores nothing for a host that turned command history off", async () => {
server = await startServer();
await server.mock.ctx.settings.setHost(2, "enableCommandHistory", false);
await server.request("POST", "/command-history", {
body: { hostId: 2, command: "ls" },
});
await server.request("POST", "/command-history", {
body: { hostId: 1, command: "ls" },
});
expect(history(server)).toEqual([
{ user_id: "user-1", host_id: 1, command: "ls" },
]);
});
it("rejects a request missing its fields", async () => {
server = await startServer();
const saved = await server.request("POST", "/command-history", {
body: { hostId: 1 },
});
expect(saved.status).toBe(400);
});
it("needs hosts.view for the recent list", async () => {
server = await startServer({ permissions: [] });
const denied = await server.request("GET", "/command-history/1/recent");
expect(denied.status).toBe(403);
});
it("lists recent commands with duplicates for a hosts.view holder", async () => {
server = await startServer({ permissions: ["hosts.view"] });
for (const command of ["uptime", "uptime"]) {
await server.request("POST", "/command-history", {
body: { hostId: 1, command },
});
}
const allowed = await server.request("GET", "/command-history/1/recent");
expect(allowed.body).toEqual(["uptime", "uptime"]);
});
});
describe("client settings and image storage", () => {
it("serves the terminal's settings to any signed-in user", async () => {
server = await startServer({
settings: { sessionTimeoutMinutes: 45, sessionPersistence: false },
});
const { body } = await server.request("GET", "/client-settings");
expect(body.sessionTimeoutMinutes).toBe(45);
expect(body.sessionPersistence).toBe(false);
expect(body.commandHistoryEnabled).toBe(true);
expect(body.touchInput.enabled).toBe(true);
});
it("refuses the image storage test to a non-admin", async () => {
server = await startServer({ permissions: [] });
const denied = await server.request("POST", "/image-storage/test", {
body: { instanceId: "tab-1" },
});
expect(denied.status).toBe(403);
});
it("reports an unconnected session to an admin", async () => {
server = await startServer({ permissions: ["admin.plugins.manage"] });
const result = await server.request("POST", "/image-storage/test", {
body: { instanceId: "tab-1" },
});
expect(result.status).toBe(200);
expect(result.body.connected).toBe(false);
});
});
describe("terminal settings routes", () => {
const host = (id: number, userId = "user-1") => ({
id,
userId,
name: `h${id}`,
ip: "10.0.0.1",
port: 22,
username: "root",
tags: null,
folder: null,
authType: "password",
});
it("hands every user their own terminal settings", async () => {
server = await startServer();
await server.mock.ctx.settings.setUser(
"user-1",
"commandAutocomplete",
true,
);
const { body } = await server.request("GET", "/client-settings");
expect(body.user).toMatchObject({
commandAutocomplete: true,
localEcho: "auto",
linkClickBehavior: "confirm",
});
expect(body).not.toHaveProperty("newHostDefaults");
const other = await server.request("GET", "/client-settings", {
user: "user-2",
});
expect(other.body.user.commandAutocomplete).toBe(false);
});
it("saves the browser preferences a user moves over, for that user only", async () => {
server = await startServer();
const saved = await server.request("PUT", "/user-settings", {
body: { localEcho: "on", linkClickBehavior: "direct", theme: "x" },
});
expect(saved.status).toBe(200);
expect(await server.mock.ctx.settings.getUser("user-1", "localEcho")).toBe(
"on",
);
expect(
await server.mock.ctx.settings.getUser("user-2", "localEcho"),
).not.toBe("on");
});
it("turns auto tmux on for a host the user can edit", async () => {
server = await startServer({ hosts: [host(1)] });
const saved = await server.request("PUT", "/hosts/1/auto-tmux", {
body: { enabled: true },
});
expect(saved.status).toBe(200);
expect(await server.mock.ctx.settings.getHost(1, "autoTmux")).toBe(true);
});
it("refuses auto tmux on a host the user cannot reach, or a bad body", async () => {
server = await startServer({ hosts: [host(1)] });
const unknown = await server.request("PUT", "/hosts/2/auto-tmux", {
body: { enabled: true },
});
expect(unknown.status).toBe(403);
const bad = await server.request("PUT", "/hosts/1/auto-tmux", {
body: { enabled: "yes" },
});
expect(bad.status).toBe(400);
});
});