FactuGES_ERP/apps/server/src/testing/http/scoped-persistence.integration.test.ts

258 lines
9.6 KiB
TypeScript

import assert from "node:assert/strict";
import { randomUUID } from "node:crypto";
import test from "node:test";
import { Name, UniqueID } from "@repo/rdx-ddd";
import { Maybe } from "@repo/rdx-utils";
import { RecurringProforma } from "../../../../../modules/customer-invoices/src/api/domain";
import { RecurringProformaModel } from "../../../../../modules/customer-invoices/src/api/infrastructure/common";
import { SequelizeRecurringProformaRepository } from "../../../../../modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma.repository";
import {
SequelizeCustomerDomainMapper,
SequelizeCustomerSummaryMapper,
} from "../../../../../modules/customers/src/api/infrastructure/persistence/sequelize/mappers";
import { CustomerModel } from "../../../../../modules/customers/src/api/infrastructure/persistence/sequelize/models/sequelize-customer.model";
import { CustomerRepository } from "../../../../../modules/customers/src/api/infrastructure/persistence/sequelize/repositories/customer.repository";
import type { DocumentSeriesCreateProps } from "../../../../../modules/document-series/src/api/domain";
import {
DocumentSeries,
DocumentSeriesCode,
DocumentSeriesNumber,
DocumentType,
} from "../../../../../modules/document-series/src/api/domain";
import { DocumentSeriesModel } from "../../../../../modules/document-series/src/api/infrastructure/common";
import { SequelizeDocumentSeriesDomainMapper } from "../../../../../modules/document-series/src/api/infrastructure/persistence/sequelize/mappers";
import { SequelizeDocumentSeriesRepository } from "../../../../../modules/document-series/src/api/infrastructure/persistence/sequelize/repositories/document-series.repository";
import {
buildCompanyHeaders,
createTestAccount,
createTestCompany,
createTestMembership,
loginByApi,
} from "./http-test-fixtures";
import { createHttpTestRuntime } from "./http-test-runtime";
test("ORM real: Customer update, Series save y Recurring save preservan tenant y transacción", async (t) => {
const runtime = await createHttpTestRuntime();
t.after(() => runtime.stop());
const a = await createTestCompany(runtime.database, { legalName: "A" });
const b = await createTestCompany(runtime.database, { legalName: "B" });
const companyId = UniqueID.create(a.id).data;
const otherCompanyId = UniqueID.create(b.id).data;
const account = await createTestAccount(runtime.database, {
email: `${randomUUID()}@example.test`,
password: "TestPassword123!",
});
await createTestMembership(runtime.database, { accountId: account.id, companyId: a.id });
const login = await loginByApi({
request: runtime.request,
email: account.email,
password: account.password,
});
assert.ok(login.body && typeof login.body === "object" && "access_token" in login.body);
const id = UniqueID.generateNewID();
const created = await runtime.request("/api/v1/customers", {
method: "POST",
headers: {
...buildCompanyHeaders({ accessToken: String(login.body.access_token), companyId: a.id }),
"content-type": "application/json",
},
body: JSON.stringify({
id: id.toPrimitive(),
is_company: true,
name: "Original",
address: { country: "ES" },
uses_retention: false,
language_code: "es",
currency_code: "EUR",
}),
});
assert.equal(created.status, 201);
const customers = new CustomerRepository(
new SequelizeCustomerDomainMapper(),
new SequelizeCustomerSummaryMapper(),
runtime.database
);
const loaded = await customers.getByIdInCompany(companyId, id);
assert.ok(loaded.isSuccess);
// El agregado válido de A nunca puede escribirse con contexto explícito B.
assert.ok(loaded.data.update({ name: Name.create("Allowed customer").data }).isSuccess);
const wrong = await runtime.database.transaction((transaction) =>
customers.update(otherCompanyId, loaded.data, transaction)
);
assert.ok(wrong.isFailure);
const originalRow = await CustomerModel.findByPk(id.toPrimitive());
assert.ok(originalRow);
const foreignAggregate = new SequelizeCustomerDomainMapper().mapToDomain(
CustomerModel.build({ ...originalRow.get(), company_id: b.id })
);
assert.ok(foreignAggregate.isSuccess);
const deniedAtSql = await runtime.database.transaction((transaction) =>
customers.update(otherCompanyId, foreignAggregate.data, transaction)
);
assert.ok(deniedAtSql.isFailure);
assert.ok(deniedAtSql.error.name.includes("NotFound"));
assert.equal((await CustomerModel.findByPk(id.toPrimitive()))?.name, "Original");
const missing = await customers.getByIdInCompany(otherCompanyId, id);
assert.ok(missing.isFailure);
assert.equal((await CustomerModel.findByPk(id.toPrimitive()))?.company_id, a.id);
await assert.rejects(
runtime.database.transaction(async (transaction) => {
const updated = await customers.update(companyId, loaded.data, transaction);
assert.ok(updated.isSuccess);
assert.equal(
(await CustomerModel.findByPk(id.toPrimitive(), { transaction }))?.name,
"Allowed customer"
);
// El mismo update sin cambios debe funcionar también con driver real.
assert.ok((await customers.update(companyId, loaded.data, transaction)).isSuccess);
throw new Error("forced rollback");
}),
/forced rollback/
);
assert.equal((await CustomerModel.findByPk(id.toPrimitive()))?.name, "Original");
assert.ok(
(
await runtime.database.transaction((transaction) =>
customers.update(companyId, loaded.data, transaction)
)
).isSuccess
);
assert.equal((await CustomerModel.findByPk(id.toPrimitive()))?.name, "Allowed customer");
const seriesProps: DocumentSeriesCreateProps = {
companyId,
branchId: Maybe.none(),
documentType: DocumentType.proforma(),
code: DocumentSeriesCode.create("PF").data,
name: Name.create("Original series").data,
description: Maybe.none(),
prefix: Maybe.none(),
suffix: Maybe.none(),
nextNumber: DocumentSeriesNumber.create(1).data,
padding: 4,
validFrom: Maybe.none(),
validTo: Maybe.none(),
isDefault: false,
isActive: true,
hasAssignedNumbers: false,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: Maybe.none(),
};
const series = DocumentSeries.create(seriesProps).data;
const seriesRepo = new SequelizeDocumentSeriesRepository(
new SequelizeDocumentSeriesDomainMapper(),
runtime.database
);
assert.ok(
(await runtime.database.transaction((transaction) => seriesRepo.save(series, transaction)))
.isSuccess
);
const foreignSeries = DocumentSeries.create(
{ ...seriesProps, companyId: otherCompanyId, name: Name.create("Foreign change").data },
series.id
).data;
assert.ok(
(
await runtime.database.transaction((transaction) =>
seriesRepo.save(foreignSeries, transaction)
)
).isFailure
);
assert.equal((await DocumentSeriesModel.findByPk(series.id.toPrimitive()))?.company_id, a.id);
assert.equal(
(await DocumentSeriesModel.findByPk(series.id.toPrimitive()))?.name,
"Original series"
);
const changedSeries = DocumentSeries.create(
{ ...seriesProps, name: Name.create("Allowed series").data },
series.id
).data;
await assert.rejects(
runtime.database.transaction(async (transaction) => {
assert.ok((await seriesRepo.save(changedSeries, transaction)).isSuccess);
assert.equal(
(await DocumentSeriesModel.findByPk(series.id.toPrimitive(), { transaction }))?.name,
"Allowed series"
);
throw new Error("forced rollback");
}),
/forced rollback/
);
assert.equal(
(await DocumentSeriesModel.findByPk(series.id.toPrimitive()))?.name,
"Original series"
);
assert.ok(
(
await runtime.database.transaction((transaction) =>
seriesRepo.save(changedSeries, transaction)
)
).isSuccess
);
assert.equal(
(await DocumentSeriesModel.findByPk(series.id.toPrimitive()))?.name,
"Allowed series"
);
const recurring = RecurringProforma.create({ companyId, name: "Original recurring" }).data;
const recurringRepo = new SequelizeRecurringProformaRepository();
assert.ok(
(
await runtime.database.transaction((transaction) =>
recurringRepo.save(recurring, transaction)
)
).isSuccess
);
const foreignRecurring = RecurringProforma.create(
{ companyId: otherCompanyId, name: "Foreign recurring" },
recurring.id
).data;
assert.ok(
(
await runtime.database.transaction((transaction) =>
recurringRepo.save(foreignRecurring, transaction)
)
).isFailure
);
assert.equal(
(await RecurringProformaModel.findByPk(recurring.id.toPrimitive()))?.company_id,
a.id
);
const changedRecurring = RecurringProforma.create(
{ companyId, name: "Allowed recurring" },
recurring.id
).data;
await assert.rejects(
runtime.database.transaction(async (transaction) => {
assert.ok((await recurringRepo.save(changedRecurring, transaction)).isSuccess);
assert.equal(
(await RecurringProformaModel.findByPk(recurring.id.toPrimitive(), { transaction }))?.name,
"Allowed recurring"
);
throw new Error("forced rollback");
}),
/forced rollback/
);
assert.equal(
(await RecurringProformaModel.findByPk(recurring.id.toPrimitive()))?.name,
"Original recurring"
);
assert.ok(
(
await runtime.database.transaction((transaction) =>
recurringRepo.save(changedRecurring, transaction)
)
).isSuccess
);
assert.equal(
(await RecurringProformaModel.findByPk(recurring.id.toPrimitive()))?.name,
"Allowed recurring"
);
});