metis-desk/apps/server/src/support.integration.ts
David 002781b56d feat: update asset status handling and improve UI labels
- Changed asset status label from "status" to "technicalStatus" in AssetForm component.
- Added help text for technical status in AssetForm.
- Updated test cases to reflect changes in asset status from "available" to "operational".
- Enhanced workstation form to utilize new asset status checks and display appropriate messages.
- Refactored asset context to replace "available" with "notRetired" across various application policies and repositories.
- Introduced new utility functions for asset status management, including technical status determination and assignment validation.
- Added integration tests for asset state verification and unlinking assets from workstations.
2026-10-13 11:35:31 +02:00

261 lines
8.3 KiB
TypeScript

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";
import { unlinkAssetFromWorkstations } from "./unlink-asset.integration-helper";
type Request = (path: string, body?: unknown, origin?: string, token?: string) => Promise<Response>;
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);
await unlinkAssetFromWorkstations(request, companyId, host.id);
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
);
}