c6df2ab8f5
contracts/openapi.yaml (OpenAPI 3.1, E-1..E-33, error shape and the exhaustive error code enum, device bearer scheme), contracts/schema/ server-v1.sql (SPEC §14.1 verbatim), seven test-vector files per §14.5.5, 80 examples (every JSON body of §14.3.4, every error code, sync pull/push, every FCM push type), and tools/contracts-check.py (stdlib-only reference of §9.1, §9.3, §9.4 and §14.5.4 plus shape checks; the M1 gate). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
142 lines
7.0 KiB
JSON
142 lines
7.0 KiB
JSON
{
|
|
"format": 1,
|
|
"function": "compute_balances",
|
|
"cases": [
|
|
{
|
|
"name": "empty: no accounts, no rows",
|
|
"input": { "user_ids": ["a", "b"], "account_ids": [], "expenses": [], "settlements": [] },
|
|
"expected": { "ok": { "per_account": {}, "global": { "a": 0, "b": 0 } } }
|
|
},
|
|
{
|
|
"name": "one account, (1001, payer a, 50/50) and (100, payer b, a 6000 bp): a +440, b -440",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["x"],
|
|
"expenses": [
|
|
{ "account_id": "x", "amount_ore": 1001, "payer_user_id": "a", "shares": { "a": 501, "b": 500 } },
|
|
{ "account_id": "x", "amount_ore": 100, "payer_user_id": "b", "shares": { "a": 60, "b": 40 } }
|
|
],
|
|
"settlements": []
|
|
},
|
|
"expected": { "ok": { "per_account": { "x": { "a": 440, "b": -440 } }, "global": { "a": 440, "b": -440 } } }
|
|
},
|
|
{
|
|
"name": "same account plus settlement b -> a 440: all zero",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["x"],
|
|
"expenses": [
|
|
{ "account_id": "x", "amount_ore": 1001, "payer_user_id": "a", "shares": { "a": 501, "b": 500 } },
|
|
{ "account_id": "x", "amount_ore": 100, "payer_user_id": "b", "shares": { "a": 60, "b": 40 } }
|
|
],
|
|
"settlements": [ { "from_user_id": "b", "to_user_id": "a", "allocations": { "x": 440 } } ]
|
|
},
|
|
"expected": { "ok": { "per_account": { "x": { "a": 0, "b": 0 } }, "global": { "a": 0, "b": 0 } } }
|
|
},
|
|
{
|
|
"name": "two accounts with opposite signs: global is the sum",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["x", "y"],
|
|
"expenses": [
|
|
{ "account_id": "x", "amount_ore": 2000, "payer_user_id": "a", "shares": { "a": 1000, "b": 1000 } },
|
|
{ "account_id": "y", "amount_ore": 600, "payer_user_id": "b", "shares": { "a": 300, "b": 300 } }
|
|
],
|
|
"settlements": []
|
|
},
|
|
"expected": { "ok": { "per_account": { "x": { "a": 1000, "b": -1000 }, "y": { "a": -300, "b": 300 } }, "global": { "a": 700, "b": -700 } } }
|
|
},
|
|
{
|
|
"name": "an account in account_ids with no rows has zeros",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["x", "y"],
|
|
"expenses": [ { "account_id": "x", "amount_ore": 1001, "payer_user_id": "a", "shares": { "a": 501, "b": 500 } } ],
|
|
"settlements": []
|
|
},
|
|
"expected": { "ok": { "per_account": { "x": { "a": 500, "b": -500 }, "y": { "a": 0, "b": 0 } }, "global": { "a": 500, "b": -500 } } }
|
|
},
|
|
{
|
|
"name": "archived accounts are included: the archived account is in account_ids and counts toward global",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["live", "arch"],
|
|
"expenses": [
|
|
{ "account_id": "live", "amount_ore": 1000, "payer_user_id": "a", "shares": { "a": 500, "b": 500 } },
|
|
{ "account_id": "arch", "amount_ore": 5000, "payer_user_id": "b", "shares": { "a": 2500, "b": 2500 } }
|
|
],
|
|
"settlements": []
|
|
},
|
|
"expected": { "ok": { "per_account": { "live": { "a": 500, "b": -500 }, "arch": { "a": -2500, "b": 2500 } }, "global": { "a": -2000, "b": 2000 } } }
|
|
},
|
|
{
|
|
"name": "account ids referenced by rows but not in account_ids still count",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["x"],
|
|
"expenses": [ { "account_id": "z", "amount_ore": 100, "payer_user_id": "b", "shares": { "a": 100, "b": 0 } } ],
|
|
"settlements": [ { "from_user_id": "a", "to_user_id": "b", "allocations": { "w": 30 } } ]
|
|
},
|
|
"expected": { "ok": { "per_account": { "x": { "a": 0, "b": 0 }, "z": { "a": -100, "b": 100 }, "w": { "a": 30, "b": -30 } }, "global": { "a": -70, "b": 70 } } }
|
|
},
|
|
{
|
|
"name": "settlement with allocations to two accounts",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["x", "y"],
|
|
"expenses": [
|
|
{ "account_id": "x", "amount_ore": 10000, "payer_user_id": "a", "shares": { "a": 5000, "b": 5000 } },
|
|
{ "account_id": "y", "amount_ore": 3000, "payer_user_id": "a", "shares": { "a": 0, "b": 3000 } }
|
|
],
|
|
"settlements": [ { "from_user_id": "b", "to_user_id": "a", "allocations": { "x": 5000, "y": 1000 } } ]
|
|
},
|
|
"expected": { "ok": { "per_account": { "x": { "a": 0, "b": 0 }, "y": { "a": 2000, "b": -2000 } }, "global": { "a": 2000, "b": -2000 } } }
|
|
},
|
|
{
|
|
"name": "over-settling flips the account's sign",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["x"],
|
|
"expenses": [ { "account_id": "x", "amount_ore": 1000, "payer_user_id": "a", "shares": { "a": 500, "b": 500 } } ],
|
|
"settlements": [ { "from_user_id": "b", "to_user_id": "a", "allocations": { "x": 800 } } ]
|
|
},
|
|
"expected": { "ok": { "per_account": { "x": { "a": -300, "b": 300 } }, "global": { "a": -300, "b": 300 } } }
|
|
},
|
|
{
|
|
"name": "partial settlement in the other direction",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["x"],
|
|
"expenses": [ { "account_id": "x", "amount_ore": 1000, "payer_user_id": "b", "shares": { "a": 500, "b": 500 } } ],
|
|
"settlements": [ { "from_user_id": "a", "to_user_id": "b", "allocations": { "x": 200 } } ]
|
|
},
|
|
"expected": { "ok": { "per_account": { "x": { "a": -300, "b": 300 } }, "global": { "a": -300, "b": 300 } } }
|
|
},
|
|
{
|
|
"name": "expense fully borne by the payer leaves the balance unchanged",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["x"],
|
|
"expenses": [
|
|
{ "account_id": "x", "amount_ore": 1001, "payer_user_id": "a", "shares": { "a": 501, "b": 500 } },
|
|
{ "account_id": "x", "amount_ore": 7000, "payer_user_id": "a", "shares": { "a": 7000, "b": 0 } }
|
|
],
|
|
"settlements": []
|
|
},
|
|
"expected": { "ok": { "per_account": { "x": { "a": 500, "b": -500 } }, "global": { "a": 500, "b": -500 } } }
|
|
},
|
|
{
|
|
"name": "tombstoned rows excluded: callers pass only non-deleted rows (§14.5.2); the account's deleted expense and deleted settlement are absent from the input",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["x"],
|
|
"expenses": [ { "account_id": "x", "amount_ore": 2000, "payer_user_id": "b", "shares": { "a": 1000, "b": 1000 } } ],
|
|
"settlements": []
|
|
},
|
|
"expected": { "ok": { "per_account": { "x": { "a": -1000, "b": 1000 } }, "global": { "a": -1000, "b": 1000 } } }
|
|
},
|
|
{
|
|
"name": "maximum amounts stay exact in 64-bit integers",
|
|
"input": {
|
|
"user_ids": ["a", "b"], "account_ids": ["x"],
|
|
"expenses": [
|
|
{ "account_id": "x", "amount_ore": 1000000000, "payer_user_id": "a", "shares": { "a": 0, "b": 1000000000 } },
|
|
{ "account_id": "x", "amount_ore": 1000000000, "payer_user_id": "a", "shares": { "a": 0, "b": 1000000000 } },
|
|
{ "account_id": "x", "amount_ore": 1000000000, "payer_user_id": "a", "shares": { "a": 0, "b": 1000000000 } }
|
|
],
|
|
"settlements": []
|
|
},
|
|
"expected": { "ok": { "per_account": { "x": { "a": 3000000000, "b": -3000000000 } }, "global": { "a": 3000000000, "b": -3000000000 } } }
|
|
}
|
|
]
|
|
}
|