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>
169 lines
8.5 KiB
JSON
169 lines
8.5 KiB
JSON
{
|
|
"format": 1,
|
|
"function": "propose_allocation",
|
|
"cases": [
|
|
{
|
|
"name": "owing X (-30000, oldest owing) and Y (-20000), owed in Z (+10000): 40000 gives X 30000, Y 10000",
|
|
"input": {
|
|
"amount_ore": 40000,
|
|
"accounts": [
|
|
{ "account_id": "X", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": -30000 },
|
|
{ "account_id": "Y", "created_at": "2025-02-01T00:00:00.000Z", "archived": false, "payer_net_ore": -20000 },
|
|
{ "account_id": "Z", "created_at": "2024-12-01T00:00:00.000Z", "archived": false, "payer_net_ore": 10000 }
|
|
]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "X", "amount_ore": 30000 }, { "account_id": "Y", "amount_ore": 10000 } ], "excess_ore": 0, "over_settled_account_ids": [] } }
|
|
},
|
|
{
|
|
"name": "same accounts, 50000 = exactly the total debt: no excess",
|
|
"input": {
|
|
"amount_ore": 50000,
|
|
"accounts": [
|
|
{ "account_id": "X", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": -30000 },
|
|
{ "account_id": "Y", "created_at": "2025-02-01T00:00:00.000Z", "archived": false, "payer_net_ore": -20000 },
|
|
{ "account_id": "Z", "created_at": "2024-12-01T00:00:00.000Z", "archived": false, "payer_net_ore": 10000 }
|
|
]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "X", "amount_ore": 30000 }, { "account_id": "Y", "amount_ore": 20000 } ], "excess_ore": 0, "over_settled_account_ids": [] } }
|
|
},
|
|
{
|
|
"name": "same accounts, 60000: excess 10000 added to the last owing account Y, over-settled [Y]",
|
|
"input": {
|
|
"amount_ore": 60000,
|
|
"accounts": [
|
|
{ "account_id": "X", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": -30000 },
|
|
{ "account_id": "Y", "created_at": "2025-02-01T00:00:00.000Z", "archived": false, "payer_net_ore": -20000 },
|
|
{ "account_id": "Z", "created_at": "2024-12-01T00:00:00.000Z", "archived": false, "payer_net_ore": 10000 }
|
|
]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "X", "amount_ore": 30000 }, { "account_id": "Y", "amount_ore": 30000 } ], "excess_ore": 10000, "over_settled_account_ids": ["Y"] } }
|
|
},
|
|
{
|
|
"name": "same accounts, 20000 is less than the first debt: only X",
|
|
"input": {
|
|
"amount_ore": 20000,
|
|
"accounts": [
|
|
{ "account_id": "X", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": -30000 },
|
|
{ "account_id": "Y", "created_at": "2025-02-01T00:00:00.000Z", "archived": false, "payer_net_ore": -20000 },
|
|
{ "account_id": "Z", "created_at": "2024-12-01T00:00:00.000Z", "archived": false, "payer_net_ore": 10000 }
|
|
]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "X", "amount_ore": 20000 } ], "excess_ore": 0, "over_settled_account_ids": [] } }
|
|
},
|
|
{
|
|
"name": "input order does not matter: accounts listed newest first",
|
|
"input": {
|
|
"amount_ore": 40000,
|
|
"accounts": [
|
|
{ "account_id": "Y", "created_at": "2025-02-01T00:00:00.000Z", "archived": false, "payer_net_ore": -20000 },
|
|
{ "account_id": "Z", "created_at": "2024-12-01T00:00:00.000Z", "archived": false, "payer_net_ore": 10000 },
|
|
{ "account_id": "X", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": -30000 }
|
|
]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "X", "amount_ore": 30000 }, { "account_id": "Y", "amount_ore": 10000 } ], "excess_ore": 0, "over_settled_account_ids": [] } }
|
|
},
|
|
{
|
|
"name": "no owing accounts: 5000 with only Z (owed) goes to Z, over-settled [Z]",
|
|
"input": {
|
|
"amount_ore": 5000,
|
|
"accounts": [ { "account_id": "Z", "created_at": "2024-12-01T00:00:00.000Z", "archived": false, "payer_net_ore": 10000 } ]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "Z", "amount_ore": 5000 } ], "excess_ore": 5000, "over_settled_account_ids": ["Z"] } }
|
|
},
|
|
{
|
|
"name": "no owing accounts: a net of exactly 0 is not owing; target is the oldest non-archived account",
|
|
"input": {
|
|
"amount_ore": 700,
|
|
"accounts": [
|
|
{ "account_id": "A", "created_at": "2024-01-01T00:00:00.000Z", "archived": true, "payer_net_ore": 0 },
|
|
{ "account_id": "B", "created_at": "2024-06-01T00:00:00.000Z", "archived": false, "payer_net_ore": 500 },
|
|
{ "account_id": "C", "created_at": "2024-03-01T00:00:00.000Z", "archived": false, "payer_net_ore": 0 }
|
|
]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "C", "amount_ore": 700 } ], "excess_ore": 700, "over_settled_account_ids": ["C"] } }
|
|
},
|
|
{
|
|
"name": "all accounts archived and none owing: the oldest account",
|
|
"input": {
|
|
"amount_ore": 1200,
|
|
"accounts": [
|
|
{ "account_id": "P", "created_at": "2025-05-01T00:00:00.000Z", "archived": true, "payer_net_ore": 0 },
|
|
{ "account_id": "Q", "created_at": "2025-03-01T00:00:00.000Z", "archived": true, "payer_net_ore": 300 },
|
|
{ "account_id": "R", "created_at": "2025-04-01T00:00:00.000Z", "archived": true, "payer_net_ore": 0 }
|
|
]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "Q", "amount_ore": 1200 } ], "excess_ore": 1200, "over_settled_account_ids": ["Q"] } }
|
|
},
|
|
{
|
|
"name": "archived owing accounts are still settled (archived never affects money)",
|
|
"input": {
|
|
"amount_ore": 1500,
|
|
"accounts": [
|
|
{ "account_id": "old", "created_at": "2024-01-01T00:00:00.000Z", "archived": true, "payer_net_ore": -1000 },
|
|
{ "account_id": "new", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": -1000 }
|
|
]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "old", "amount_ore": 1000 }, { "account_id": "new", "amount_ore": 500 } ], "excess_ore": 0, "over_settled_account_ids": [] } }
|
|
},
|
|
{
|
|
"name": "equal created_at among owing accounts: ordered by id ascending",
|
|
"input": {
|
|
"amount_ore": 1500,
|
|
"accounts": [
|
|
{ "account_id": "q", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": -1000 },
|
|
{ "account_id": "p", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": -1000 }
|
|
]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "p", "amount_ore": 1000 }, { "account_id": "q", "amount_ore": 500 } ], "excess_ore": 0, "over_settled_account_ids": [] } }
|
|
},
|
|
{
|
|
"name": "equal created_at with no owing account: target chosen by id ascending",
|
|
"input": {
|
|
"amount_ore": 100,
|
|
"accounts": [
|
|
{ "account_id": "n", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": 0 },
|
|
{ "account_id": "m", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": 50 }
|
|
]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "m", "amount_ore": 100 } ], "excess_ore": 100, "over_settled_account_ids": ["m"] } }
|
|
},
|
|
{
|
|
"name": "single owing account, amount above its debt: excess to it",
|
|
"input": {
|
|
"amount_ore": 1000,
|
|
"accounts": [
|
|
{ "account_id": "Z", "created_at": "2024-12-01T00:00:00.000Z", "archived": false, "payer_net_ore": 10000 },
|
|
{ "account_id": "X", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": -1 }
|
|
]
|
|
},
|
|
"expected": { "ok": { "allocations": [ { "account_id": "X", "amount_ore": 1000 } ], "excess_ore": 999, "over_settled_account_ids": ["X"] } }
|
|
},
|
|
{
|
|
"name": "no accounts",
|
|
"input": { "amount_ore": 1000, "accounts": [] },
|
|
"expected": { "error": "no_accounts" }
|
|
},
|
|
{
|
|
"name": "amount 0",
|
|
"input": {
|
|
"amount_ore": 0,
|
|
"accounts": [ { "account_id": "X", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": -30000 } ]
|
|
},
|
|
"expected": { "error": "amount_out_of_range" }
|
|
},
|
|
{
|
|
"name": "amount 1 000 000 001",
|
|
"input": {
|
|
"amount_ore": 1000000001,
|
|
"accounts": [ { "account_id": "X", "created_at": "2025-01-01T00:00:00.000Z", "archived": false, "payer_net_ore": -30000 } ]
|
|
},
|
|
"expected": { "error": "amount_out_of_range" }
|
|
},
|
|
{
|
|
"name": "error order: amount checked before no accounts",
|
|
"input": { "amount_ore": 0, "accounts": [] },
|
|
"expected": { "error": "amount_out_of_range" }
|
|
}
|
|
]
|
|
}
|