- Added `useAssetPickerController` for managing asset selection logic. - Created `AssetSelectProps` type for defining asset selection properties. - Implemented `AssetPickerDialog` for displaying the asset selection interface. - Developed `AssetPickerList` for rendering the list of selectable assets. - Introduced `AssetSelect` component for integrating the asset picker into forms. - Created `AssetList` component for shared asset listing functionality. - Added tests for workstation creation logic to ensure proper validation. - Implemented workstation asset fields and association fields for managing workstation assets. - Developed workstation lifecycle dialog for managing work profile states. - Added form progress component for visualizing multi-step forms.
213 lines
7.1 KiB
TypeScript
213 lines
7.1 KiB
TypeScript
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<Response>;
|
|
|
|
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");
|
|
}
|