From c1be14bf530d7b57f41ba5c8bdc57e198f0729d8 Mon Sep 17 00:00:00 2001 From: Victor <70475442+vsolano9@users.noreply.github.com> Date: Sun, 23 Aug 2026 15:08:03 +0200 Subject: [PATCH] fix: reject invalid refund amounts Require refund amounts to be finite and greater than zero before automatic action. Escalate invalid values with a clear reason and cover valid, boundary, invalid, and missing-amount policy paths. --- lib/policy.ts | 9 +++++ package.json | 1 + test/policy.test.mjs | 91 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 test/policy.test.mjs diff --git a/lib/policy.ts b/lib/policy.ts index 19d2731..4a9a111 100644 --- a/lib/policy.ts +++ b/lib/policy.ts @@ -50,6 +50,15 @@ export function decide(triage: Triage, payload: InboundPayload): Decision { case "refund": { const amount = triage.refundAmount ?? null; + if (amount != null && (!Number.isFinite(amount) || amount <= 0)) { + return { + status: "escalated", + proposedAction: "Confirm a valid refund amount, then approve", + actionTaken: null, + reason: "refund amount must be finite and greater than zero", + draftReply: triage.draftReply, + }; + } if (amount != null && amount <= refundAutoLimit) { return { status: "resolved", diff --git a/package.json b/package.json index fe8ea0c..ec26458 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "dev": "next dev", "build": "next build", "start": "next start", + "test": "node --test test/*.test.mjs", "lint": "next lint", "deploy": "NODE_OPTIONS=--no-deprecation opennextjs-cloudflare build && NODE_OPTIONS=--no-deprecation opennextjs-cloudflare deploy" }, diff --git a/test/policy.test.mjs b/test/policy.test.mjs new file mode 100644 index 0000000..f526248 --- /dev/null +++ b/test/policy.test.mjs @@ -0,0 +1,91 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import test, { after } from "node:test"; +import ts from "typescript"; + +const source = await readFile( + new URL("../lib/policy.ts", import.meta.url), + "utf8", +); +const compiled = ts.transpileModule(source, { + compilerOptions: { + module: ts.ModuleKind.ESNext, + target: ts.ScriptTarget.ES2022, + }, + fileName: "lib/policy.ts", + reportDiagnostics: true, +}); +assert.equal(compiled.diagnostics?.length ?? 0, 0); + +const moduleUrl = `data:text/javascript;base64,${Buffer.from( + compiled.outputText, +).toString("base64")}`; +const { decide } = await import(moduleUrl); + +const originalLimit = process.env.REFUND_AUTO_LIMIT; +process.env.REFUND_AUTO_LIMIT = "50"; +after(() => { + if (originalLimit === undefined) delete process.env.REFUND_AUTO_LIMIT; + else process.env.REFUND_AUTO_LIMIT = originalLimit; +}); + +function refundTriage(refundAmount) { + return { + category: "refund", + urgency: "normal", + sentiment: "neutral", + refundAmount, + draftReply: "We are reviewing your request.", + summary: "Refund request", + }; +} + +const payload = { + sender: "customer@example.com", + body: "Please refund this order.", +}; + +test("auto-resolves valid positive refunds at or below the configured limit", () => { + for (const amount of [0.01, 20, 50]) { + const decision = decide(refundTriage(amount), payload); + assert.equal(decision.status, "resolved", `expected $${amount} to resolve`); + assert.match(decision.reason, /<= auto-limit/); + } +}); + +test("escalates valid refunds above the configured limit", () => { + const decision = decide(refundTriage(50.01), payload); + assert.equal(decision.status, "escalated"); + assert.equal(decision.actionTaken, null); + assert.equal(decision.reason, "refund $50.01 exceeds auto-limit $50"); +}); + +test("escalates non-positive and non-finite refund amounts as invalid", () => { + const invalidAmounts = [ + 0, + -1, + Number.NaN, + Number.POSITIVE_INFINITY, + Number.NEGATIVE_INFINITY, + ]; + for (const amount of invalidAmounts) { + const decision = decide(refundTriage(amount), payload); + assert.equal(decision.status, "escalated", `expected ${amount} to escalate`); + assert.equal(decision.actionTaken, null); + assert.equal( + decision.proposedAction, + "Confirm a valid refund amount, then approve", + ); + assert.equal( + decision.reason, + "refund amount must be finite and greater than zero", + ); + } +}); + +test("preserves the missing-amount escalation", () => { + const decision = decide(refundTriage(undefined), payload); + assert.equal(decision.status, "escalated"); + assert.equal(decision.proposedAction, "Confirm refund amount, then approve"); + assert.equal(decision.reason, "refund amount not stated"); +});