143 lines
4.2 KiB
TypeScript
143 lines
4.2 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { AuditChangeModel, AuditEventModel } from "@/infrastructure/audit/sequelize";
|
|
import {
|
|
buildCompanyHeaders,
|
|
createTestAccount,
|
|
createTestCompany,
|
|
createTestMembership,
|
|
loginByApi,
|
|
} from "@/testing/http/http-test-fixtures";
|
|
import { createHttpTestRuntime } from "@/testing/http/http-test-runtime";
|
|
|
|
function event(id: string, scopeId: string | null) {
|
|
return {
|
|
id,
|
|
kind: "event",
|
|
occurred_at: new Date("2026-08-25T10:00:00.000Z"),
|
|
source_type: "application",
|
|
source_id: "factuges-erp",
|
|
operation_id: `op-${id}`,
|
|
correlation_id: `cor-${id}`,
|
|
origin: "http",
|
|
actor_type: "user",
|
|
actor_id: "actor",
|
|
actor_label: "Actor",
|
|
scope_type: scopeId ? "company" : null,
|
|
scope_id: scopeId,
|
|
scope_label: null,
|
|
action: "customer.updated",
|
|
target_type: "customer",
|
|
target_id: id,
|
|
target_label: id,
|
|
};
|
|
}
|
|
|
|
test("Audit HTTP aplica tenant A/B y valida el DTO estricto", async (t) => {
|
|
const runtime = await createHttpTestRuntime();
|
|
t.after(async () => runtime.stop());
|
|
const account = await createTestAccount(runtime.database, {
|
|
email: "audit-reader@test.local",
|
|
password: "Password123!",
|
|
});
|
|
const companyA = await createTestCompany(runtime.database, { legalName: "Company A" });
|
|
const companyB = await createTestCompany(runtime.database, { legalName: "Company B" });
|
|
await createTestMembership(runtime.database, { accountId: account.id, companyId: companyA.id });
|
|
await AuditEventModel.bulkCreate([
|
|
event("entry-a", companyA.id),
|
|
event("entry-b", companyB.id),
|
|
event("entry-global", null),
|
|
]);
|
|
await AuditChangeModel.create({
|
|
audit_event_id: "entry-a",
|
|
position: 0,
|
|
path: "name",
|
|
kind: "change",
|
|
before_value: "Before",
|
|
after_value: "After",
|
|
});
|
|
const login = await loginByApi({
|
|
request: runtime.request,
|
|
email: account.email,
|
|
password: account.password,
|
|
});
|
|
assert.equal(login.status, 200);
|
|
const token = (login.body as { access_token: string }).access_token;
|
|
const headers = buildCompanyHeaders({ accessToken: token, companyId: companyA.id });
|
|
const list = await runtime.request("/api/v1/audit", { headers });
|
|
assert.equal(list.status, 200);
|
|
const body = list.body as {
|
|
items: { id: string }[];
|
|
page: number;
|
|
per_page: number;
|
|
total_pages: number;
|
|
total_items: number;
|
|
};
|
|
assert.deepEqual(
|
|
body.items.map((item) => item.id),
|
|
["entry-a"]
|
|
);
|
|
assert.equal(body.page, 0);
|
|
assert.equal(body.per_page, 50);
|
|
assert.deepEqual(Object.keys(body).sort(), [
|
|
"items",
|
|
"page",
|
|
"per_page",
|
|
"total_items",
|
|
"total_pages",
|
|
]);
|
|
assert.deepEqual(Object.keys(body.items[0] ?? {}).sort(), [
|
|
"action",
|
|
"actor",
|
|
"changes_count",
|
|
"id",
|
|
"kind",
|
|
"occurred_at",
|
|
"operation",
|
|
"scope",
|
|
"source",
|
|
"target",
|
|
]);
|
|
const detailA = await runtime.request("/api/v1/audit/entry-a", { headers });
|
|
assert.equal(detailA.status, 200);
|
|
const detailBody = detailA.body as { changes: Record<string, unknown>[] };
|
|
assert.deepEqual(Object.keys(detailBody).sort(), [
|
|
"action",
|
|
"actor",
|
|
"changes",
|
|
"changes_count",
|
|
"id",
|
|
"kind",
|
|
"occurred_at",
|
|
"operation",
|
|
"scope",
|
|
"source",
|
|
"target",
|
|
]);
|
|
assert.deepEqual(Object.keys(detailBody.changes[0] ?? {}).sort(), [
|
|
"after",
|
|
"before",
|
|
"kind",
|
|
"path",
|
|
"position",
|
|
]);
|
|
const requestedB = await runtime.request(`/api/v1/audit?scopeId=${companyB.id}`, { headers });
|
|
assert.equal(requestedB.status, 200);
|
|
assert.equal((requestedB.body as { items: unknown[] }).items.length, 0);
|
|
assert.equal((await runtime.request("/api/v1/audit/entry-b", { headers })).status, 404);
|
|
assert.equal((await runtime.request("/api/v1/audit/entry-global", { headers })).status, 404);
|
|
assert.equal((await runtime.request("/api/v1/audit/entry-missing", { headers })).status, 404);
|
|
for (const query of [
|
|
"unknownField=x",
|
|
"pageNumber=-1",
|
|
"pageSize=101",
|
|
"kind=invalid",
|
|
"orderBy=invalid",
|
|
"order=invalid",
|
|
"from=invalid",
|
|
"to=invalid",
|
|
])
|
|
assert.equal((await runtime.request(`/api/v1/audit?${query}`, { headers })).status, 422);
|
|
});
|