mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 14:15:24 +00:00
204 lines
6.2 KiB
TypeScript
204 lines
6.2 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
import { layout, startServer, type TestServer } from "./helpers";
|
|
|
|
let server: TestServer;
|
|
|
|
beforeEach(async () => {
|
|
server = await startServer();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await server.close();
|
|
});
|
|
|
|
async function create(name = "Prod", user = "user-1") {
|
|
const res = await server.request("POST", "/", {
|
|
user,
|
|
body: { name, color: "#ef4444", payload: layout() },
|
|
});
|
|
expect(res.status).toBe(200);
|
|
return res.body;
|
|
}
|
|
|
|
describe("workspace routes", () => {
|
|
it("creates and lists workspaces with a parsed payload and tab count", async () => {
|
|
const created = await create();
|
|
expect(created).toMatchObject({
|
|
name: "Prod",
|
|
color: "#ef4444",
|
|
kind: "manual",
|
|
isDefault: false,
|
|
tabCount: 1,
|
|
});
|
|
expect(created.payload.tabs).toHaveLength(1);
|
|
expect(created.syncId).toEqual(expect.any(String));
|
|
|
|
const list = await server.request("GET", "/");
|
|
expect(list.body).toHaveLength(1);
|
|
expect(list.body[0].id).toBe(created.id);
|
|
});
|
|
|
|
it("rejects a missing name or payload", async () => {
|
|
expect(
|
|
(await server.request("POST", "/", { body: { payload: layout() } }))
|
|
.status,
|
|
).toBe(400);
|
|
expect(
|
|
(await server.request("POST", "/", { body: { name: "x", payload: {} } }))
|
|
.status,
|
|
).toBe(400);
|
|
});
|
|
|
|
it("keeps one last session row per user and never lists it as editable", async () => {
|
|
expect((await server.request("GET", "/last-session")).body).toBeNull();
|
|
|
|
await server.request("PUT", "/last-session", {
|
|
body: { payload: layout() },
|
|
});
|
|
const second = await server.request("PUT", "/last-session", {
|
|
body: { payload: layout([]) },
|
|
});
|
|
expect(second.body.tabCount).toBe(0);
|
|
|
|
const list = (await server.request("GET", "/")).body;
|
|
expect(
|
|
list.filter(
|
|
(w: { kind: string; isDefault: boolean; id: number }) =>
|
|
w.kind === "last_session",
|
|
),
|
|
).toHaveLength(1);
|
|
|
|
const id = second.body.id;
|
|
expect(
|
|
(await server.request("PATCH", `/${id}`, { body: { name: "x" } })).status,
|
|
).toBe(404);
|
|
expect((await server.request("DELETE", `/${id}`)).status).toBe(404);
|
|
});
|
|
|
|
it("renames, replaces content and duplicates", async () => {
|
|
const created = await create();
|
|
|
|
const renamed = await server.request("PATCH", `/${created.id}`, {
|
|
body: { name: " Staging ", color: "#22c55e" },
|
|
});
|
|
expect(renamed.body).toMatchObject({ name: "Staging", color: "#22c55e" });
|
|
|
|
const replaced = await server.request("PUT", `/${created.id}/content`, {
|
|
body: { payload: layout([]) },
|
|
});
|
|
expect(replaced.body.tabCount).toBe(0);
|
|
|
|
const copy = await server.request("POST", `/${created.id}/duplicate`, {
|
|
body: { name: "Staging (copy)" },
|
|
});
|
|
expect(copy.body).toMatchObject({
|
|
name: "Staging (copy)",
|
|
color: "#22c55e",
|
|
tabCount: 0,
|
|
});
|
|
expect(copy.body.id).not.toBe(created.id);
|
|
});
|
|
|
|
it("keeps a single default", async () => {
|
|
const a = await create("A");
|
|
const b = await create("B");
|
|
|
|
await server.request("POST", `/${a.id}/set-default`);
|
|
await server.request("POST", `/${b.id}/set-default`);
|
|
|
|
const list = (await server.request("GET", "/")).body;
|
|
const defaults = list.filter(
|
|
(w: { kind: string; isDefault: boolean; id: number }) => w.isDefault,
|
|
);
|
|
expect(
|
|
defaults.map(
|
|
(w: { kind: string; isDefault: boolean; id: number }) => w.id,
|
|
),
|
|
).toEqual([b.id]);
|
|
|
|
const unset = await server.request("POST", `/${b.id}/unset-default`);
|
|
expect(unset.body.isDefault).toBe(false);
|
|
});
|
|
|
|
it("marks a workspace as used when it is applied", async () => {
|
|
const created = await create();
|
|
expect(created.lastUsedAt).toBeNull();
|
|
|
|
const applied = await server.request("POST", `/${created.id}/apply`);
|
|
expect(applied.status).toBe(200);
|
|
|
|
const list = (await server.request("GET", "/")).body;
|
|
expect(list[0].lastUsedAt).toEqual(expect.any(String));
|
|
});
|
|
|
|
it("deletes a workspace", async () => {
|
|
const created = await create();
|
|
expect((await server.request("DELETE", `/${created.id}`)).body).toEqual({
|
|
success: true,
|
|
});
|
|
expect((await server.request("GET", "/")).body).toEqual([]);
|
|
expect((await server.request("DELETE", `/${created.id}`)).status).toBe(404);
|
|
});
|
|
|
|
it("rejects ids that are not numbers", async () => {
|
|
expect((await server.request("DELETE", "/abc")).status).toBe(400);
|
|
expect((await server.request("POST", "/abc/apply")).status).toBe(400);
|
|
});
|
|
|
|
it("never shows or changes another user's workspaces", async () => {
|
|
const mine = await create("Mine", "user-1");
|
|
|
|
expect((await server.request("GET", "/", { user: "user-2" })).body).toEqual(
|
|
[],
|
|
);
|
|
for (const [method, path] of [
|
|
["PATCH", `/${mine.id}`],
|
|
["DELETE", `/${mine.id}`],
|
|
["POST", `/${mine.id}/apply`],
|
|
["POST", `/${mine.id}/set-default`],
|
|
["POST", `/${mine.id}/duplicate`],
|
|
] as const) {
|
|
const res = await server.request(method, path, {
|
|
user: "user-2",
|
|
body: { name: "stolen" },
|
|
});
|
|
expect(res.status).toBe(404);
|
|
}
|
|
});
|
|
|
|
it("flushes the database after every write", async () => {
|
|
const before = server.db.persisted;
|
|
const created = await create();
|
|
await server.request("POST", `/${created.id}/set-default`);
|
|
await server.request("DELETE", `/${created.id}`);
|
|
expect(server.db.persisted).toBeGreaterThanOrEqual(before + 3);
|
|
});
|
|
});
|
|
|
|
describe("workspaces.use", () => {
|
|
it("refuses every route without it", async () => {
|
|
await server.close();
|
|
server = await startServer({ permissions: [] });
|
|
|
|
for (const [method, path] of [
|
|
["GET", "/"],
|
|
["POST", "/"],
|
|
["GET", "/last-session"],
|
|
["PUT", "/last-session"],
|
|
["PATCH", "/1"],
|
|
["PUT", "/1/content"],
|
|
["POST", "/1/duplicate"],
|
|
["POST", "/1/set-default"],
|
|
["POST", "/1/unset-default"],
|
|
["POST", "/1/apply"],
|
|
["DELETE", "/1"],
|
|
] as const) {
|
|
const res = await server.request(method, path, {
|
|
body: { name: "x", payload: layout() },
|
|
});
|
|
expect(res.status, `${method} ${path}`).toBe(403);
|
|
expect(res.body.required).toBe("workspaces.use");
|
|
}
|
|
});
|
|
});
|