fix(quota,i18n): close B26 audit gap on DELETE plan + pt-BR translation

Two gaps found in second-pass code review of Group B:

1. B26 violation: DELETE /api/quota/plans/[connectionId] did not emit
   logAuditEvent. Per master plan B26, every plan mutation must audit.
   Now emits quota.plan.updated with metadata.reverted=true to mark the
   revert-to-auto/catalog semantic. Test integration extended with
   assertion that audit event is present after DELETE.

2. pt-BR / pt locales had "costsSection": "Costs" (English label) instead
   of the Portuguese "Custos". Other section labels in the same block are
   left in English intentionally (analytics, monitoring) — they are
   project-wide untranslated terms; "Custos" is the established repo
   translation for the Costs section title.

Validation:
- npm run typecheck:core: clean
- tests/integration/quota-plans-crud.test.ts: 10/10 pass (includes new
  assertion on DELETE → audit event)
- eslint --no-warn-ignored on touched files: clean
This commit is contained in:
diegosouzapw
2026-05-28 16:03:30 -03:00
parent f2306942a3
commit cc22ca0088
4 changed files with 30 additions and 5 deletions

View File

@@ -5,7 +5,7 @@
*
* Auth: requireManagementAuth
* Zod: PlanUpsertSchema from @/shared/schemas/quota (PUT only)
* Audit: quota.plan.updated on PUT (B26)
* Audit: quota.plan.updated on PUT and DELETE (B26 — DELETE reverts override to auto/catalog)
* Sanitization: all error responses via buildErrorBody (Hard Rule #12, B25)
*
* For PUT: provider is derived from the connectionId via getProviderConnectionById.
@@ -115,9 +115,21 @@ export async function DELETE(request: Request, { params }: RouteParams): Promise
try {
const { connectionId } = await params;
// deleteProviderPlan returns true if a row was deleted, false if not found
// We return 204 either way (idempotent delete)
const existing = getProviderPlan(connectionId);
const provider = existing?.provider ?? (await resolveProvider(connectionId));
deleteProviderPlan(connectionId);
const ctx = getAuditRequestContext(request);
logAuditEvent({
action: "quota.plan.updated",
target: connectionId,
metadata: { provider, source: "auto", reverted: true },
ipAddress: ctx.ipAddress ?? undefined,
requestId: ctx.requestId,
});
return new Response(null, { status: 204 });
} catch (err) {
const message = err instanceof Error ? err.message : "Failed to delete plan";

View File

@@ -819,7 +819,7 @@
"agentsAiSection": "Agents & AI",
"cacheContextSection": "Cache & Context",
"analyticsSection": "Analytics",
"costsSection": "Costs",
"costsSection": "Custos",
"monitoringSection": "Monitoring",
"auditSecuritySection": "Audit & Security",
"devtoolsSection": "Dev Tools",

View File

@@ -819,7 +819,7 @@
"agentsAiSection": "Agents & AI",
"cacheContextSection": "Cache & Context",
"analyticsSection": "Analytics",
"costsSection": "Costs",
"costsSection": "Custos",
"monitoringSection": "Monitoring",
"auditSecuritySection": "Audit & Security",
"devtoolsSection": "Dev Tools",

View File

@@ -238,6 +238,19 @@ test("DELETE /api/quota/plans/[connectionId] clears override → 204; GET revert
// After delete, should fall back to catalog or empty (source=auto or manual-empty)
// For a connectionId with no catalog match, source=manual + dimensions=[]
assert.ok(["auto", "manual"].includes(body.plan.source));
// B26: DELETE must emit logAuditEvent with quota.plan.updated + metadata.reverted=true
const logs = compliance.getAuditLog({ action: "quota.plan.updated", limit: 20 });
const events = Array.isArray(logs) ? logs : [];
const deleteEvt = events.find(
(e) =>
typeof e === "object" &&
e !== null &&
(e as Record<string, unknown>).action === "quota.plan.updated" &&
(e as Record<string, unknown>).target === connectionId &&
(e as { metadata?: { reverted?: boolean } }).metadata?.reverted === true
);
assert.ok(deleteEvt, "quota.plan.updated audit event (reverted=true) must be present after DELETE");
});
test("DELETE /api/quota/plans/[connectionId] is idempotent → 204 even when not found", async () => {