156 lines
6.1 KiB
TypeScript
156 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: "available",
|
||
|
|
siteId: null,
|
||
|
|
personId: 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);
|
||
|
|
}
|