import assert from "node:assert/strict"; import { randomUUID } from "node:crypto"; import { InventoryResponseSchema, SupportListSchema, SupportResponseSchema, } from "@metis/assets/common"; import { CatalogResponseSchema, ResourceResponseSchema, SaveDefinitionResponseSchema, } from "@metis/resources/common"; import type { Sequelize } from "sequelize"; import { z } from "zod/v4"; import { verifySupportResourceMigration } from "./support-resource-migration.integration"; type Request = (path: string, body?: unknown, origin?: string, token?: string) => Promise; export async function verifySupport( request: Request, companyId: string, otherId: string, database: Sequelize ) { const host = InventoryResponseSchema.parse( await (await request(`/assets/${companyId}`)).json() ).assets.find((item) => item.values.status !== "retired"); assert.ok(host); await verifySupportResourceMigration(database, companyId, host.id); const catalog = CatalogResponseSchema.parse( await (await request(`/resource-catalog/${companyId}`)).json() ); const template = catalog.items.find((item) => item.builtinKey === "template:support"); assert.ok(template); const tools = []; for (const name of ["AnyDesk", "Supremo Control", "Escritorio remoto", "TeamViewer"]) { const response = await request(`/resources/${companyId}`, { values: { name, templateId: template.id, active: true, notes: "", characteristics: {} }, }); assert.equal(response.status, 201, await response.clone().text()); const resource = ResourceResponseSchema.parse(await response.json()).resource; tools.push({ id: resource.id, name, active: true }); } const tool = tools[0]; assert.ok(tool); const path = `/asset-support/${companyId}/${host.id}`; const available = SupportListSchema.parse(await (await request(path)).json()); assert.ok(tools.every((tool) => available.tools.some((item) => item.id === tool.id))); const values = { resourceId: tool.id, destination: "soporte-test", requiresVpn: false, preferred: true, state: "available" as const, notes: "Test", }; async function error(url: string, body: unknown, status: number, code: string) { const response = await request(url, body); assert.equal(response.status, status); assert.equal(z.object({ code: z.string() }).parse(await response.json()).code, code); } const saved = await request(path, { values }); assert.equal(saved.status, 201, await saved.clone().text()); const first = SupportResponseSchema.parse(await saved.json()).connection; 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, resourceId: tools.find((item) => item.name === "Supremo Control")?.id, preferred: false, }; const second = await request(path, { values: secondValues }); assert.equal(second.status, 201); const secondItem = SupportResponseSchema.parse(await second.json()).connection; 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 }); 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 }, }); assert.equal(update.status, 200); const changed = SupportResponseSchema.parse(await update.json()).connection; 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 }, }); 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(`/resources/${companyId}`, { values: { name: "RustDesk test", templateId: template.id, active: true, notes: "", characteristics: {}, }, }); assert.equal(custom.status, 201); const customTool = ResourceResponseSchema.parse(await custom.json()).resource; const disabled = await request(`/resources/${companyId}/${customTool.id}/update`, { values: { ...customTool.values, active: false }, }); assert.equal(disabled.status, 200); await error( path, { values: { ...values, resourceId: customTool.id, preferred: false } }, 409, "TOOL_UNAVAILABLE" ); const foreign = ResourceResponseSchema.parse( await ( await request(`/resources/${otherId}`, { values: { name: "AnyDesk ajeno", templateId: template.id, active: true, notes: "", characteristics: {}, }, }) ).json() ).resource; await error( path, { values: { ...values, resourceId: foreign.id, preferred: false } }, 409, "TOOL_UNAVAILABLE" ); const generalTemplate = catalog.items.find((item) => item.builtinKey === "template:application"); assert.ok(generalTemplate); const application = ResourceResponseSchema.parse( await ( await request(`/resources/${companyId}`, { values: { name: "Aplicación sin asistencia", templateId: generalTemplate.id, active: true, notes: "", characteristics: {}, }, }) ).json() ).resource; await error( path, { values: { ...values, resourceId: application.id, preferred: false } }, 409, "TOOL_UNAVAILABLE" ); await assert.rejects(() => database.query("UPDATE asset_support SET resourceId=:resourceId WHERE id=:id", { replacements: { resourceId: foreign.id, id: first.id }, }) ); const provider = catalog.items.find((item) => item.builtinKey === "field:provider"); assert.ok(provider); const customTemplateResponse = await request(`/resource-catalog/${companyId}`, { values: { kind: "template", name: "Asistencia propia", description: "", usage: "support", fields: [{ fieldId: provider.id, required: false }], }, }); assert.equal(customTemplateResponse.status, 201); const customTemplate = SaveDefinitionResponseSchema.parse(await customTemplateResponse.json()); const customResourceResponse = await request(`/resources/${companyId}`, { values: { name: "RustDesk propio", templateId: customTemplate.savedId, active: true, notes: "", characteristics: {}, }, }); assert.equal(customResourceResponse.status, 201); const customResource = ResourceResponseSchema.parse(await customResourceResponse.json()).resource; const customList = SupportListSchema.parse(await (await request(path)).json()); assert.ok(customList.tools.some((item) => item.id === customResource.id)); const listed = SupportListSchema.parse(await (await request(path)).json()); 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" }, }); 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 ); }