- 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.
261 lines
8.3 KiB
TypeScript
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
|
|
);
|
|
}
|