@@ -0,0 +1,151 @@
|
||||
import type { IsolationTenant } from "@conusai-kit/testing"
|
||||
import { describe, expect, it, vi } from "vitest"
|
||||
import { simulatedBusinessAdmission } from "../../../test/helpers/business-admission.js"
|
||||
import type { LedgerService } from "../core/ports.js"
|
||||
import { ledgerApp } from "./app.js"
|
||||
import { ledgerIsolationFixtures } from "./isolation.js"
|
||||
|
||||
const fixtureInput = (name: string, tenant: IsolationTenant) => {
|
||||
const fixture = ledgerIsolationFixtures[name]
|
||||
if (!fixture) throw new Error("missing isolation fixture")
|
||||
return fixture.input(tenant)
|
||||
}
|
||||
const resultAccount = {
|
||||
accountId: "acc_00000000-0000-4000-8000-000000000001",
|
||||
code: "SYNTHETIC",
|
||||
name: "Operational",
|
||||
currency: "EUR",
|
||||
type: "asset",
|
||||
normalBalance: "debit",
|
||||
allowNegative: false,
|
||||
status: "active",
|
||||
revision: "1",
|
||||
} as const
|
||||
const resultTransaction = {
|
||||
transactionId: "txn_00000000-0000-4000-8000-000000000001",
|
||||
revision: "1",
|
||||
effectiveAt: "2026-11-03T00:00:00Z",
|
||||
description: "Synthetic",
|
||||
reference: null,
|
||||
reverses: null,
|
||||
status: "pending",
|
||||
entries: [],
|
||||
} as const
|
||||
const fake = () =>
|
||||
({
|
||||
accounts: vi.fn(async () => ({ rows: [] })),
|
||||
verify: vi.fn(async () => ({ checkedAccounts: 0, drift: [] })),
|
||||
createAccount: vi.fn(async () => resultAccount),
|
||||
updateAccount: vi.fn(async () => resultAccount),
|
||||
record: vi.fn(async () => ({ ...resultTransaction, entries: [] })),
|
||||
transition: vi.fn(async () => ({ ...resultTransaction, entries: [] })),
|
||||
}) as unknown as LedgerService
|
||||
|
||||
describe("Ledger actual caller with synthetic admission/port", () => {
|
||||
it("refuses all 13 procedures with wrong-tenant slugs before service access", async () => {
|
||||
const s = fake()
|
||||
const app = ledgerApp(s)
|
||||
const a = await app.tenant("alpha")
|
||||
const b = await app.tenant("beta")
|
||||
const tenant = { orgSlug: b.orgSlug } as IsolationTenant
|
||||
expect(Object.keys(ledgerIsolationFixtures)).toHaveLength(13)
|
||||
for (const [name, fixture] of Object.entries(ledgerIsolationFixtures)) {
|
||||
await expect(a.call.callByName(name, fixture.input(tenant))).rejects.toHaveProperty("problem.status", 404)
|
||||
}
|
||||
expect(s.accounts).not.toHaveBeenCalled()
|
||||
})
|
||||
it("allows viewer reads, refuses viewer writes and separates member writer from account/reversal admin", async () => {
|
||||
const app = ledgerApp(fake())
|
||||
const t = await app.tenant("alpha")
|
||||
const viewer = await app.member(t, "viewer@alpha.test", "viewer")
|
||||
const member = await app.member(t, "writer@alpha.test", "member")
|
||||
expect(await viewer.callByName("ledger.accounts.list", { org: t.orgSlug })).toEqual({ rows: [] })
|
||||
const tenant = { orgSlug: t.orgSlug } as IsolationTenant
|
||||
for (const name of [
|
||||
"ledger.accounts.create",
|
||||
"ledger.accounts.update",
|
||||
"ledger.transactions.record",
|
||||
"ledger.transactions.post",
|
||||
"ledger.transactions.void",
|
||||
"ledger.transactions.reverse",
|
||||
]) {
|
||||
await expect(viewer.callByName(name, fixtureInput(name, tenant))).rejects.toHaveProperty("problem.status", 404)
|
||||
}
|
||||
for (const name of ["ledger.accounts.create", "ledger.accounts.update", "ledger.transactions.reverse"])
|
||||
await expect(member.callByName(name, fixtureInput(name, tenant))).rejects.toHaveProperty("problem.status", 404)
|
||||
})
|
||||
it("refuses unregistered default-off module and missing readiness/binding", async () => {
|
||||
for (const [enabled, ready] of [
|
||||
[false, true],
|
||||
[true, false],
|
||||
]) {
|
||||
const s = fake()
|
||||
const app = ledgerApp(s, enabled, ready)
|
||||
const t = await app.tenant("alpha")
|
||||
await expect(t.call.callByName("ledger.accounts.list", { org: t.orgSlug })).rejects.toThrow()
|
||||
expect(s.accounts).not.toHaveBeenCalled()
|
||||
}
|
||||
})
|
||||
it("refuses missing approved service scopes and Convex before any adapter call", async () => {
|
||||
for (const readiness of [
|
||||
{
|
||||
...simulatedBusinessAdmission,
|
||||
observe: async () => ({
|
||||
...(await simulatedBusinessAdmission.observe({} as never, {} as never)),
|
||||
authorized: false,
|
||||
}),
|
||||
},
|
||||
{ ...simulatedBusinessAdmission, profile: "convex" as const },
|
||||
]) {
|
||||
const s = fake()
|
||||
const app = ledgerApp(s, true, readiness)
|
||||
const t = await app.tenant("alpha")
|
||||
await expect(t.call.callByName("ledger.accounts.list", { org: t.orgSlug })).rejects.toHaveProperty(
|
||||
"problem.code",
|
||||
readiness.profile === "convex" ? "service_profile_unsupported" : "service_permission_missing",
|
||||
)
|
||||
expect(s.accounts).not.toHaveBeenCalled()
|
||||
}
|
||||
})
|
||||
it("allows member financial writes and owner account/reversal administration through the real caller", async () => {
|
||||
const service = fake()
|
||||
const app = ledgerApp(service)
|
||||
const t = await app.tenant("alpha")
|
||||
const member = await app.member(t, "writer@alpha.test", "member")
|
||||
const tenant = { orgSlug: t.orgSlug } as IsolationTenant
|
||||
for (const name of ["ledger.transactions.record", "ledger.transactions.post", "ledger.transactions.void"]) {
|
||||
expect(await member.callByName(name, fixtureInput(name, tenant))).toHaveProperty(
|
||||
"transactionId",
|
||||
resultTransaction.transactionId,
|
||||
)
|
||||
}
|
||||
for (const name of ["ledger.accounts.create", "ledger.accounts.update"]) {
|
||||
expect(await t.call.callByName(name, fixtureInput(name, tenant))).toHaveProperty(
|
||||
"accountId",
|
||||
resultAccount.accountId,
|
||||
)
|
||||
}
|
||||
expect(
|
||||
await t.call.callByName("ledger.transactions.reverse", fixtureInput("ledger.transactions.reverse", tenant)),
|
||||
).toHaveProperty("transactionId", resultTransaction.transactionId)
|
||||
})
|
||||
it("requires explicit financial confirmation and refuses extra body-selected authority", async () => {
|
||||
const service = fake()
|
||||
const app = ledgerApp(service)
|
||||
const t = await app.tenant("alpha")
|
||||
const input = fixtureInput("ledger.transactions.record", { orgSlug: t.orgSlug } as IsolationTenant) as Record<
|
||||
string,
|
||||
unknown
|
||||
>
|
||||
for (const invalid of [
|
||||
{ ...input, confirmed: false },
|
||||
{ ...input, appTenantId: "forged", platformOrg: "beta" },
|
||||
]) {
|
||||
await expect(t.call.callByName("ledger.transactions.record", invalid)).rejects.toHaveProperty(
|
||||
"problem.status",
|
||||
422,
|
||||
)
|
||||
}
|
||||
expect(service.record).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user