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

210 lines
7.1 KiB
TypeScript

import assert from "node:assert/strict";
import {
AssetResponseSchema,
CatalogResponseSchema,
InventoryResponseSchema,
SaveDefinitionResponseSchema,
} from "@metis/assets/common";
import { CompanyResponseSchema } from "@metis/companies/common";
import { z } from "zod/v4";
import { UniqueID } from "../../../packages/rdx-ddd/src/index";
export async function verifyInventory(
request: (path: string, body?: unknown, origin?: string, token?: string) => Promise<Response>,
companyId: string,
otherId: string
) {
const path = `/assets/${companyId}`;
async function save(url: string, values: unknown) {
const response = await request(url, { values });
assert.ok([200, 201].includes(response.status), await response.clone().text());
return AssetResponseSchema.parse(await response.json()).asset;
}
async function error(url: string, values: unknown, code: string, status = 409) {
const response = await request(url, { values });
assert.equal(response.status, status);
assert.equal(z.object({ code: z.string() }).parse(await response.json()).code, code);
}
async function read(url = path) {
const response = await request(url);
assert.equal(response.status, 200);
return InventoryResponseSchema.parse(await response.json());
}
const catalog = CatalogResponseSchema.parse(
await (await request(`/asset-catalog/${companyId}`)).json()
);
const monitor = catalog.items.find((item) => item.values.name === "Monitor");
assert.ok(monitor);
const laptop = catalog.items.find((item) => item.values.name === "Portátil");
assert.ok(laptop);
const inch = catalog.items.find((item) => item.values.name === "Tamaño de pantalla");
assert.ok(inch);
async function site(company: string) {
const response = await request(`/companies/${company}/sites`, {
name: "Sede inventario",
address: null,
});
assert.equal(response.status, 201);
const saved = CompanyResponseSchema.parse(await response.json()).company.sites.at(-1);
assert.ok(saved);
return saved;
}
const ownSite = await site(companyId);
const foreignSite = await site(otherId);
const input = {
name: "MON-01",
templateId: monitor.id,
siteId: null,
status: "available",
notes: "",
characteristics: {},
};
assert.equal((await read()).assets.length, 0);
const created = await save(path, input);
assert.equal(created.history.length, 1);
assert.equal(created.id.charAt(14), "7");
assert.equal(created.history[0]?.id.charAt(14), "7");
assert.ok(created.history[0]?.actorName);
assert.ok(created.history[0]?.actorId);
assert.equal((await read(`/assets/${otherId}`)).assets.length, 0);
await error(`/assets/${otherId}/${created.id}/update`, input, "ASSET_NOT_FOUND", 404);
await error(path, { ...input, siteId: foreignSite.id }, "SITE_NOT_FOUND", 404);
await error(path, { ...input, characteristics: { [inch.id]: 24 } }, "INVALID_CHARACTERISTICS");
await error(
path,
{ ...input, characteristics: { [foreignSite.id]: 24 } },
"INVALID_CHARACTERISTICS"
);
await error(path, { ...input, actorId: foreignSite.id }, "INVALID_REQUEST", 400);
assert.equal((await request(path, { values: input }, "https://other.example")).status, 403);
assert.equal((await request(path, { values: input }, undefined, "a".repeat(64))).status, 403);
const inUse = await save(`${path}/${created.id}/update`, {
...input,
siteId: ownSite.id,
status: "in_use",
characteristics: { [inch.id]: "24 pulgadas" },
});
assert.equal(inUse.id, created.id);
assert.equal(inUse.history.length, 2);
const unchanged = await save(`${path}/${created.id}/update`, inUse.values);
assert.deepEqual(unchanged.history, inUse.history);
await error(
`${path}/${created.id}/update`,
{ ...inUse.values, templateId: laptop.id },
"TEMPLATE_FIXED"
);
const retired = await save(`${path}/${created.id}/update`, {
...inUse.values,
status: "retired",
});
assert.equal(retired.history.length, 3);
const section = { id: UniqueID.generateNewID().toPrimitive(), name: "Pantalla", description: "" };
const templateResponse = await request(`/asset-catalog/${companyId}`, {
values: {
kind: "template",
name: "Monitor obligatorio inventario",
sections: [section],
description: "",
fields: [{ fieldId: inch.id, required: true, sectionId: section.id }],
},
});
assert.equal(templateResponse.status, 201);
const templateId = SaveDefinitionResponseSchema.parse(await templateResponse.json()).savedId;
await error(path, { ...input, templateId }, "REQUIRED_CHARACTERISTICS");
await error(`/assets/${otherId}`, { ...input, templateId }, "TEMPLATE_UNAVAILABLE");
const custom = await save(path, {
...input,
templateId,
name: "MON-02",
characteristics: { [inch.id]: "0" },
});
const revisionId = custom.history[0]?.id;
assert.equal(
(
await request(`/asset-catalog/${companyId}/${templateId}/update`, {
values: {
kind: "template",
name: "Plantilla renombrada",
description: "",
fields: [{ fieldId: inch.id, required: false }],
},
})
).status,
200
);
const current = (await read()).assets.find((asset) => asset.id === custom.id);
assert.ok(current);
assert.equal(current.specification.templateName, "Monitor obligatorio inventario");
assert.equal(current.specification.fields[0]?.required, true);
assert.deepEqual(current.specification.sections, [section]);
assert.equal(current.specification.fields[0]?.sectionId, section.id);
await error(
`${path}/${custom.id}/update`,
{ ...custom.values, characteristics: {} },
"REQUIRED_CHARACTERISTICS"
);
assert.equal(
(await request(`/asset-catalog/${companyId}/${templateId}/status`, { active: false })).status,
200
);
await error(path, { ...input, templateId }, "TEMPLATE_UNAVAILABLE");
const updated = await save(`${path}/${custom.id}/update`, {
...custom.values,
notes: "Plantilla conservada",
});
assert.equal(updated.history[0]?.id, revisionId);
const stored = (await read()).assets.find((asset) => asset.id === updated.id);
assert.deepEqual(stored, updated);
const router = catalog.items.find((item) => item.values.name === "Router");
const ports = catalog.items.find((item) => item.values.name === "Puertos físicos");
assert.ok(router && ports);
const networkAsset = await save(path, {
...input,
name: "Router texto largo",
templateId: router.id,
characteristics: { [ports.id]: "24 RJ45\n4 SFP+" },
});
const networkUpdated = await save(`${path}/${networkAsset.id}/update`, {
...networkAsset.values,
characteristics: { [ports.id]: "24 RJ45\n4 SFP+\nUSB de servicio" },
});
const networkStored = (await read()).assets.find((asset) => asset.id === networkUpdated.id);
assert.deepEqual(networkStored, networkUpdated);
assert.equal(networkStored?.history[0]?.values.characteristics[ports.id], "24 RJ45\n4 SFP+");
assert.equal(networkStored?.values.characteristics[ports.id], "24 RJ45\n4 SFP+\nUSB de servicio");
assert.equal((await request("/assets/00000000-0000-4000-8000-000000000001")).status, 404);
}