import assert from "node:assert/strict"; import { test } from "node:test"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import { Resource } from "../domain/resource.aggregate"; import { ResourceCatalog } from "../domain/resource-catalog.aggregate"; import { ResourceCatalogError } from "../domain/resource-definition"; import { ResourceSpecification, ResourceValues } from "../domain/value-objects/resource-values.vo"; import type { IResourceRepository, InventoryContext } from "./resource.repository"; import { ResourceUseCases } from "./resource.use-cases"; function fixture() { const company = UniqueID.generateNewID(); const actor = UniqueID.generateNewID(); const templateId = UniqueID.generateNewID().toPrimitive(); const fieldId = UniqueID.generateNewID().toPrimitive(); const spec = ResourceSpecification.create({ templateId, templateName: "Aplicación", fields: [{ fieldId, name: "Fabricante", type: "text", unit: "", choices: [], required: false }], }); assert.ok(spec.isSuccess); const values = ResourceValues.create( { name: "Office 2024", templateId, active: true, notes: "Notas conservadas", characteristics: { [fieldId]: "Microsoft" }, }, spec.data ); assert.ok(values.isSuccess); const created = Resource.create( company, spec.data, values.data, actor, "2026-01-01T12:00:00.000Z" ); assert.ok(created.isSuccess); let resources = [created.data]; let inUse = false; const context = (): InventoryContext => ({ catalog: ResourceCatalog.create(), resources, references: [], actorNames: new Map([[actor.toPrimitive(), "Técnico"]]), }); const repository: IResourceRepository = { get: async () => Result.ok(context()), save: async (id, action) => { const changed = action({ ...context(), resources: id.equals(company) ? resources : [] }); if (!changed.isSuccess) { return Result.fail(changed.error); } resources = [changed.data]; return Result.ok({ resource: changed.data, actorNames: context().actorNames }); }, remove: async (id, identity) => { if (!(id.equals(company) && resources.some((resource) => resource.id.equals(identity)))) { return Result.fail(new ResourceCatalogError("RESOURCE_NOT_FOUND")); } if (inUse) { return Result.fail(new ResourceCatalogError("RESOURCE_IN_USE")); } resources = []; return Result.ok(undefined); }, }; return { service: new ResourceUseCases(repository), created: created.data, company: company.toPrimitive(), actor: actor.toPrimitive(), setInUse: () => { inUse = true; }, }; } test("recurso: activar/desactivar conserva datos e historial y repetir estado no añade revisiones", async () => { const { service, company, actor, created } = fixture(); for (const [active, revisions] of [ [false, 2], [false, 2], [true, 3], ] as const) { const result = await service.setActive(company, created.id.toPrimitive(), active, actor); assert.ok(result.isSuccess); assert.equal(result.data.resource.values.toPrimitive().active, active); assert.equal(result.data.resource.history.length, revisions); assert.deepEqual( result.data.resource.values.toPrimitive().characteristics, created.values.toPrimitive().characteristics ); assert.equal( result.data.resource.values.toPrimitive().notes, created.values.toPrimitive().notes ); assert.equal(result.data.resource.history.at(-1)?.actorId.toPrimitive(), actor); } }); test("recurso: valida identidades y rechaza actuar sobre recursos ajenos", async () => { const { service, company, actor, created } = fixture(); const id = created.id.toPrimitive(); const invalid = await service.setActive("invalid", id, false, actor); assert.ok(!invalid.isSuccess); assert.equal(invalid.error.code, "INVALID_REQUEST"); const invalidActor = await service.setActive(company, id, false, "invalid"); assert.ok(!invalidActor.isSuccess); const invalidDelete = await service.remove(company, "invalid"); assert.ok(!invalidDelete.isSuccess); const other = UniqueID.generateNewID().toPrimitive(); const foreign = await service.setActive(other, id, false, actor); assert.ok(!foreign.isSuccess); assert.equal(foreign.error.code, "RESOURCE_NOT_FOUND"); const foreignDelete = await service.remove(other, id); assert.ok(!foreignDelete.isSuccess); assert.equal(foreignDelete.error.code, "RESOURCE_NOT_FOUND"); }); test("recurso: propaga el bloqueo por asociaciones y permite desactivar", async () => { const { service, company, actor, created, setInUse } = fixture(); setInUse(); const removed = await service.remove(company, created.id.toPrimitive()); assert.ok(!removed.isSuccess); assert.equal(removed.error.code, "RESOURCE_IN_USE"); assert.ok((await service.setActive(company, created.id.toPrimitive(), false, actor)).isSuccess); }); test("recurso: elimina y la repetición devuelve ausencia sin efectos adicionales", async () => { const { service, company, created } = fixture(); assert.ok((await service.remove(company, created.id.toPrimitive())).isSuccess); const missing = await service.remove(company, created.id.toPrimitive()); assert.ok(!missing.isSuccess); assert.equal(missing.error.code, "RESOURCE_NOT_FOUND"); });