Files
Termix/plugins/workspaces/tests/backend/routes.test.ts
T

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");
}
});
});