Files
2026-11-14 09:56:45 +00:00

84 lines
3.9 KiB
TypeScript

// A refusal of the Ledger service reaches the person as the kit's sentence for it, in their language.
// Before the kit helper, the module mapped refusals itself and compared a `BusinessErrorCode` with Ledger's
// reasons (`unbalanced`, `insufficient-funds`), which it never is: an unbalanced posting was answered
// "reload the ledger record". Generated HTTP client with a synthetic binding; no gateway/live acceptance.
import type { RequestBusinessBinding } from "@conusai-kit/business"
import { describeBusinessRefusal } from "@conusai-kit/business/refusals"
import { describe, expect, it } from "vitest"
import { sdkLedgerService } from "../adapters/service.js"
import { LEDGER_ERRORS } from "../contract/index.js"
import type { LedgerService } from "../core/ports.js"
import { en } from "../i18n/en.js"
import { lt } from "../i18n/lt.js"
import { ledgerFailure } from "../ui/errors.js"
import { ledgerLabels } from "../ui/labels.js"
const id = "acc_00000000-0000-4000-8000-000000000001"
function refusing(reason: string, status: number) {
const binding = {
orgSlug: "verified-org",
client: (factory: Parameters<RequestBusinessBinding["client"]>[0]) => {
const client = factory({ baseUrl: "https://ledger.example.test", auth: "synthetic", throwOnError: false })
client.setConfig({
fetch: async () =>
new Response(
JSON.stringify({
type: `urn:conusai:ledger:${reason}`,
status,
code: `LEDGER_${reason.replaceAll("-", "_").toUpperCase()}`,
title: "Ledger request refused",
}),
{ status, headers: { "content-type": "application/json" } },
),
})
return client
},
} as RequestBusinessBinding
return sdkLedgerService(binding)
}
const terms: Parameters<LedgerService["record"]>[0] = {
effectiveAt: "2026-11-03T00:00:00Z",
description: "Synthetic",
status: "posted",
entries: [
{ accountId: id, direction: "debit", amountMinor: "100" },
{ accountId: id, direction: "credit", amountMinor: "99" },
],
}
async function refusalOf(reason: string, status: number): Promise<unknown> {
try {
await refusing(reason, status).record(terms, "stable-key")
} catch (error) {
return error
}
throw new Error("expected a refusal")
}
describe("Ledger refusals through the kit", () => {
it("tells the person an unbalanced posting does not balance, in English and Lithuanian", async () => {
const error = await refusalOf("unbalanced", 409)
expect(error).toHaveProperty("problem.code", "business_unbalanced")
expect(error).toHaveProperty("problem.status", 409)
expect(describeBusinessRefusal(error, "en")).toBe("Debits and credits do not balance.")
expect(describeBusinessRefusal(error, "lt")).toBe("Debetas ir kreditas nesutampa.")
})
it("names insufficient funds and a transaction's state as such", async () => {
expect(await refusalOf("insufficient-funds", 409)).toHaveProperty("problem.code", "business_insufficient_funds")
expect(await refusalOf("transaction-state", 409)).toHaveProperty("problem.code", "business_transaction_state")
})
it("shows a form the kit's sentence, a conflict in this module's words, and the module's own drift fence", async () => {
const labelsOf = (messages: Record<string, string>, locale: "en" | "lt") =>
ledgerLabels((key) => messages[key] ?? key, locale)
const english = labelsOf(en, "en")
const lithuanian = labelsOf(lt, "lt")
expect(ledgerFailure(await refusalOf("unbalanced", 409), english)).toBe("Debits and credits do not balance.")
expect(ledgerFailure(await refusalOf("unbalanced", 409), lithuanian)).toBe("Debetas ir kreditas nesutampa.")
expect(ledgerFailure(await refusalOf("revision-conflict", 409), english)).toBe(en.revisionConflict)
expect(ledgerFailure(await refusalOf("idempotency-conflict", 409), lithuanian)).toBe(lt.idempotencyConflict)
expect(ledgerFailure({ problem: { code: LEDGER_ERRORS.drift } }, english)).toBe(en.drift)
})
})