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