metis-desk/apps/server/src/relationships.integration.ts
David 002781b56d feat: update asset status handling and improve UI labels
- 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.
2026-10-13 11:35:31 +02:00

179 lines
6.1 KiB
TypeScript

import assert from "node:assert/strict";
import {
AssetResponseSchema,
CatalogResponseSchema,
InventoryResponseSchema,
RelationshipResponseSchema,
SaveDefinitionResponseSchema,
} from "@metis/assets/common";
import type { Sequelize } from "sequelize";
import { z } from "zod/v4";
type Request = (path: string, body?: unknown, origin?: string, token?: string) => Promise<Response>;
export async function verifyRelationships(
request: Request,
companyId: string,
otherId: string,
database: Sequelize
) {
const path = `/assets/${companyId}`;
const catalog = CatalogResponseSchema.parse(
await (await request(`/asset-catalog/${companyId}`)).json()
);
const type = catalog.items.find(
(item) => item.values.kind === "relation" && item.values.symmetric
);
assert.ok(type);
const template = catalog.items.find(
(item) => item.values.kind === "template" && item.values.name === "Monitor"
);
assert.ok(template);
const own = InventoryResponseSchema.parse(await (await request(path)).json()).assets;
const first = own[0];
const second = own[1];
assert.ok(first && second);
const foreignResponse = await request(`/assets/${otherId}`, {
values: {
name: "MON-Foreign",
templateId: template.id,
status: "operational",
siteId: null,
characteristics: {},
notes: "",
},
});
assert.equal(foreignResponse.status, 201);
const foreign = AssetResponseSchema.parse(await foreignResponse.json()).asset;
const input = { sourceId: first.id, targetId: second.id, typeId: type.id, notes: "USB" };
const linkPath = `${path}/relationships`;
const save = async (values: unknown) => {
const response = await request(linkPath, { values });
assert.equal(response.status, 201, await response.clone().text());
return RelationshipResponseSchema.parse(await response.json()).relationship;
};
const error = async (values: unknown, code: string, status = 409) => {
const response = await request(linkPath, { values });
assert.equal(response.status, status);
assert.equal(z.object({ code: z.string() }).parse(await response.json()).code, code);
};
await error({ ...input, targetId: first.id }, "SELF_RELATIONSHIP");
await error({ ...input, targetId: foreign.id }, "ASSET_NOT_FOUND", 404);
await error({ ...input, typeId: template.id }, "RELATION_TYPE_UNAVAILABLE");
await error({ ...input, createdBy: first.id }, "INVALID_REQUEST", 400);
assert.equal((await request(linkPath, { values: input }, "https://other.example")).status, 403);
assert.equal((await request(linkPath, { values: input }, undefined, "a".repeat(64))).status, 403);
const [created, retry] = await Promise.all([save(input), save(input)]);
assert.equal(created.id, retry.id);
assert.equal(created.id.charAt(14), "7");
assert.ok(created.createdByName);
assert.equal((await save({ ...input, sourceId: second.id, targetId: first.id })).id, created.id);
await error({ ...input, notes: "Otro cable" }, "RELATIONSHIP_EXISTS");
await assert.rejects(
database.query("UPDATE asset_relationships SET targetId=:target WHERE id=:id", {
replacements: { target: foreign.id, id: created.id },
})
);
assert.equal(
(await request(`/asset-catalog/${companyId}/${type.id}/status`, { active: false })).status,
200
);
await error(input, "RELATION_TYPE_UNAVAILABLE");
const endPath = `${path}/relationships/${created.id}/end`;
const ending = await request(endPath, {});
assert.equal(ending.status, 200);
const ended = RelationshipResponseSchema.parse(await ending.json()).relationship;
assert.ok(ended.endedAt);
assert.ok(ended.endedByName);
assert.deepEqual(
RelationshipResponseSchema.parse(await (await request(endPath, {})).json()).relationship,
ended
);
assert.equal(
(await request(`/assets/${otherId}/relationships/${created.id}/end`, {})).status,
404
);
assert.equal(
(await request(`/asset-catalog/${companyId}/${type.id}/status`, { active: null })).status,
200
);
const replacement = await save(input);
assert.notEqual(replacement.id, created.id);
await verifyDirectional(request, companyId, otherId, first.id, second.id);
const stored = InventoryResponseSchema.parse(await (await request(path)).json()).relationships;
assert.deepEqual(
stored.find((item) => item.id === ended.id),
ended
);
assert.ok(
!InventoryResponseSchema.parse(await (await request(`/assets/${otherId}`)).json()).relationships
.length
);
}
async function verifyDirectional(
request: Request,
companyId: string,
otherId: string,
sourceId: string,
targetId: string
) {
const definition = {
kind: "relation",
name: "Replica a",
inverseName: "Recibe réplica de",
description: "",
symmetric: false,
};
const response = await request(`/asset-catalog/${companyId}`, { values: definition });
assert.equal(response.status, 201);
const typeId = SaveDefinitionResponseSchema.parse(await response.json()).savedId;
const path = `/assets/${companyId}/relationships`;
const values = { sourceId, targetId, typeId, notes: "" };
const added = await request(path, { values });
assert.equal(added.status, 201);
const link = RelationshipResponseSchema.parse(await added.json()).relationship;
const reverse = await request(path, {
values: { ...values, sourceId: targetId, targetId: sourceId },
});
assert.equal(reverse.status, 201);
assert.notEqual(RelationshipResponseSchema.parse(await reverse.json()).relationship.id, link.id);
const edited = await request(`/asset-catalog/${companyId}/${typeId}/update`, {
values: { ...definition, name: "Replica hacia" },
});
assert.equal(edited.status, 200);
const conversion = await request(`/asset-catalog/${companyId}/${typeId}/update`, {
values: { ...definition, symmetric: true },
});
assert.equal(conversion.status, 409);
const stored = InventoryResponseSchema.parse(
await (await request(`/assets/${companyId}`)).json()
).relationships.find((item) => item.id === link.id);
assert.equal(stored?.values.name, "Replica a");
const other = await request(`/assets/${otherId}/relationships`, { values });
assert.equal(other.status, 404);
}