import assert from "node:assert/strict"; import { once } from "node:events"; import { createServer } from "node:http"; import { test } from "node:test"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import express from "express"; import { InventoryResponseSchema, ResourceResponseSchema } from "../../common/inventory"; import { Resource } from "../domain/resource.aggregate"; import { ResourceCatalogError } from "../domain/resource-definition"; import { scopeFixture } from "../domain/resource-scope.fixture"; import { ResourceValues } from "../domain/value-objects/resource-values.vo"; import { registerResourceRoutes } from "../infrastructure/http/resource.routes"; import type { IResourceRepository, InventoryContext } from "./resource.repository"; import { ResourceUseCases } from "./resource.use-cases"; import { ResourceInventoryService } from "./resource-inventory.service"; function inventoryFixture() { const fixture = scopeFixture(); let resources = [fixture.local, fixture.global, fixture.foreign]; const context = (): InventoryContext => ({ catalog: fixture.catalog, resources, references: [], actorNames: new Map([[fixture.actor.toPrimitive(), "Técnico de pruebas"]]), }); const repository: IResourceRepository = { get: async () => Result.ok(context()), save: async (_company, action) => { const changed = action(context()); if (!changed.isSuccess) { return changed; } resources = [...resources.filter((item) => !item.id.equals(changed.data.id)), changed.data]; return Result.ok({ resource: changed.data, actorNames: context().actorNames }); }, remove: async () => Result.fail(new ResourceCatalogError("RESOURCE_NOT_FOUND")), }; return { ...fixture, repository, useCases: new ResourceUseCases(repository), setResources: (next: Resource[]) => { resources = next; }, }; } test("lectores locales: excluyen global y otra compañía incluso si el repositorio los entrega; stockKey no depende del nombre", async () => { const { repository, useCases, company, local, global, foreign, actor } = inventoryFixture(); const inventory = await new ResourceInventoryService(repository).list(company); assert.deepEqual( inventory.map((item) => item.id.toPrimitive()), [local.id.toPrimitive()] ); assert.equal(inventory[0]?.usage, "application"); const read = await useCases.get(company.toPrimitive()); assert.ok(read.isSuccess); assert.deepEqual(read.data.resources, [local]); for (const resource of [global, foreign]) { for (const operation of [ useCases.setActive( company.toPrimitive(), resource.id.toPrimitive(), false, actor.toPrimitive() ), useCases.save( company.toPrimitive(), resource.id.toPrimitive(), resource.values.toPrimitive(), actor.toPrimitive() ), ]) { const result = await operation; assert.ok(!result.isSuccess); assert.equal(result.error.code, "RESOURCE_NOT_FOUND"); } } }); test("HTTP actual sin MariaDB: aislamiento con 15 compañías, 50 recursos locales, alta, edición, estado e historial", async () => { const { local, global, foreign, actor, company, useCases, setResources } = inventoryFixture(); const companies = [ company, foreign.requireCompanyId(), ...Array.from({ length: 13 }, () => UniqueID.generateNewID()), ]; const resources = [local, foreign]; for (const owner of [company, ...companies.slice(2)]) { const count = owner.equals(company) ? 49 : 1; for (let index = 0; index < count; index += 1) { const values = ResourceValues.create( { ...local.values.toPrimitive(), name: `Aplicación de pruebas con tildes ${index + 1}`, active: index % 2 === 0, }, local.specification ); assert.ok(values.isSuccess); const created = Resource.create( owner, local.specification, values.data, actor, "2026-10-24T10:00:00.000Z" ); assert.ok(created.isSuccess); resources.push(created.data); } } setResources([...resources, global]); const app = express(); app.use(express.json()); registerResourceRoutes(app, useCases, (request, _response, next) => { Object.assign(request, { session: { userId: actor.toPrimitive() } }); next(); }); const server = createServer(app); server.listen(0, "127.0.0.1"); await once(server, "listening"); const address = server.address(); assert.ok(address && typeof address !== "string"); const base = `http://127.0.0.1:${address.port}/api/resources`; const request = (path: string, body?: unknown) => fetch(`${base}/${path}`, { method: body === undefined ? "GET" : "POST", ...(body === undefined ? {} : { headers: { "content-type": "application/json" }, body: JSON.stringify(body) }), signal: AbortSignal.timeout(5000), }); try { const response = await request(company.toPrimitive()); const listed = InventoryResponseSchema.parse(await response.json()); assert.equal(response.status, 200); assert.equal(listed.resources.length, 50); assert.ok( listed.resources.every( (item) => item.companyId === company.toPrimitive() && !Object.hasOwn(item, "scope") ) ); for (const owner of companies.slice(1)) { const response = await request(owner.toPrimitive()); const listed = InventoryResponseSchema.parse(await response.json()); assert.equal(listed.resources.length, 1); assert.equal(listed.resources[0]?.companyId, owner.toPrimitive()); } for (const resource of [global, foreign]) { assert.equal( ( await request(`${company.toPrimitive()}/${resource.id.toPrimitive()}/status`, { active: false, }) ).status, 404 ); assert.equal( ( await request(`${company.toPrimitive()}/${resource.id.toPrimitive()}/update`, { values: resource.values.toPrimitive(), }) ).status, 404 ); } for (const extra of [ { scope: "global", companyId: null }, { scope: "company", companyId: company.toPrimitive() }, ]) { assert.equal( (await request(company.toPrimitive(), { values: local.values.toPrimitive(), ...extra })) .status, 400 ); assert.equal( ( await request(company.toPrimitive(), { values: { ...local.values.toPrimitive(), ...extra }, }) ).status, 400 ); } const create = await request(company.toPrimitive(), { values: { ...local.values.toPrimitive(), name: "Aplicación nueva" }, }); const created = ResourceResponseSchema.parse(await create.json()).resource; assert.equal(create.status, 201); assert.equal(created.companyId, company.toPrimitive()); assert.equal(created.history.length, 1); const edit = await request(`${company.toPrimitive()}/${created.id}/update`, { values: { ...created.values, notes: "Edición local" }, }); const edited = ResourceResponseSchema.parse(await edit.json()).resource; assert.equal(edit.status, 200); assert.equal(edited.id, created.id); assert.equal(edited.history.length, 2); for (const active of [false, false, true]) { const status = await request(`${company.toPrimitive()}/${created.id}/status`, { active }); const changed = ResourceResponseSchema.parse(await status.json()).resource; assert.equal(status.status, 200); assert.equal(changed.values.active, active); assert.equal(changed.history.length, active ? 4 : 3); assert.deepEqual(changed.history[0], created.history[0]); } } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())) ); } });