import assert from "node:assert/strict"; import { AccessResponseSchema, MailboxesResponseSchema } from "@metis/companies/common"; import type { ResourceDTO } from "@metis/resources/common"; import { InventoryResponseSchema, ResourceResponseSchema } from "@metis/resources/common"; import { createResourceModule } from "@metis/resources/module"; import { QueryTypes, type Sequelize } from "sequelize"; import { z } from "zod/v4"; type Request = (path: string, body?: unknown) => Promise; export async function verifyResourceMigration( request: Request, companyId: string, otherId: string, database: Sequelize ) { const sources = await database.query<{ id: string; kind: string }>( "SELECT id,'mailbox' AS kind FROM mailboxes WHERE companyId=:companyId UNION ALL SELECT i.id,'access' AS kind FROM access_items i JOIN access_workspaces w ON w.id=i.workspaceId WHERE w.companyId=:companyId AND i.kind='resource'", { replacements: { companyId }, type: QueryTypes.SELECT } ); const originalMail = MailboxesResponseSchema.parse( await (await request(`/mailboxes/${companyId}`)).json() ); const originalAccess = AccessResponseSchema.parse( await (await request(`/access/${companyId}`)).json() ); const result = await request(`/resources/${companyId}`); assert.equal(result.status, 200); const inventory = InventoryResponseSchema.parse(await result.json()); for (const source of sources) { const resource = inventory.resources.find((item) => item.id === source.id); assert.ok(resource); assert.equal(resource.values.integration?.kind, source.kind); verifySourceValues(resource, originalMail, originalAccess); const table = source.kind === "mailbox" ? "mailbox_revisions" : "access_revisions"; const revisions = await database.query<{ id: string; actorId: string; recordedAt: string }>( `SELECT id,actorId,recordedAt FROM ${table} WHERE itemId=:id`, { replacements: { id: source.id }, type: QueryTypes.SELECT } ); assert.equal(resource.history.length, revisions.length); for (const old of revisions) { const current: ResourceDTO["history"][number] | undefined = resource.history.find( (item) => item.id === old.id ); assert.ok(current); assert.equal(current.actorId, old.actorId); assert.equal(current.recordedAt, old.recordedAt); } } const module = await createResourceModule(database, (_request, _response, next) => next()); await module.migrateLegacy(); const again = InventoryResponseSchema.parse( await (await request(`/resources/${companyId}`)).json() ); assert.deepEqual( again.resources.map((item) => [item.id, item.history.map((row) => row.id)]).sort(), inventory.resources.map((item) => [item.id, item.history.map((row) => row.id)]).sort() ); const mailbox = inventory.resources.find((item) => item.values.integration?.kind === "mailbox"); assert.ok(mailbox && mailbox.values.integration?.kind === "mailbox"); const integration = mailbox.values.integration; const values = { ...mailbox.values, active: false, notes: "Revisión desde Recursos" }; const system = inventory.references?.find((item) => item.kind === "system"); assert.ok(system); const wrongReference = await request(`/resources/${companyId}/${mailbox.id}/update`, { values: { ...values, integration: { ...integration, accountId: system.id } }, }); assert.equal(wrongReference.status, 409); assert.equal( z.object({ code: z.string() }).parse(await wrongReference.json()).code, "REFERENCE_UNAVAILABLE" ); const invalidEmail = await request(`/resources/${companyId}/${mailbox.id}/update`, { values: { ...values, characteristics: { ...values.characteristics, [integration.addressFieldId]: "correo-invalido", }, }, }); assert.equal(invalidEmail.status, 409); assert.equal( z.object({ code: z.string() }).parse(await invalidEmail.json()).code, "INVALID_EMAIL" ); const duplicate = await request(`/resources/${companyId}`, { values: { ...mailbox.values, name: "Otro nombre", characteristics: { ...mailbox.values.characteristics, [integration.addressFieldId]: String( mailbox.values.characteristics[integration.addressFieldId] ).toUpperCase(), }, }, }); assert.equal(duplicate.status, 409); assert.equal(z.object({ code: z.string() }).parse(await duplicate.json()).code, "NAME_EXISTS"); const changed = await request(`/resources/${companyId}/${mailbox.id}/update`, { values }); assert.equal(changed.status, 200); const updated = ResourceResponseSchema.parse(await changed.json()).resource; assert.equal(updated.history.length, mailbox.history.length + 1); assert.equal(updated.id, mailbox.id); const boxes = MailboxesResponseSchema.parse( await (await request(`/mailboxes/${companyId}`)).json() ); assert.equal(boxes.mailboxes.find((item) => item.id === mailbox.id)?.values.active, false); assert.equal( boxes.mailboxes.find((item) => item.id === mailbox.id)?.values.accountId, integration.accountId ); assert.equal( (await request(`/resources/${otherId}/${mailbox.id}/update`, { values })).status, 404 ); assert.equal( ( await request(`/mailboxes/${companyId}/${mailbox.id}/update`, { values: { address: mailbox.values.characteristics[integration.addressFieldId], kind: integration.mailboxKind, context: null, accountId: integration.accountId, active: true, notes: null, }, }) ).status, 409 ); const access = inventory.resources.find((item) => item.values.integration?.kind === "access"); assert.ok(access && access.values.integration?.kind === "access"); const referenceField = access.values.integration.referenceFieldId; const nextValues = { ...access.values, characteristics: { ...access.values.characteristics, [referenceField]: "\\\\SERVER\\Revision" }, }; const saved = await request(`/resources/${companyId}/${access.id}/update`, { values: nextValues, }); assert.equal(saved.status, 200); const snapshot = AccessResponseSchema.parse(await (await request(`/access/${companyId}`)).json()); const projected = snapshot.items.find((item) => item.id === access.id); assert.ok(projected && projected.values.kind === "resource"); assert.equal(projected.values.reference, nextValues.characteristics[referenceField]); const oldWrite = await request(`/access/${companyId}/${access.id}/update`, { values: projected.values, }); assert.equal(oldWrite.status, 409); assert.equal(z.object({ code: z.string() }).parse(await oldWrite.json()).code, "RESOURCE_MOVED"); } function verifySourceValues( resource: ResourceDTO, originalMail: z.infer, originalAccess: z.infer ) { const source = { id: resource.id }; const integration = resource.values.integration; if (integration?.kind === "mailbox") { const sourceValues = originalMail.mailboxes.find((item) => item.id === source.id)?.values; assert.ok(sourceValues); assert.equal(resource.values.characteristics[integration.addressFieldId], sourceValues.address); assert.equal( resource.values.characteristics[integration.contextFieldId] ?? null, sourceValues.context ); assert.equal(integration.mailboxKind, sourceValues.kind); assert.equal(integration.accountId, sourceValues.accountId); assert.equal(resource.values.active, sourceValues.active); assert.equal(resource.values.notes, sourceValues.notes ?? ""); } else if (integration?.kind === "access") { const sourceValues = originalAccess.items.find((item) => item.id === source.id)?.values; assert.ok(sourceValues?.kind === "resource"); assert.equal(resource.values.name, sourceValues.name); assert.equal( resource.values.characteristics[integration.referenceFieldId] ?? null, sourceValues.reference ); assert.equal(integration.resourceTypeId, sourceValues.resourceTypeId); assert.equal(integration.systemId, sourceValues.systemId); assert.equal(integration.environmentId, sourceValues.environmentId); assert.equal(resource.values.active, sourceValues.active); assert.equal(resource.values.notes, sourceValues.notes ?? ""); } }