metis-desk/apps/server/src/resource-migration.integration.ts

189 lines
8.2 KiB
TypeScript

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<Response>;
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<typeof MailboxesResponseSchema>,
originalAccess: z.infer<typeof AccessResponseSchema>
) {
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 ?? "");
}
}