238 lines
9.2 KiB
TypeScript
238 lines
9.2 KiB
TypeScript
|
|
import assert from "node:assert/strict";
|
||
|
|
import { randomUUID } from "node:crypto";
|
||
|
|
import test from "node:test";
|
||
|
|
|
||
|
|
import { SequelizeTransactionManager } from "@erp/core/api";
|
||
|
|
import { Name, UniqueID } from "@repo/rdx-ddd";
|
||
|
|
import { Result } from "@repo/rdx-utils";
|
||
|
|
|
||
|
|
import { AuditChangeModel, AuditEventModel } from "@/infrastructure/audit/sequelize";
|
||
|
|
|
||
|
|
import { PaymentMethodUpdater } from "../../../../../modules/catalogs/src/api/application/payment-methods/services/payment-method-updater";
|
||
|
|
import { SequelizePaymentMethodDomainMapper } from "../../../../../modules/catalogs/src/api/infrastructure/payment-methods/persistence/sequelize/mappers/sequelize-payment-method-domain.mapper";
|
||
|
|
import { SequelizePaymentMethodSummaryMapper } from "../../../../../modules/catalogs/src/api/infrastructure/payment-methods/persistence/sequelize/mappers/sequelize-payment-method-summary.mapper";
|
||
|
|
import { PaymentMethodModel } from "../../../../../modules/catalogs/src/api/infrastructure/payment-methods/persistence/sequelize/models/sequelize-payment-method.model";
|
||
|
|
import { SequelizePaymentMethodRepository } from "../../../../../modules/catalogs/src/api/infrastructure/payment-methods/persistence/sequelize/repositories/sequelize-payment-method.repository";
|
||
|
|
|
||
|
|
import {
|
||
|
|
buildCompanyHeaders,
|
||
|
|
createTestAccount,
|
||
|
|
createTestCompany,
|
||
|
|
createTestMembership,
|
||
|
|
loginByApi,
|
||
|
|
} from "./http-test-fixtures";
|
||
|
|
import { createHttpTestRuntime } from "./http-test-runtime";
|
||
|
|
|
||
|
|
test("payment methods registra create y update con el contexto HTTP en MariaDB", async (t) => {
|
||
|
|
const runtime = await createHttpTestRuntime();
|
||
|
|
t.after(async () => runtime.stop());
|
||
|
|
|
||
|
|
const account = await createTestAccount(runtime.database, {
|
||
|
|
email: `payment-method-audit-${randomUUID()}@example.test`,
|
||
|
|
password: "Password123!",
|
||
|
|
});
|
||
|
|
const company = await createTestCompany(runtime.database, {
|
||
|
|
legalName: "Payment method audit company",
|
||
|
|
});
|
||
|
|
await createTestMembership(runtime.database, {
|
||
|
|
accountId: account.id,
|
||
|
|
companyId: company.id,
|
||
|
|
});
|
||
|
|
const loginResponse = await loginByApi({
|
||
|
|
request: runtime.request,
|
||
|
|
email: account.email,
|
||
|
|
password: account.password,
|
||
|
|
});
|
||
|
|
assert.equal(loginResponse.status, 200);
|
||
|
|
const accessToken = (loginResponse.body as { access_token: string }).access_token;
|
||
|
|
const paymentMethodId = randomUUID();
|
||
|
|
|
||
|
|
const createResponse = await runtime.request("/api/v1/catalogs/payment-methods", {
|
||
|
|
method: "POST",
|
||
|
|
headers: {
|
||
|
|
...buildCompanyHeaders({ accessToken, companyId: company.id }),
|
||
|
|
"content-type": "application/json",
|
||
|
|
"x-correlation-id": "payment-method-create-correlation",
|
||
|
|
},
|
||
|
|
body: JSON.stringify({
|
||
|
|
id: paymentMethodId,
|
||
|
|
name: "Cash",
|
||
|
|
description: "Counter payment",
|
||
|
|
is_active: true,
|
||
|
|
}),
|
||
|
|
});
|
||
|
|
assert.equal(createResponse.status, 201);
|
||
|
|
|
||
|
|
const createdAudit = await AuditEventModel.findOne({
|
||
|
|
where: { action: "payment_method.created", target_id: paymentMethodId },
|
||
|
|
});
|
||
|
|
assert.ok(createdAudit);
|
||
|
|
assert.equal(createdAudit.source_type, "application");
|
||
|
|
assert.equal(createdAudit.source_id, "uecko-erp");
|
||
|
|
assert.equal(createdAudit.origin, "http");
|
||
|
|
assert.equal(createdAudit.operation_id, createResponse.headers.get("x-request-id"));
|
||
|
|
assert.equal(createdAudit.correlation_id, "payment-method-create-correlation");
|
||
|
|
assert.equal(createdAudit.actor_type, "user");
|
||
|
|
assert.equal(createdAudit.actor_id, account.id);
|
||
|
|
assert.equal(createdAudit.scope_type, "company");
|
||
|
|
assert.equal(createdAudit.scope_id, company.id);
|
||
|
|
assert.equal(createdAudit.target_type, "payment_method");
|
||
|
|
assert.equal(createdAudit.target_label, "Cash");
|
||
|
|
|
||
|
|
const updateResponse = await runtime.request(
|
||
|
|
`/api/v1/catalogs/payment-methods/${paymentMethodId}`,
|
||
|
|
{
|
||
|
|
method: "PUT",
|
||
|
|
headers: {
|
||
|
|
...buildCompanyHeaders({ accessToken, companyId: company.id }),
|
||
|
|
"content-type": "application/json",
|
||
|
|
"x-correlation-id": "payment-method-update-correlation",
|
||
|
|
},
|
||
|
|
body: JSON.stringify({ name: "Cash updated" }),
|
||
|
|
}
|
||
|
|
);
|
||
|
|
assert.equal(updateResponse.status, 200);
|
||
|
|
|
||
|
|
const updatedAudit = await AuditEventModel.findOne({
|
||
|
|
where: { action: "payment_method.updated", target_id: paymentMethodId },
|
||
|
|
});
|
||
|
|
assert.ok(updatedAudit);
|
||
|
|
assert.equal(updatedAudit.target_label, "Cash updated");
|
||
|
|
assert.equal(updatedAudit.actor_id, account.id);
|
||
|
|
assert.equal(updatedAudit.scope_id, company.id);
|
||
|
|
const changes = await AuditChangeModel.findAll({
|
||
|
|
where: { audit_event_id: updatedAudit.id },
|
||
|
|
order: [["position", "ASC"]],
|
||
|
|
});
|
||
|
|
assert.deepEqual(
|
||
|
|
changes.map((change) => ({
|
||
|
|
path: change.path,
|
||
|
|
kind: change.kind,
|
||
|
|
before: change.before_value,
|
||
|
|
after: change.after_value,
|
||
|
|
})),
|
||
|
|
[{ path: "name", kind: "change", before: "Cash", after: "Cash updated" }]
|
||
|
|
);
|
||
|
|
|
||
|
|
const secondPaymentMethodId = randomUUID();
|
||
|
|
const secondCreateResponse = await runtime.request("/api/v1/catalogs/payment-methods", {
|
||
|
|
method: "POST",
|
||
|
|
headers: {
|
||
|
|
...buildCompanyHeaders({ accessToken, companyId: company.id }),
|
||
|
|
"content-type": "application/json",
|
||
|
|
},
|
||
|
|
body: JSON.stringify({
|
||
|
|
id: secondPaymentMethodId,
|
||
|
|
name: "Card",
|
||
|
|
is_active: true,
|
||
|
|
}),
|
||
|
|
});
|
||
|
|
assert.equal(secondCreateResponse.status, 201);
|
||
|
|
|
||
|
|
const firstDefaultResponse = await runtime.request(
|
||
|
|
`/api/v1/catalogs/payment-methods/${paymentMethodId}/default`,
|
||
|
|
{
|
||
|
|
method: "PATCH",
|
||
|
|
headers: buildCompanyHeaders({ accessToken, companyId: company.id }),
|
||
|
|
}
|
||
|
|
);
|
||
|
|
assert.equal(firstDefaultResponse.status, 200);
|
||
|
|
|
||
|
|
const defaultResponse = await runtime.request(
|
||
|
|
`/api/v1/catalogs/payment-methods/${secondPaymentMethodId}/default`,
|
||
|
|
{
|
||
|
|
method: "PATCH",
|
||
|
|
headers: {
|
||
|
|
...buildCompanyHeaders({ accessToken, companyId: company.id }),
|
||
|
|
"x-correlation-id": "payment-method-default-correlation",
|
||
|
|
},
|
||
|
|
}
|
||
|
|
);
|
||
|
|
assert.equal(defaultResponse.status, 200);
|
||
|
|
|
||
|
|
const defaultEvents = await AuditEventModel.findAll({
|
||
|
|
where: {
|
||
|
|
action: ["payment_method.default_unset", "payment_method.default_set"],
|
||
|
|
operation_id: defaultResponse.headers.get("x-request-id") ?? "",
|
||
|
|
},
|
||
|
|
order: [["action", "ASC"]],
|
||
|
|
});
|
||
|
|
assert.equal(defaultEvents.length, 2);
|
||
|
|
const unsetEvent = defaultEvents.find((entry) => entry.action === "payment_method.default_unset");
|
||
|
|
const setEvent = defaultEvents.find((entry) => entry.action === "payment_method.default_set");
|
||
|
|
assert.equal(unsetEvent?.target_id, paymentMethodId);
|
||
|
|
assert.equal(setEvent?.target_id, secondPaymentMethodId);
|
||
|
|
assert.equal(unsetEvent?.correlation_id, "payment-method-default-correlation");
|
||
|
|
assert.equal(setEvent?.correlation_id, "payment-method-default-correlation");
|
||
|
|
assert.equal(unsetEvent?.actor_id, account.id);
|
||
|
|
assert.equal(setEvent?.scope_id, company.id);
|
||
|
|
|
||
|
|
const [unsetChange] = await AuditChangeModel.findAll({
|
||
|
|
where: { audit_event_id: unsetEvent?.id ?? "" },
|
||
|
|
});
|
||
|
|
const [setChange] = await AuditChangeModel.findAll({
|
||
|
|
where: { audit_event_id: setEvent?.id ?? "" },
|
||
|
|
});
|
||
|
|
assert.deepEqual(
|
||
|
|
{ path: unsetChange?.path, before: unsetChange?.before_value, after: unsetChange?.after_value },
|
||
|
|
{ path: "isDefault", before: true, after: false }
|
||
|
|
);
|
||
|
|
assert.deepEqual(
|
||
|
|
{ path: setChange?.path, before: setChange?.before_value, after: setChange?.after_value },
|
||
|
|
{ path: "isDefault", before: false, after: true }
|
||
|
|
);
|
||
|
|
|
||
|
|
const originalName = "Cash updated";
|
||
|
|
const rollbackName = "Cash rollback attempt";
|
||
|
|
const repository = new SequelizePaymentMethodRepository(
|
||
|
|
new SequelizePaymentMethodDomainMapper(),
|
||
|
|
new SequelizePaymentMethodSummaryMapper(),
|
||
|
|
runtime.database
|
||
|
|
);
|
||
|
|
const updater = new PaymentMethodUpdater(repository, runtime.audit);
|
||
|
|
const transactionManager = new SequelizeTransactionManager(
|
||
|
|
runtime.database,
|
||
|
|
runtime.transactionContext
|
||
|
|
);
|
||
|
|
const paymentMethodIdResult = UniqueID.create(paymentMethodId);
|
||
|
|
const rollbackNameResult = Name.create(rollbackName);
|
||
|
|
assert.equal(paymentMethodIdResult.isSuccess, true);
|
||
|
|
assert.equal(rollbackNameResult.isSuccess, true);
|
||
|
|
const changesBeforeRollback = await AuditChangeModel.count();
|
||
|
|
|
||
|
|
await assert.rejects(
|
||
|
|
() =>
|
||
|
|
runtime.operationContext.run(
|
||
|
|
{
|
||
|
|
operationId: "payment-method-rollback-operation",
|
||
|
|
correlationId: "payment-method-rollback-correlation",
|
||
|
|
origin: "http",
|
||
|
|
actor: { type: "user", id: account.id, label: account.email },
|
||
|
|
scope: { type: "company", id: company.id, label: null },
|
||
|
|
},
|
||
|
|
() =>
|
||
|
|
transactionManager.complete(async (transaction) => {
|
||
|
|
const updateResult = await updater.update({
|
||
|
|
companyId: UniqueID.create(company.id).data,
|
||
|
|
id: paymentMethodIdResult.data,
|
||
|
|
patchProps: { name: rollbackNameResult.data },
|
||
|
|
transaction,
|
||
|
|
});
|
||
|
|
assert.equal(updateResult.isSuccess, true);
|
||
|
|
return Result.fail(new Error("forced payment method rollback"));
|
||
|
|
})
|
||
|
|
),
|
||
|
|
/forced payment method rollback/
|
||
|
|
);
|
||
|
|
|
||
|
|
const rolledBackPaymentMethod = await PaymentMethodModel.findByPk(paymentMethodId);
|
||
|
|
assert.equal(rolledBackPaymentMethod?.name, originalName);
|
||
|
|
assert.equal(
|
||
|
|
await AuditEventModel.count({
|
||
|
|
where: { operation_id: "payment-method-rollback-operation" },
|
||
|
|
}),
|
||
|
|
0
|
||
|
|
);
|
||
|
|
assert.equal(await AuditChangeModel.count(), changesBeforeRollback);
|
||
|
|
});
|