import assert from "node:assert/strict"; import { test } from "node:test"; import { UniqueID } from "@repo/rdx-ddd"; import { Maybe } from "@repo/rdx-utils"; import { ResourceCatalog } from "./resource-catalog.aggregate"; import { seedResourceCatalog } from "./resource-catalog-seed"; import type { ResourceDefinitionSpec } from "./resource-definition"; import { ResourceDefinition } from "./resource-definition.entity"; import { ResourceDefinitionValues } from "./value-objects/resource-definition-values.vo"; function values(input: ResourceDefinitionSpec) { const result = ResourceDefinitionValues.create(input); assert.ok(result.isSuccess); return result.data; } const field = { kind: "field", name: "Marca", description: "", type: "text", unit: "", choices: [], } as const; test("catálogo de recursos: serie idempotente, UUID v7, características opcionales y protección", () => { const catalog = seedResourceCatalog(ResourceCatalog.create()); assert.equal(catalog.definitions.length, 28); assert.equal(seedResourceCatalog(catalog), catalog); assert.ok(catalog.definitions.every((item) => item.id.toPrimitive()[14] === "7")); assert.ok( catalog.definitions .filter((item) => item.values.data.kind === "field") .every((item) => item.values.data.description.includes("Ejemplo:")) ); const previousDescription = ResourceDefinitionValues.create({ kind: "field", name: "Dirección de correo", description: "Indica la dirección completa del buzón.", type: "text", unit: "", choices: [], }); assert.ok(previousDescription.isSuccess); const email = catalog.definitions.find( (item) => item.stockKey.getOrUndefined() === "field:email" ); assert.ok(email); const catalogWithoutHelp = ResourceCatalog.rehydrate( { definitions: catalog.definitions.map((item) => item.id.equals(email.id) ? ResourceDefinition.rehydrate( { companyId: item.companyId, stockKey: item.stockKey, values: previousDescription.data, active: item.active, bindings: item.bindings, }, item.id ) : item ), availability: catalog.availability, }, catalog.id ); const enriched = seedResourceCatalog(catalogWithoutHelp); assert.ok( enriched.definitions .find((item) => item.id.equals(email.id)) ?.values.data.description.includes("Ejemplo:") ); const template = catalog.definitions.find((item) => item.values.data.kind === "template"); assert.ok(template && template.values.data.kind === "template"); assert.ok(template.values.data.fields.every((binding) => !binding.required)); assert.ok(template.bindings.every((binding) => binding.id.toPrimitive()[14] === "7")); const changed = catalog.save( Maybe.none(), Maybe.some(template.id), values({ ...template.values.data, name: "Cambio" }) ); assert.ok(changed.isSuccess); const edited = changed.data.definitions.find((item) => item.id.equals(template.id)); assert.equal(edited?.values.data.name, "Cambio"); assert.equal(seedResourceCatalog(changed.data), changed.data); const disabled = catalog.setActive(Maybe.none(), template.id, false); assert.ok(disabled.isSuccess); assert.equal( seedResourceCatalog(disabled.data).definitions.find((item) => item.id.equals(template.id)) ?.active, false ); }); test("ámbitos, tipos, listas, campos externos y disponibilidad por compañía", () => { const company = UniqueID.generateNewID(); const other = UniqueID.generateNewID(); const saved = ResourceCatalog.create().save( Maybe.some(company), Maybe.none(), values({ ...field, choices: [] }) ); assert.ok(saved.isSuccess); const local = saved.data.definitions[0]; assert.ok(local); assert.equal(saved.data.visible(Maybe.some(other)).length, 0); const template = (fieldId: string): ResourceDefinitionSpec => ({ kind: "template", name: "Propia", description: "", fields: [{ fieldId, required: false }], }); for (const [scope, id] of [ [Maybe.none(), local.id], [Maybe.some(other), local.id], [Maybe.some(company), UniqueID.generateNewID()], ] as const) { const result = saved.data.save(scope, Maybe.none(), values(template(id.toPrimitive()))); assert.ok(!result.isSuccess); assert.equal(result.error.code, "FIELD_UNAVAILABLE"); } const duplicate = saved.data.save( Maybe.some(company), Maybe.none(), values({ ...field, choices: [], name: " marca " }) ); assert.ok(!duplicate.isSuccess); assert.equal(duplicate.error.code, "NAME_EXISTS"); const changeType = local.update(values({ ...field, choices: [], type: "number" })); assert.ok(!changeType.isSuccess); assert.equal(changeType.error.code, "FIELD_TYPE_FIXED"); assert.equal( ResourceDefinitionValues.create({ ...field, type: "choice", choices: ["A", " a "] }).isSuccess, false ); const choices = saved.data.save( Maybe.none(), Maybe.none(), values({ ...field, name: "Lista", type: "choice", choices: ["A", "B"] }) ); assert.ok(choices.isSuccess); const list = choices.data.definitions.find((item) => item.values.data.name === "Lista"); assert.ok(list); assert.equal( list.update(values({ ...field, name: "Lista", type: "choice", choices: ["A"] })).isSuccess, false ); const seeded = seedResourceCatalog(ResourceCatalog.create()); const stock = seeded.definitions[0]; assert.ok(stock); const override = seeded.setActive(Maybe.some(company), stock.id, false); assert.ok(override.isSuccess); assert.equal(override.data.active(stock, Maybe.some(company)), false); assert.equal(override.data.active(stock, Maybe.some(other)), true); const reset = override.data.setActive(Maybe.some(company), stock.id, null); assert.ok(reset.isSuccess); assert.equal(reset.data.active(stock, Maybe.some(company)), true); }); test("licencias: ampliar un catálogo previo conserva identidades y disponibilidad", () => { const seeded = seedResourceCatalog(ResourceCatalog.create()); const originalKeys = new Set([ "field:email", "field:provider", "field:context", "field:unc", "field:server", "field:description", "field:url", "field:holder", "field:issuer", "field:expires", "field:fingerprint", "field:purpose", "field:reference", "template:mailbox", "template:access", "template:shared_folder", "template:application", "template:certificate", ]); const previous = ResourceCatalog.rehydrate( { definitions: seeded.definitions.filter((item) => originalKeys.has(item.stockKey.getOrUndefined() ?? "") ), availability: [], }, seeded.id ); const original = previous.definitions[0]; assert.ok(original); const disabled = previous.setActive(Maybe.none(), original.id, false); assert.ok(disabled.isSuccess); const upgraded = seedResourceCatalog(disabled.data); assert.equal(upgraded.definitions.length, 28); for (const existing of disabled.data.definitions) { assert.equal( upgraded.definitions.find((item) => item.id.equals(existing.id)), existing ); } const license = upgraded.definitions.find( (item) => item.stockKey.getOrUndefined() === "template:software_license" ); assert.ok(license && license.values.data.kind === "template"); assert.ok(license.values.data.fields.every((binding) => !binding.required)); assert.equal(license.values.data.usage, "license"); const editedTemplate = license.update(values({ ...license.values.data, name: "Otra" })); assert.ok(editedTemplate.isSuccess); assert.ok(editedTemplate.data.id.equals(license.id)); const company = UniqueID.generateNewID(); const locallyDisabled = upgraded.setActive(Maybe.some(company), license.id, false); assert.ok(locallyDisabled.isSuccess); assert.equal(locallyDisabled.data.active(license, Maybe.some(company)), false); assert.equal(locallyDisabled.data.active(license, Maybe.none()), true); assert.equal(seedResourceCatalog(locallyDisabled.data), locallyDisabled.data); }); test("plantillas globales de serie editables: conserva identidad, asociaciones, disponibilidad y cambios al reiniciar", () => { const initial = seedResourceCatalog(ResourceCatalog.create()); const template = initial.definitions.find((item) => item.values.data.kind === "template"); assert.ok(template?.values.data.kind === "template"); const original = template.values.data; const input = values({ ...original, name: "Plantilla revisada", description: "Personalizada", sections: [], fields: original.fields .slice(0, 1) .map((field) => ({ fieldId: field.fieldId, required: true })), }); const result = initial.save(Maybe.none(), Maybe.some(template.id), input); assert.ok(result.isSuccess); const edited = result.data.definitions.find((item) => item.id.equals(template.id)); assert.ok(edited?.values.data.kind === "template"); assert.equal(edited.values.data.name, "Plantilla revisada"); assert.equal(edited.values.data.fields[0]?.required, true); assert.ok(edited.bindings[0]?.id.equals(template.bindings[0]?.id)); assert.equal(edited.stockKey.getOrUndefined(), template.stockKey.getOrUndefined()); assert.equal(edited.active, template.active); assert.equal(seedResourceCatalog(result.data), result.data); assert.deepEqual(template.values.data, original); const repeated = result.data.save(Maybe.none(), Maybe.some(template.id), input); assert.ok(repeated.isSuccess); assert.equal(repeated.data, result.data); const crossScope = result.data.save( Maybe.some(UniqueID.generateNewID()), Maybe.some(template.id), input ); assert.ok(!crossScope.isSuccess); const protectedField = initial.definitions.find((item) => item.values.data.kind === "field"); assert.ok(protectedField); const fieldChange = initial.save( Maybe.none(), Maybe.some(protectedField.id), values({ ...protectedField.values.data, name: "Campo revisado" }) ); assert.ok(fieldChange.isSuccess); const fieldEdited = fieldChange.data.definitions.find((item) => item.id.equals(protectedField.id) ); assert.equal(fieldEdited?.values.data.name, "Campo revisado"); assert.equal(seedResourceCatalog(fieldChange.data), fieldChange.data); }); test("campos globales de serie editables: listas ampliables, tipo fijo y opciones existentes conservadas", () => { const catalog = seedResourceCatalog(ResourceCatalog.create()); const field = catalog.definitions.find( (item) => item.values.data.kind === "field" && item.values.data.type === "choice" ); assert.ok(field?.values.data.kind === "field"); const previous = field.values.data; const added = values({ ...previous, name: "Lista revisada", description: "Personalizada", unit: "unidad", choices: [...previous.choices, "Nueva opción"], }); const result = catalog.save(Maybe.none(), Maybe.some(field.id), added); assert.ok(result.isSuccess); const edited = result.data.definitions.find((item) => item.id.equals(field.id)); assert.ok(edited); assert.deepEqual(edited.values.data, added.data); assert.equal(seedResourceCatalog(result.data), result.data); assert.deepEqual(field.values.data, previous); const differentType = result.data.save( Maybe.none(), Maybe.some(field.id), values({ ...previous, type: "text", choices: [] }) ); assert.ok(!differentType.isSuccess); assert.equal(differentType.error.code, "FIELD_TYPE_FIXED"); const removedOption = result.data.save(Maybe.none(), Maybe.some(field.id), values(previous)); assert.ok(!removedOption.isSuccess); assert.equal(removedOption.error.code, "FIELD_TYPE_FIXED"); }); test("sistema operativo: plantilla con uso explícito, copia y ampliación sin sobrescribir definiciones", () => { const catalog = seedResourceCatalog(ResourceCatalog.create()); const template = catalog.definitions.find( (item) => item.stockKey.getOrUndefined() === "template:os" ); assert.ok(template && template.values.data.kind === "template"); assert.equal(template.values.data.usage, "os"); assert.equal(template.values.data.fields.length, 3); assert.equal(seedResourceCatalog(catalog), catalog); });