166 lines
5.8 KiB
TypeScript
166 lines
5.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
|
|
import {
|
|
AssetResponseSchema,
|
|
InventoryResponseSchema,
|
|
WorkstationResponseSchema,
|
|
} from "@metis/assets/common";
|
|
import { CompanyResponseSchema } from "@metis/companies/common";
|
|
import type { Sequelize } from "sequelize";
|
|
import { QueryTypes } from "sequelize";
|
|
|
|
type Request = (path: string, body?: unknown, origin?: string, token?: string) => Promise<Response>;
|
|
|
|
export async function verifyWorkstationLifecycle(
|
|
request: Request,
|
|
companyId: string,
|
|
otherId: string,
|
|
database: Sequelize
|
|
) {
|
|
const path = `/assets/${companyId}/workstations`;
|
|
const inventory = InventoryResponseSchema.parse(
|
|
await (await request(`/assets/${companyId}`)).json()
|
|
);
|
|
const source = inventory.assets[0];
|
|
|
|
assert.ok(source);
|
|
const assets = [];
|
|
|
|
for (const name of ["Equipo de baja", "Impresora de baja"]) {
|
|
const response = await request(`/assets/${companyId}`, {
|
|
values: { ...source.values, name, status: "operational" },
|
|
});
|
|
|
|
assert.equal(response.status, 201, await response.clone().text());
|
|
assets.push(AssetResponseSchema.parse(await response.json()).asset);
|
|
}
|
|
const [computer, shared] = assets;
|
|
|
|
assert.ok(computer && shared);
|
|
const personResponse = await request(`/companies/${companyId}/people`, {
|
|
name: "Responsable baja",
|
|
position: null,
|
|
email: null,
|
|
phone: null,
|
|
active: true,
|
|
isContact: false,
|
|
});
|
|
|
|
assert.equal(personResponse.status, 201);
|
|
const person = CompanyResponseSchema.parse(await personResponse.json()).company.people.at(-1);
|
|
|
|
assert.ok(person);
|
|
const values = {
|
|
name: "Puesto para baja",
|
|
active: true,
|
|
siteId: source.values.siteId,
|
|
personId: person.id,
|
|
computerAssetId: computer.id,
|
|
assetIds: [computer.id, shared.id],
|
|
};
|
|
|
|
const create = async (data = values) => {
|
|
const response = await request(path, { values: data });
|
|
|
|
assert.equal(response.status, 201, await response.clone().text());
|
|
|
|
return WorkstationResponseSchema.parse(await response.json()).workstation;
|
|
};
|
|
|
|
const station = await create();
|
|
const retirePath = `${path}/${station.id}/decommission`;
|
|
const deletePath = `${path}/${station.id}/delete`;
|
|
|
|
for (const action of ["decommission", "delete"]) {
|
|
assert.equal(
|
|
(await request(`/assets/${otherId}/workstations/${station.id}/${action}`, {})).status,
|
|
404
|
|
);
|
|
assert.equal(
|
|
(await request(`${path}/${station.id}/${action}`, {}, "https://foreign.example")).status,
|
|
403
|
|
);
|
|
assert.equal(
|
|
(await request(`${path}/${station.id}/${action}`, {}, undefined, "a".repeat(64))).status,
|
|
403
|
|
);
|
|
assert.equal(
|
|
(await request(`${path}/${station.id}/${action}`, { actorId: person.id })).status,
|
|
400
|
|
);
|
|
}
|
|
const response = await request(retirePath, {});
|
|
|
|
assert.equal(response.status, 200);
|
|
const retired = WorkstationResponseSchema.parse(await response.json()).workstation;
|
|
|
|
assert.equal(retired.values.active, false);
|
|
assert.equal(retired.values.personId, null);
|
|
assert.equal(retired.values.computerAssetId ?? null, null);
|
|
assert.deepEqual(retired.values.assetIds, []);
|
|
assert.equal(retired.history.length, 2);
|
|
assert.deepEqual(retired.history[0]?.values.assetIds, [computer.id, shared.id]);
|
|
assert.deepEqual(
|
|
WorkstationResponseSchema.parse(await (await request(retirePath, {})).json()).workstation,
|
|
retired
|
|
);
|
|
// Los activos de la composición pueden reservarse de nuevo tras la baja.
|
|
const replacement = await create({
|
|
...values,
|
|
name: "Puesto reutiliza activos",
|
|
assetIds: [computer.id, shared.id],
|
|
});
|
|
|
|
assert.equal((await request(`${path}/${replacement.id}/delete`, {})).status, 200);
|
|
assert.equal((await request(deletePath, {})).status, 200);
|
|
assert.equal((await request(deletePath, {})).status, 404);
|
|
const after = InventoryResponseSchema.parse(await (await request(`/assets/${companyId}`)).json());
|
|
|
|
assert.equal(
|
|
after.workstations.some((item) => item.id === station.id),
|
|
false
|
|
);
|
|
assert.ok(after.assets.some((item) => item.id === computer.id));
|
|
assert.ok(after.assets.some((item) => item.id === shared.id));
|
|
const rows = await database.query<{ total: number }>(
|
|
"SELECT COUNT(*) AS total FROM workstation_revisions WHERE workstationId=:id",
|
|
{ replacements: { id: station.id }, type: QueryTypes.SELECT }
|
|
);
|
|
|
|
assert.equal(Number(rows[0]?.total), 0);
|
|
for (const table of ["workstation_assets", "workstation_members"]) {
|
|
const remaining = await database.query<{ total: number }>(
|
|
`SELECT COUNT(*) AS total FROM ${table} WHERE workstationId=:id`,
|
|
{ replacements: { id: station.id }, type: QueryTypes.SELECT }
|
|
);
|
|
|
|
assert.equal(Number(remaining[0]?.total), 0);
|
|
}
|
|
// La eliminación directa de un puesto activo libera su reserva sin borrar activos.
|
|
const direct = await create({ ...values, name: "Eliminar puesto activo" });
|
|
|
|
assert.equal((await request(`${path}/${direct.id}/delete`, {})).status, 200);
|
|
const reusable = await create({
|
|
...values,
|
|
name: "Reserva después de eliminación",
|
|
});
|
|
|
|
assert.equal((await request(`${path}/${reusable.id}/delete`, {})).status, 200);
|
|
const concurrent = await create({ ...values, name: "Puesto concurrente" });
|
|
const candidate = { ...values, name: "Nueva reserva concurrente" };
|
|
const [retirement, reservation] = await Promise.all([
|
|
request(`${path}/${concurrent.id}/decommission`, {}),
|
|
request(path, { values: candidate }),
|
|
]);
|
|
|
|
assert.equal(retirement.status, 200);
|
|
assert.ok([201, 409].includes(reservation.status));
|
|
const newStation =
|
|
reservation.status === 201
|
|
? WorkstationResponseSchema.parse(await reservation.json()).workstation
|
|
: await create(candidate);
|
|
|
|
assert.equal((await request(`${path}/${newStation.id}/delete`, {})).status, 200);
|
|
assert.equal((await request(`${path}/${concurrent.id}/delete`, {})).status, 200);
|
|
}
|