import assert from "node:assert/strict"; import type { AssetInput } from "@metis/assets/common"; import { AssetResponseSchema, InventoryResponseSchema, WorkstationResponseSchema, } from "@metis/assets/common"; import { SoftwareInstallationResponseSchema, SoftwareInstallationsResponseSchema, } from "@metis/companies/common"; import { CatalogResponseSchema as ResourceCatalogSchema, ResourceResponseSchema, } from "@metis/resources/common"; import type { Sequelize } from "sequelize"; import { QueryTypes } from "sequelize"; import { z } from "zod/v4"; import { enforceAssetRelationalConstraints } from "../../../modules/assets/src/api/infrastructure/persistence/asset-relational-constraints"; type Request = (path: string, body?: unknown) => Promise; export async function verifyAssetStates(request: Request, companyId: string, database: Sequelize) { const path = `/assets/${companyId}`; const source = InventoryResponseSchema.parse(await (await request(path)).json()).assets[0]; assert.ok(source); const response = await request(path, { values: { ...source.values, status: "operational", name: "Estado técnico prueba" }, }); assert.equal(response.status, 201, await response.clone().text()); const original = AssetResponseSchema.parse(await response.json()).asset; const current = async () => { const asset = InventoryResponseSchema.parse(await (await request(path)).json()).assets.find( (item) => item.id === original.id ); assert.ok(asset); return asset; }; const state = async (status: AssetInput["status"]) => { const previous = await current(); const result = await request(`${path}/${original.id}/update`, { values: { ...previous.values, status }, }); assert.equal(result.status, 200, await result.clone().text()); return AssetResponseSchema.parse(await result.json()).asset; }; const stationValues = { name: "Asignación prueba", active: true, siteId: null, personId: null, assetIds: [original.id], }; const station = async (name: string) => { const result = await request(`${path}/workstations`, { values: { ...stationValues, name } }); assert.equal(result.status, 201, await result.clone().text()); return WorkstationResponseSchema.parse(await result.json()).workstation; }; assert.equal(original.displayStatus, "available"); assert.equal(original.assigned, false); const first = await station("Puesto de prueba"); const assigned = await current(); assert.equal(assigned.values.status, "operational"); assert.equal(assigned.displayStatus, "in_use"); assert.equal(assigned.assigned, true); assert.equal(assigned.history.length, 1); const repairing = await state("maintenance"); assert.equal(repairing.displayStatus, "maintenance"); assert.equal(repairing.assigned, true); const resourceCatalog = ResourceCatalogSchema.parse( await (await request(`/resource-catalog/${companyId}`)).json() ); const osTemplate = resourceCatalog.items.find((item) => item.builtinKey === "template:os"); assert.ok(osTemplate); const osResponse = await request(`/resources/${companyId}`, { values: { name: "SO durante reparación", templateId: osTemplate.id, active: true, notes: "", characteristics: {}, }, }); assert.equal(osResponse.status, 201); const os = ResourceResponseSchema.parse(await osResponse.json()).resource; const installationResponse = await request(`/software-installations/${companyId}`, { values: { assetId: original.id, resourceId: os.id, licenseResourceId: null, version: "24H2", state: "configured", notes: null, }, }); assert.equal(installationResponse.status, 201, await installationResponse.clone().text()); const installation = SoftwareInstallationResponseSchema.parse( await installationResponse.json() ).configuration; assert.equal(installation.needsReview, false); assert.equal( ( await request(`${path}/workstations/${first.id}/update`, { values: { ...first.values, name: "Puesto durante reparación" }, }) ).status, 200 ); for (const status of ["maintenance", "broken"] as const) { await state(status); const blocked = await request(`${path}/workstations`, { values: { ...stationValues, name: "Asignación no permitida" }, }); assert.equal(blocked.status, 409); assert.equal( z.object({ code: z.string() }).parse(await blocked.json()).code, "ASSET_NOT_OPERATIONAL" ); } const retiredWhileAssigned = await request(`${path}/${original.id}/update`, { values: { ...(await current()).values, status: "retired" }, }); assert.equal(retiredWhileAssigned.status, 409); assert.equal( z.object({ code: z.string() }).parse(await retiredWhileAssigned.json()).code, "ASSET_ASSIGNED" ); assert.equal((await current()).values.status, "broken"); await state("operational"); assert.equal((await request(`${path}/workstations/${first.id}/decommission`, {})).status, 200); assert.equal((await request(`${path}/workstations/${first.id}/delete`, {})).status, 200); assert.equal((await current()).displayStatus, "available"); assert.equal((await current()).assigned, false); const retired = await state("retired"); assert.equal(retired.displayStatus, "retired"); const installations = SoftwareInstallationsResponseSchema.parse( await (await request(`/software-installations/${companyId}`)).json() ); assert.equal( installations.configurations.find((item) => item.id === installation.id)?.needsReview, true ); assert.equal((await request(`${path}/workstations`, { values: stationValues })).status, 409); for (const status of ["in_use", "available"]) { assert.equal( (await request(`${path}/${original.id}/update`, { values: { ...retired.values, status } })) .status, 400 ); } // Datos históricos originales: no se convierten en asignaciones ficticias ni se reescriben. await state("operational"); const before = await current(); const revisionId = before.history.at(-1)?.id; assert.ok(revisionId); await database.transaction(async (transaction) => { await database.query("UPDATE assets SET status='in_use' WHERE id=:id", { replacements: { id: original.id }, transaction, }); await database.query("UPDATE asset_revisions SET status='in_use' WHERE id=:id", { replacements: { id: revisionId }, transaction, }); }); await enforceAssetRelationalConstraints(database); const historical = await current(); assert.equal(historical.values.status, "operational"); assert.equal(historical.displayStatus, "available"); assert.equal(historical.history.at(-1)?.values.status, "in_use"); const repeated = await request(`${path}/${original.id}/update`, { values: historical.values }); assert.equal(repeated.status, 200); assert.deepEqual( AssetResponseSchema.parse(await repeated.json()).asset.history, historical.history ); const stored = await database.query<{ status: string }>( "SELECT status FROM asset_revisions WHERE id=:id", { replacements: { id: revisionId }, type: QueryTypes.SELECT } ); assert.equal(stored[0]?.status, "in_use"); }