metis-desk/apps/server/src/support.integration.ts

164 lines
5.2 KiB
TypeScript
Raw Normal View History

2026-10-07 08:16:39 +00:00
import assert from "node:assert/strict";
import { randomUUID } from "node:crypto";
import {
InventoryResponseSchema,
SupportListSchema,
SupportResponseSchema,
SupportToolResponseSchema,
SupportToolsSchema,
} from "@metis/assets/common";
import { z } from "zod/v4";
type Request = (path: string, body?: unknown, origin?: string, token?: string) => Promise<Response>;
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
export async function verifySupport(request: Request, companyId: string, otherId: string) {
const host = InventoryResponseSchema.parse(
await (await request(`/assets/${companyId}`)).json()
).assets.find((item) => item.values.status !== "retired");
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.ok(host);
const tools = SupportToolsSchema.parse(
await (await request("/asset-support/tools")).json()
).tools;
const tool = tools.find((item) => item.name === "AnyDesk");
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.ok(tool);
for (const name of ["AnyDesk", "Supremo Control", "Escritorio remoto", "TeamViewer"]) {
assert.ok(tools.some((item) => item.name === name));
}
const path = `/asset-support/${companyId}/${host.id}`;
const values = {
toolId: tool.id,
destination: "soporte-test",
requiresVpn: false,
preferred: true,
state: "available" as const,
notes: "Test",
};
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
async function error(url: string, body: unknown, status: number, code: string) {
const response = await request(url, body);
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.equal(response.status, status);
assert.equal(z.object({ code: z.string() }).parse(await response.json()).code, code);
}
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
const saved = await request(path, { values });
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.equal(saved.status, 201, await saved.clone().text());
const first = SupportResponseSchema.parse(await saved.json()).connection;
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.equal(first.id[14], "7");
assert.equal(first.history.length, 1);
await error(`/asset-support/${otherId}/${host.id}`, { values }, 404, "ASSET_NOT_FOUND");
await error(
path,
{ values: { ...values, preferred: true, state: "pending" } },
400,
"INVALID_REQUEST"
);
await error(path, { values }, 409, "PREFERRED_EXISTS");
const secondValues = {
...values,
toolId: tools.find((item) => item.name === "Supremo Control")?.id,
preferred: false,
};
const second = await request(path, { values: secondValues });
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.equal(second.status, 201);
const secondItem = SupportResponseSchema.parse(await second.json()).connection;
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
await error(path, { values: secondValues }, 409, "CONNECTION_EXISTS");
await error(`${path}/${randomUUID()}/update`, { values }, 404, "CONNECTION_NOT_FOUND");
const same = await request(`${path}/${first.id}/update`, { values });
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.equal(same.status, 200);
assert.equal(SupportResponseSchema.parse(await same.json()).connection.history.length, 1);
const update = await request(`${path}/${first.id}/update`, {
values: { ...values, requiresVpn: true },
});
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.equal(update.status, 200);
const changed = SupportResponseSchema.parse(await update.json()).connection;
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.equal(changed.history.length, 2);
assert.equal(changed.history[0]?.values.requiresVpn, false);
const switched = await request(`${path}/${first.id}/update`, {
values: { ...changed.values, preferred: false },
});
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.equal(switched.status, 200);
assert.equal(
(
await request(`${path}/${secondItem.id}/update`, {
values: { ...secondItem.values, preferred: true },
})
).status,
200
);
assert.equal((await request(`${path}?unknown=1`)).status, 400);
assert.equal((await request(path, { values }, "http://evil.example")).status, 403);
assert.equal((await request(path, { values }, undefined, "invalid-token")).status, 403);
const custom = await request("/asset-support/tools", {
values: { name: "RustDesk test", active: true },
});
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.equal(custom.status, 201);
const customTool = SupportToolResponseSchema.parse(await custom.json()).tool;
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
await error(
"/asset-support/tools",
{ values: { name: "rustdesk TEST", active: true } },
409,
"TOOL_EXISTS"
);
await error(
`/asset-support/tools/${tool.id}/update`,
{ values: { name: "Renamed", active: true } },
409,
"BUILTIN_PROTECTED"
);
assert.equal(
(
await request(`/asset-support/tools/${customTool.id}/update`, {
values: { name: customTool.name, active: false },
})
).status,
200
);
await error(
path,
{ values: { ...values, toolId: customTool.id, preferred: false } },
409,
"TOOL_UNAVAILABLE"
);
const listed = SupportListSchema.parse(await (await request(path)).json());
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.equal(listed.connections.length, 2);
assert.equal(listed.connections.filter((item) => item.values.preferred).length, 1);
const retired = await request(`/assets/${companyId}/${host.id}/update`, {
values: { ...host.values, status: "retired" },
});
2026-10-10 10:01:58 +00:00
2026-10-07 08:16:39 +00:00
assert.equal(retired.status, 200);
await error(
`${path}/${first.id}/update`,
{ values: { ...values, preferred: false } },
409,
"ASSET_RETIRED"
);
assert.equal(
(
await request(`${path}/${first.id}/update`, {
values: { ...values, preferred: false, state: "retired" },
})
).status,
200
);
assert.equal(
(await request(`/assets/${companyId}/${host.id}/update`, { values: host.values })).status,
200
);
}