import assert from "node:assert/strict"; import { test } from "node:test"; import { UniqueID } from "@repo/rdx-ddd"; import type { SoftwareContext } from "../application/software-installation.repository"; import { mapSoftwareInput } from "../application/software-installation-input.mapper"; import { buildSoftwareResponse } from "../application/software-installation-response.builder"; import { SoftwareInstallationPersistenceMapper } from "../infrastructure/persistence/software-installation-persistence.mapper"; import { SoftwareInstallation } from "./software-installation.aggregate"; import type { SoftwareInstallationInput } from "./software-installation-values"; import { SoftwareInstallationInputSchema } from "./software-installation-values"; import { SoftwareInstallationValues } from "./value-objects/software-installation-values.vo"; const company = UniqueID.generateNewID(); const asset = UniqueID.generateNewID(); const resource = UniqueID.generateNewID(); const actor = UniqueID.generateNewID(); const date = "2026-10-12T10:00:00.000Z"; const input: SoftwareInstallationInput = { assetId: asset.toPrimitive(), resourceId: resource.toPrimitive(), licenseResourceId: null, version: "24H2", state: "pending", notes: null, }; const context: SoftwareContext = { globalResourceIds: new Set(), configurations: [], assets: [{ id: asset, notRetired: true }], resources: [{ id: resource, active: true, expiresOn: null, usage: "os" }], actors: new Map([[actor.toPrimitive(), "David"]]), }; function values(data = input) { const result = SoftwareInstallationValues.create(data); assert.ok(result.isSuccess); return result.data; } function installation() { const result = SoftwareInstallation.create(company, values(), actor, date); assert.ok(result.isSuccess); return result.data; } test("instalaciones: pertenecen al equipo, admiten SO/aplicaciones y rechazan recursos ajenos y destinos variables", () => { assert.ok(mapSoftwareInput(input, context).isSuccess); assert.ok( mapSoftwareInput(input, { ...context, resources: [{ id: resource, active: true, expiresOn: null, usage: "application" }], }).isSuccess ); assert.ok(!mapSoftwareInput(input, { ...context, assets: [] }).isSuccess); assert.ok( !mapSoftwareInput(input, { ...context, resources: [{ id: resource, active: true, expiresOn: null, usage: "certificate" }], }).isSuccess ); assert.equal( SoftwareInstallationInputSchema.safeParse({ ...input, profileId: actor.toPrimitive() }).success, false ); const previous = installation(); assert.ok(!mapSoftwareInput(input, { ...context, configurations: [previous] }).isSuccess); assert.ok( mapSoftwareInput(input, { ...context, configurations: [previous] }, previous).isSuccess ); assert.ok( !previous.update(values({ ...input, assetId: actor.toPrimitive() }), actor, date).isSuccess ); }); test("instalaciones: versiones, idempotencia, retirada, revisión y persistencia de historial", () => { const previous = installation(); assert.ok(previous.id.toPrimitive().charAt(14) === "7"); const same = previous.update(values(), actor, date); assert.ok(same.isSuccess); assert.equal(same.data, previous); const configured = previous.update( values({ ...input, state: "configured", version: "25H2" }), actor, date ); assert.ok(configured.isSuccess); const unavailable = { ...context, assets: [{ id: asset, notRetired: false }] }; assert.ok(buildSoftwareResponse(configured.data, unavailable).needsReview); assert.ok( mapSoftwareInput( { ...configured.data.values.toPrimitive(), notes: "Revisar equipo" }, unavailable, configured.data ).isSuccess ); assert.ok(!mapSoftwareInput(input, unavailable).isSuccess); assert.ok(!configured.data.update(values({ ...input, state: "removed" }), actor, date).isSuccess); const retiring = configured.data.update( values({ ...input, state: "removal_pending" }), actor, date ); assert.ok(retiring.isSuccess); const removed = retiring.data.update(values({ ...input, state: "removed" }), actor, date); assert.ok(removed.isSuccess); assert.ok(!removed.data.update(values(), actor, date).isSuccess); assert.ok(mapSoftwareInput(input, { ...context, configurations: [removed.data] }).isSuccess); const mapper = new SoftwareInstallationPersistenceMapper(); const rows = mapper.toPersistence(removed.data); const restored = mapper.toDomain(rows.root, rows.revisions); assert.equal(restored.history.length, 4); assert.ok(restored.values.equals(removed.data.values)); assert.throws(() => mapper.toDomain({ ...rows.root, currentKey: "incorrect" }, rows.revisions)); assert.throws(() => mapper.toDomain( rows.root, rows.revisions.map((row) => ({ ...row, position: 9 })) ) ); }); test("licencia del software: opcional, misma compañía y tipo, vigencia, desvinculación e historial idempotente", () => { const license = UniqueID.generateNewID(); const licensedInput = { ...input, licenseResourceId: license.toPrimitive() }; const licenseItem = { id: license, active: true, usage: "license" as const, expiresOn: null }; const licensedContext = { ...context, resources: [...context.resources, licenseItem] }; assert.ok(mapSoftwareInput(licensedInput, licensedContext).isSuccess); assert.ok(!mapSoftwareInput(licensedInput, context).isSuccess); assert.ok( !mapSoftwareInput({ ...input, licenseResourceId: resource.toPrimitive() }, context).isSuccess ); assert.equal( SoftwareInstallationInputSchema.safeParse({ ...input, licenseResourceId: "invalid" }).success, false ); const previous = installation(); const linked = previous.update(values(licensedInput), actor, date); assert.ok(linked.isSuccess); assert.equal(linked.data.history.length, 2); assert.equal(linked.data.history[0]?.values.toPrimitive().licenseResourceId, null); assert.equal( linked.data.history[1]?.values.toPrimitive().licenseResourceId, license.toPrimitive() ); const same = linked.data.update(values(licensedInput), actor, date); assert.ok(same.isSuccess); assert.equal(same.data, linked.data); for (const unavailable of [ { ...licenseItem, active: false }, { ...licenseItem, expiresOn: "2000-01-01" }, ]) { const unavailableContext = { ...context, resources: [...context.resources, unavailable] }; assert.ok(!mapSoftwareInput(licensedInput, unavailableContext).isSuccess); assert.ok(buildSoftwareResponse(linked.data, unavailableContext).needsReview); assert.ok( mapSoftwareInput( { ...licensedInput, notes: "Revisar contrato" }, unavailableContext, linked.data ).isSuccess ); assert.ok(mapSoftwareInput(input, unavailableContext, linked.data).isSuccess); assert.ok( mapSoftwareInput( input, { ...unavailableContext, assets: [{ id: asset, notRetired: false }] }, linked.data ).isSuccess ); assert.ok( !mapSoftwareInput( { ...licensedInput, state: "removal_pending" }, unavailableContext, previous ).isSuccess ); assert.ok( mapSoftwareInput( { ...licensedInput, state: "never_configured" }, unavailableContext, linked.data ).isSuccess ); } const detached = linked.data.update(values(input), actor, date); assert.ok(detached.isSuccess); assert.equal(detached.data.history.length, 3); const mapper = new SoftwareInstallationPersistenceMapper(); const stored = mapper.toPersistence(detached.data); const restored = mapper.toDomain(stored.root, stored.revisions); assert.equal(restored.values.toPrimitive().licenseResourceId, null); assert.equal(restored.history[1]?.values.toPrimitive().licenseResourceId, license.toPrimitive()); assert.throws(() => mapper.toDomain({ ...stored.root, licenseResourceId: license.toPrimitive() }, stored.revisions) ); }); test("R2.4: global activo e inactivo separan alta, reconfiguración y recuperación histórica", () => { const globalContext: SoftwareContext = { ...context, globalResourceIds: new Set([resource.toPrimitive()]), resources: [{ id: resource, usage: "application", active: true, expiresOn: null }], }; const previous = installation(); assert.ok(mapSoftwareInput(input, globalContext).isSuccess); const inactive: SoftwareContext = { ...globalContext, resources: [{ id: resource, usage: "application", active: false, expiresOn: null }], }; for (const state of ["pending", "configured", "removed"] as const) { const rejected = mapSoftwareInput({ ...input, state }, inactive); assert.ok(!rejected.isSuccess && rejected.error.code === "SOFTWARE_REFERENCE_UNAVAILABLE"); } assert.ok( mapSoftwareInput({ ...input, notes: "Revisar producto desactivado" }, inactive, previous) .isSuccess ); const changedVersion = mapSoftwareInput( { ...input, version: "Nueva versión" }, inactive, previous ); assert.ok( !changedVersion.isSuccess && changedVersion.error.code === "SOFTWARE_REFERENCE_UNAVAILABLE" ); assert.equal(buildSoftwareResponse(previous, inactive).needsReview, true); assert.equal( buildSoftwareResponse(previous, inactive).history[0]?.values.resourceId, resource.toPrimitive() ); assert.equal(buildSoftwareResponse(previous, inactive).history[0]?.values.version, "24H2"); const globalLicense = mapSoftwareInput( { ...input, licenseResourceId: resource.toPrimitive() }, globalContext ); assert.ok( !globalLicense.isSuccess && globalLicense.error.code === "SOFTWARE_REFERENCE_NOT_FOUND" ); });