metis-desk/modules/resources/src/api/application/resource-reference-contracts.test.ts
2026-10-25 01:19:17 +02:00

242 lines
7.4 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "node:test";
import { UniqueID } from "@repo/rdx-ddd";
import { normalizeSearchText } from "@repo/rdx-utils";
import {
CreateGlobalApplicationRequestSchema,
ListApplicationCandidatesRequestSchema,
ListApplicationCandidatesResponseSchema,
ResolveApplicationReferencesRequestSchema,
ResolveApplicationReferencesResponseSchema,
SoftwareReferenceSchema,
} from "../../common";
import { globalApplicationTemplateEligible } from "./global-application-template-policy";
import { ResourceGlobalGatesSchema, globalResourcePurposeEnabled } from "./resource-global-gates";
import { paginateResourceMetadata } from "./resource-reference-pagination";
const id = () => UniqueID.generateNewID().toPrimitive();
test("elegibilidad global exige serie exacta y campos globales; las copias no elevan ámbito", () => {
const template = {
kind: "template",
stockKey: "template:application",
companyId: null,
hasLocalFields: false,
};
assert.equal(globalApplicationTemplateEligible(template), true);
for (const change of [
{ stockKey: null },
{ stockKey: "template:os" },
{ stockKey: "template:software_license" },
{ stockKey: "template:APPLICATION" },
{ kind: "field" },
{ companyId: id() },
{ hasLocalFields: true },
]) {
assert.equal(globalApplicationTemplateEligible({ ...template, ...change }), false);
}
});
test("schemas discriminan owner, propósito, OS local y proyecciones privadas", () => {
const reference = {
id: id(),
templateId: id(),
name: "Aplicación",
active: true,
kind: "application",
scope: "global",
companyId: null,
};
assert.equal(SoftwareReferenceSchema.safeParse(reference).success, true);
for (const change of [
{ companyId: id() },
{ kind: "os" },
{ notes: "Privado" },
{ history: [] },
{ scope: "GLOBAL" },
]) {
assert.equal(SoftwareReferenceSchema.safeParse({ ...reference, ...change }).success, false);
}
const os = { ...reference, kind: "os", scope: "company", companyId: id() };
const payload = { items: [{ id: os.id, status: "resolved", reference: os }] };
assert.equal(
ResolveApplicationReferencesResponseSchema.safeParse({
...payload,
purpose: "software-installation",
}).success,
true
);
assert.equal(
ResolveApplicationReferencesResponseSchema.safeParse({
...payload,
purpose: "application-configuration",
}).success,
false
);
assert.equal(
ListApplicationCandidatesResponseSchema.safeParse({
purpose: "application-configuration",
items: [{ ...os, provider: null }],
nextCursor: null,
}).success,
false
);
});
test("requests acotan lotes, consulta, paginación y rechazan privilegios o integración", () => {
const ids = Array.from({ length: 101 }, id);
const query = { purpose: "software-installation" };
for (const input of [[], ids, [ids[0], ids[0]], ["invalid"]]) {
assert.equal(
ResolveApplicationReferencesRequestSchema.safeParse({ ...query, ids: input }).success,
false
);
}
assert.equal(
ResolveApplicationReferencesRequestSchema.safeParse({ ...query, ids: ids.slice(0, 100) })
.success,
true
);
for (const extra of [
{ pageSize: 0 },
{ pageSize: 101 },
{ search: "a".repeat(101) },
{ cursor: "a".repeat(2049) },
{ purpose: "remote" },
{ canManageGlobalResources: true },
]) {
assert.equal(
ListApplicationCandidatesRequestSchema.safeParse({ ...query, ...extra }).success,
false
);
}
const create = {
creationId: id(),
values: { name: "Producto", templateId: id(), active: true, notes: "", characteristics: {} },
};
assert.equal(CreateGlobalApplicationRequestSchema.safeParse(create).success, true);
for (const extra of [
{ scope: "global" },
{ companyId: null },
{ isInitialAdmin: true },
{ actorId: id() },
]) {
assert.equal(
CreateGlobalApplicationRequestSchema.safeParse({ ...create, ...extra }).success,
false
);
}
assert.equal(
CreateGlobalApplicationRequestSchema.safeParse({
...create,
values: { ...create.values, integration: {} },
}).success,
false
);
});
test("búsqueda literal conserva ñ, puntuación y espacios; pagina tras filtrar con desempate UUID", () => {
assert.equal(normalizeSearchText(" ÁÉÏõÜ Ñ %_ "), "aeiou ñ %_");
const rows = Array.from({ length: 51 }, (_, index) => ({
id: id(),
name: index < 25 ? "Aplicación Ágil %_" : "Aplicación Ágil",
provider: "Fábrica",
}));
const base = {
search: "%_",
pageSize: 7,
sort: "name",
direction: "asc",
context: "company-A/software",
} as const;
const seen = new Set<string>();
let cursor: string | undefined;
do {
const page = paginateResourceMetadata(rows, { ...base, cursor });
assert.ok(page.isSuccess);
for (const item of page.data.items) {
assert.equal(seen.has(item.id), false);
seen.add(item.id);
}
cursor = page.data.nextCursor ?? undefined;
if (cursor) {
assert.equal(
paginateResourceMetadata(rows, { ...base, cursor, context: "company-B/software" })
.isSuccess,
false
);
assert.equal(
paginateResourceMetadata(rows, { ...base, cursor, search: "otra" }).isSuccess,
false
);
}
} while (cursor);
assert.equal(seen.size, 25);
assert.equal(paginateResourceMetadata(rows, { ...base, cursor: "corrupto" }).isSuccess, false);
});
test("R2.5 admite gates configurables; los valores predeterminados siguen cerrados", () => {
const gates = {
catalog: false,
administration: false,
mixedSelection: false,
software: false,
profile: false,
};
assert.equal(ResourceGlobalGatesSchema.safeParse(gates).success, true);
assert.equal(
ResourceGlobalGatesSchema.safeParse({ ...gates, catalog: true, administration: true }).success,
true
);
assert.equal(ResourceGlobalGatesSchema.safeParse({ ...gates, profile: true }).success, true);
});
test("R2.5: software y perfil se habilitan por separado y requieren catálogo", () => {
const open = {
catalog: true,
administration: true,
mixedSelection: true,
software: true,
profile: false,
} as const;
assert.ok(ResourceGlobalGatesSchema.safeParse(open).success);
assert.equal(globalResourcePurposeEnabled("software-installation", open), true);
assert.equal(
globalResourcePurposeEnabled("software-installation", { ...open, catalog: false }),
false
);
assert.equal(globalResourcePurposeEnabled("application-configuration", open), false);
assert.ok(ResourceGlobalGatesSchema.safeParse({ ...open, mixedSelection: false }).success);
assert.ok(ResourceGlobalGatesSchema.safeParse({ ...open, software: false }).success);
assert.equal(ResourceGlobalGatesSchema.safeParse({ ...open, profile: true }).success, true);
});
test("R2.5: perfil no depende de software ni abre asociaciones con catálogo cerrado", () => {
const profileOnly = {
catalog: true,
administration: false,
mixedSelection: false,
software: false,
profile: true,
};
assert.equal(globalResourcePurposeEnabled("application-configuration", profileOnly), true);
assert.equal(globalResourcePurposeEnabled("software-installation", profileOnly), false);
assert.equal(
globalResourcePurposeEnabled("application-configuration", { ...profileOnly, catalog: false }),
false
);
});