import assert from "node:assert/strict"; import { test } from "node:test"; import { UniqueID } from "@repo/rdx-ddd"; import type { ApplicationContext } from "../application/application-configuration.repository"; import { mapApplicationInput } from "../application/application-configuration-input.mapper"; import { buildApplicationResponse } from "../application/application-configuration-response.builder"; import { ApplicationConfigurationPersistenceMapper } from "../infrastructure/persistence/application-configuration-persistence.mapper"; import { ApplicationConfiguration } from "./application-configuration.aggregate"; import type { ApplicationConfigurationInput } from "./application-configuration-values"; import { ApplicationConfigurationInputSchema } from "./application-configuration-values"; import { ApplicationConfigurationValues } from "./value-objects/application-configuration-values.vo"; import { WorkProfileValues } from "./value-objects/work-profile-values.vo"; import { WorkProfile } from "./work-profile.aggregate"; const company = UniqueID.generateNewID(); const actor = UniqueID.generateNewID(); const person = UniqueID.generateNewID(); const host = UniqueID.generateNewID(); const resource = UniqueID.generateNewID(); const date = "2026-10-06T12:00:00.000Z"; function fixture() { const value = WorkProfileValues.create({ name: "Ana", personId: person.toPrimitive(), assetId: host.toPrimitive(), accountId: null, active: true, notes: null, }); assert.ok(value.isSuccess); const profile = WorkProfile.create(company, value.data, actor, date); assert.ok(profile.isSuccess); const input: ApplicationConfigurationInput = { profileId: profile.data.id.toPrimitive(), resourceId: resource.toPrimitive(), delivery: "installed", version: null, state: "pending", notes: null, }; const context: ApplicationContext = { globalResourceIds: new Set(), profiles: [profile.data], configurations: [], people: [{ id: person, active: true }], assets: [{ id: host, notRetired: true }], resources: [{ id: resource, active: true, usage: "application" }], actors: new Map([[actor.toPrimitive(), "Técnico"]]), }; return { input, context }; } function values(input: ApplicationConfigurationInput) { const value = ApplicationConfigurationValues.create(input); assert.ok(value.isSuccess); return value.data; } function create(input: ApplicationConfigurationInput) { const root = ApplicationConfiguration.create(company, values(input), actor, date); assert.ok(root.isSuccess); return root.data; } test("aplicaciones: contrato estricto, versión opcional y solo instalada", () => { const { input } = fixture(); assert.equal( ApplicationConfigurationInputSchema.safeParse({ ...input, actorId: actor.toPrimitive() }) .success, false ); assert.equal( ApplicationConfigurationValues.create({ ...input, delivery: "web", version: "1" }).isSuccess, false ); assert.equal(values({ ...input, version: " ", notes: " " }).toPrimitive().version, null); assert.equal(values({ ...input, delivery: "web" }).toPrimitive().version, null); assert.equal( ApplicationConfigurationValues.create({ ...input, version: "a".repeat(101) }).isSuccess, false ); assert.equal( ApplicationConfigurationValues.create({ ...input, notes: "a".repeat(4001) }).isSuccess, false ); }); test("aplicaciones: UUID, idempotencia, versión auditada, destino fijo y retirada", () => { const { input } = fixture(); const root = create(input); assert.equal(root.id.toPrimitive()[14], "7"); assert.equal(root.history[0]?.id.toPrimitive()[14], "7"); const same = root.update(values({ ...input, notes: " " }), actor, date); assert.ok(same.isSuccess); assert.equal(same.data, root); const configured = root.update( values({ ...input, state: "configured", version: "128" }), actor, date ); assert.ok(configured.isSuccess); assert.equal(configured.data.history.length, 2); assert.equal(configured.data.history[0]?.values.version.getOrUndefined(), undefined); assert.equal( configured.data.update( values({ ...input, resourceId: UniqueID.generateNewID().toPrimitive() }), actor, date ).isSuccess, false ); const removal = configured.data.update( values({ ...configured.data.values.toPrimitive(), state: "removal_pending" }), actor, date ); assert.ok(removal.isSuccess); const removed = removal.data.update( values({ ...removal.data.values.toPrimitive(), state: "removed" }), actor, date ); assert.ok(removed.isSuccess); assert.equal( removed.data.update( values({ ...removed.data.values.toPrimitive(), notes: "Otra" }), actor, date ).isSuccess, false ); assert.equal( root.update(values({ ...input, state: "configured" }), actor, "2026-10-05T12:00:00.000Z") .isSuccess, false ); assert.equal( ApplicationConfiguration.create(company, values({ ...input, state: "removed" }), actor, date) .isSuccess, false ); }); test("aplicaciones: ámbito y clasificación explícita, duplicados abiertos y reapertura nueva", () => { const { input, context } = fixture(); const root = create(input); const duplicate = mapApplicationInput(input, { ...context, configurations: [root] }); assert.ok(!duplicate.isSuccess); assert.equal(duplicate.error.code, "APPLICATION_ALREADY_OPEN"); assert.equal( mapApplicationInput({ ...input, resourceId: host.toPrimitive() }, context).isSuccess, false ); assert.equal( mapApplicationInput(input, { ...context, resources: [{ id: resource, active: true, usage: "certificate" }], }).isSuccess, false ); assert.equal( mapApplicationInput(input, { ...context, resources: [{ id: resource, active: true, usage: null }], }).isSuccess, false ); const closed = root.update(values({ ...input, state: "never_configured" }), actor, date); assert.ok(closed.isSuccess); assert.ok(mapApplicationInput(input, { ...context, configurations: [closed.data] }).isSuccess); }); test("aplicaciones: inactividad conserva notas y permite retirada, impide reconfigurar", () => { const { input, context } = fixture(); const root = create({ ...input, state: "configured" }); const inactive = { ...context, configurations: [root], resources: [{ id: resource, active: false, usage: "application" as const }], }; assert.equal(buildApplicationResponse(root, inactive).needsReview, true); assert.ok( mapApplicationInput({ ...root.values.toPrimitive(), notes: "Retirar" }, inactive, root) .isSuccess ); assert.ok( mapApplicationInput({ ...root.values.toPrimitive(), state: "removal_pending" }, inactive, root) .isSuccess ); assert.equal( mapApplicationInput({ ...root.values.toPrimitive(), version: "2" }, inactive, root).isSuccess, false ); assert.equal( mapApplicationInput({ ...root.values.toPrimitive(), delivery: "web" }, inactive, root) .isSuccess, false ); assert.equal( mapApplicationInput(input, { ...context, people: [{ id: person, active: false }] }).isSuccess, false ); assert.equal( mapApplicationInput(input, { ...context, assets: [{ id: host, notRetired: false }] }).isSuccess, false ); }); test("aplicaciones: persistencia preserva historial y rechaza claves y orden incoherentes", () => { const { input } = fixture(); const root = create(input); const mapper = new ApplicationConfigurationPersistenceMapper(); const record = mapper.toPersistence(root); const rehydrated = mapper.toDomain(record.root, record.revisions); assert.ok(rehydrated.id.equals(root.id)); assert.ok(rehydrated.history[0]?.id.equals(root.history[0]?.id)); assert.throws(() => mapper.toDomain({ ...record.root, currentKey: null }, record.revisions)); assert.throws(() => mapper.toDomain( record.root, record.revisions.map((r) => ({ ...r, position: 1 })) ) ); assert.throws(() => mapper.toDomain( record.root, record.revisions.map((r) => ({ ...r, configurationId: company.toPrimitive() })) ) ); }); test("R2.5: global conserva perfil, delivery e historial; disponibilidad distinta de lectura histórica", () => { const { input, context } = fixture(); const globalContext: ApplicationContext = { ...context, globalResourceIds: new Set([resource.toPrimitive()]), }; assert.ok(mapApplicationInput(input, globalContext).isSuccess); const root = create({ ...input, version: "Perfil-123", state: "configured" }); const inactive: ApplicationContext = { ...globalContext, resources: [{ id: resource, active: false, usage: "application" }], }; for (const state of ["pending", "configured", "removed"] as const) { assert.equal(mapApplicationInput({ ...input, state }, inactive).isSuccess, false); } assert.ok( mapApplicationInput( { ...root.values.toPrimitive(), notes: "Mantener trazabilidad" }, inactive, root ).isSuccess ); assert.ok( mapApplicationInput({ ...root.values.toPrimitive(), state: "removal_pending" }, inactive, root) .isSuccess ); assert.equal( mapApplicationInput( { ...root.values.toPrimitive(), delivery: "web", version: null }, inactive, root ).isSuccess, false ); assert.equal( mapApplicationInput({ ...root.values.toPrimitive(), version: "Perfil-124" }, inactive, root) .isSuccess, false ); const response = buildApplicationResponse(root, inactive); assert.equal(response.needsReview, true); assert.equal(response.values.profileId, input.profileId); assert.equal(response.values.resourceId, input.resourceId); assert.equal(response.values.delivery, "installed"); assert.equal(response.values.version, "Perfil-123"); assert.equal(response.history.length, 1); assert.equal(mapApplicationInput(input, { ...globalContext, profiles: [] }).isSuccess, false); // La disponibilidad operacional se comprueba además de la pertenencia del perfil y producto. const unavailable = mapApplicationInput(input, { ...globalContext, assets: [] }); assert.equal(unavailable.isSuccess, false); });