[ { "name": "under the cap: everything is charged", "args": [ [ { "date": "2026-03-02", "label": "Unarranged overdraft fee", "amount": { "minor": 500, "currency": "GBP" } }, { "date": "2026-03-09", "label": "Unarranged overdraft fee", "amount": { "minor": 500, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 1 ], "expect": { "charges": [ { "date": "2026-03-02", "label": "Unarranged overdraft fee", "amount": { "minor": 500, "currency": "GBP" }, "charged": { "minor": 500, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } }, { "date": "2026-03-09", "label": "Unarranged overdraft fee", "amount": { "minor": 500, "currency": "GBP" }, "charged": { "minor": 500, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } } ], "totalCharged": { "minor": 1000, "currency": "GBP" }, "totalWaived": { "minor": 0, "currency": "GBP" } } }, { "name": "exactly at the cap: nothing is waived", "args": [ [ { "date": "2026-03-02", "label": "Unarranged overdraft fee", "amount": { "minor": 1000, "currency": "GBP" } }, { "date": "2026-03-09", "label": "Unarranged overdraft fee", "amount": { "minor": 1000, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 1 ], "expect": { "charges": [ { "date": "2026-03-02", "label": "Unarranged overdraft fee", "amount": { "minor": 1000, "currency": "GBP" }, "charged": { "minor": 1000, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } }, { "date": "2026-03-09", "label": "Unarranged overdraft fee", "amount": { "minor": 1000, "currency": "GBP" }, "charged": { "minor": 1000, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } } ], "totalCharged": { "minor": 2000, "currency": "GBP" }, "totalWaived": { "minor": 0, "currency": "GBP" } } }, { "name": "the charge that crosses the cap is reduced, later ones waived", "args": [ [ { "date": "2026-03-02", "label": "Unarranged overdraft fee", "amount": { "minor": 800, "currency": "GBP" } }, { "date": "2026-03-05", "label": "Unarranged overdraft fee", "amount": { "minor": 800, "currency": "GBP" } }, { "date": "2026-03-06", "label": "Unarranged overdraft fee", "amount": { "minor": 800, "currency": "GBP" } }, { "date": "2026-03-20", "label": "Unarranged overdraft fee", "amount": { "minor": 800, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 1 ], "expect": { "charges": [ { "date": "2026-03-02", "label": "Unarranged overdraft fee", "amount": { "minor": 800, "currency": "GBP" }, "charged": { "minor": 800, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } }, { "date": "2026-03-05", "label": "Unarranged overdraft fee", "amount": { "minor": 800, "currency": "GBP" }, "charged": { "minor": 800, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } }, { "date": "2026-03-06", "label": "Unarranged overdraft fee", "amount": { "minor": 800, "currency": "GBP" }, "charged": { "minor": 400, "currency": "GBP" }, "waived": { "minor": 400, "currency": "GBP" } }, { "date": "2026-03-20", "label": "Unarranged overdraft fee", "amount": { "minor": 800, "currency": "GBP" }, "charged": { "minor": 0, "currency": "GBP" }, "waived": { "minor": 800, "currency": "GBP" } } ], "totalCharged": { "minor": 2000, "currency": "GBP" }, "totalWaived": { "minor": 1200, "currency": "GBP" } } }, { "name": "a new calendar month starts a fresh cap", "args": [ [ { "date": "2026-03-30", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" } }, { "date": "2026-03-31", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" } }, { "date": "2026-04-01", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 1 ], "expect": { "charges": [ { "date": "2026-03-30", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 1500, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } }, { "date": "2026-03-31", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 500, "currency": "GBP" }, "waived": { "minor": 1000, "currency": "GBP" } }, { "date": "2026-04-01", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 1500, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } } ], "totalCharged": { "minor": 3500, "currency": "GBP" }, "totalWaived": { "minor": 1000, "currency": "GBP" } } }, { "name": "dates out of order are capped in date order but returned in input order", "args": [ [ { "date": "2026-03-20", "label": "Late fee", "amount": { "minor": 1500, "currency": "GBP" } }, { "date": "2026-03-02", "label": "Early fee", "amount": { "minor": 1500, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 1 ], "expect": { "charges": [ { "date": "2026-03-20", "label": "Late fee", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 500, "currency": "GBP" }, "waived": { "minor": 1000, "currency": "GBP" } }, { "date": "2026-03-02", "label": "Early fee", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 1500, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } } ], "totalCharged": { "minor": 2000, "currency": "GBP" }, "totalWaived": { "minor": 1000, "currency": "GBP" } } }, { "name": "same-day charges keep their input order", "args": [ [ { "date": "2026-03-10", "label": "First", "amount": { "minor": 1500, "currency": "GBP" } }, { "date": "2026-03-10", "label": "Second", "amount": { "minor": 1500, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 1 ], "expect": { "charges": [ { "date": "2026-03-10", "label": "First", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 1500, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } }, { "date": "2026-03-10", "label": "Second", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 500, "currency": "GBP" }, "waived": { "minor": 1000, "currency": "GBP" } } ], "totalCharged": { "minor": 2000, "currency": "GBP" }, "totalWaived": { "minor": 1000, "currency": "GBP" } } }, { "name": "cycle day 15: the 14th belongs to the previous period", "args": [ [ { "date": "2026-03-14", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" } }, { "date": "2026-03-15", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" } }, { "date": "2026-04-14", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 15 ], "expect": { "charges": [ { "date": "2026-03-14", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 1500, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } }, { "date": "2026-03-15", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 1500, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } }, { "date": "2026-04-14", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 500, "currency": "GBP" }, "waived": { "minor": 1000, "currency": "GBP" } } ], "totalCharged": { "minor": 3500, "currency": "GBP" }, "totalWaived": { "minor": 1000, "currency": "GBP" } } }, { "name": "cycle day 15 across a year end", "args": [ [ { "date": "2025-12-20", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" } }, { "date": "2026-01-10", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" } }, { "date": "2026-01-15", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 15 ], "expect": { "charges": [ { "date": "2025-12-20", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 1500, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } }, { "date": "2026-01-10", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 500, "currency": "GBP" }, "waived": { "minor": 1000, "currency": "GBP" } }, { "date": "2026-01-15", "label": "Unarranged overdraft fee", "amount": { "minor": 1500, "currency": "GBP" }, "charged": { "minor": 1500, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } } ], "totalCharged": { "minor": 3500, "currency": "GBP" }, "totalWaived": { "minor": 1000, "currency": "GBP" } } }, { "name": "a zero cap waives everything", "args": [ [ { "date": "2026-03-02", "label": "Unarranged overdraft fee", "amount": { "minor": 500, "currency": "GBP" } }, { "date": "2026-03-09", "label": "Unarranged overdraft fee", "amount": { "minor": 700, "currency": "GBP" } } ], { "minor": 0, "currency": "GBP" }, 1 ], "expect": { "charges": [ { "date": "2026-03-02", "label": "Unarranged overdraft fee", "amount": { "minor": 500, "currency": "GBP" }, "charged": { "minor": 0, "currency": "GBP" }, "waived": { "minor": 500, "currency": "GBP" } }, { "date": "2026-03-09", "label": "Unarranged overdraft fee", "amount": { "minor": 700, "currency": "GBP" }, "charged": { "minor": 0, "currency": "GBP" }, "waived": { "minor": 700, "currency": "GBP" } } ], "totalCharged": { "minor": 0, "currency": "GBP" }, "totalWaived": { "minor": 1200, "currency": "GBP" } } }, { "name": "zero charges are allowed and change nothing", "args": [ [ { "date": "2026-03-02", "label": "Waived fee", "amount": { "minor": 0, "currency": "GBP" } }, { "date": "2026-03-03", "label": "Unarranged overdraft fee", "amount": { "minor": 2500, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 1 ], "expect": { "charges": [ { "date": "2026-03-02", "label": "Waived fee", "amount": { "minor": 0, "currency": "GBP" }, "charged": { "minor": 0, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } }, { "date": "2026-03-03", "label": "Unarranged overdraft fee", "amount": { "minor": 2500, "currency": "GBP" }, "charged": { "minor": 2000, "currency": "GBP" }, "waived": { "minor": 500, "currency": "GBP" } } ], "totalCharged": { "minor": 2000, "currency": "GBP" }, "totalWaived": { "minor": 500, "currency": "GBP" } } }, { "name": "no charges at all", "args": [ [], { "minor": 2000, "currency": "GBP" }, 1 ], "expect": { "charges": [], "totalCharged": { "minor": 0, "currency": "GBP" }, "totalWaived": { "minor": 0, "currency": "GBP" } } }, { "name": "a leap day is an ordinary date", "args": [ [ { "date": "2024-02-29", "label": "Unarranged overdraft fee", "amount": { "minor": 1200, "currency": "GBP" } }, { "date": "2024-02-01", "label": "Unarranged overdraft fee", "amount": { "minor": 1200, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 1 ], "expect": { "charges": [ { "date": "2024-02-29", "label": "Unarranged overdraft fee", "amount": { "minor": 1200, "currency": "GBP" }, "charged": { "minor": 800, "currency": "GBP" }, "waived": { "minor": 400, "currency": "GBP" } }, { "date": "2024-02-01", "label": "Unarranged overdraft fee", "amount": { "minor": 1200, "currency": "GBP" }, "charged": { "minor": 1200, "currency": "GBP" }, "waived": { "minor": 0, "currency": "GBP" } } ], "totalCharged": { "minor": 2000, "currency": "GBP" }, "totalWaived": { "minor": 400, "currency": "GBP" } } }, { "name": "a negative charge is an error", "args": [ [ { "date": "2026-03-02", "label": "Refund", "amount": { "minor": -500, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 1 ], "expectError": "must not be negative" }, { "name": "a charge in another currency is an error", "args": [ [ { "date": "2026-03-02", "label": "Unarranged overdraft fee", "amount": { "minor": 500, "currency": "EUR" } } ], { "minor": 2000, "currency": "GBP" }, 1 ], "expectError": "currency mismatch: EUR and GBP" }, { "name": "cycle day 29 is refused", "args": [ [], { "minor": 2000, "currency": "GBP" }, 29 ], "expectError": "cycleDay must be 1 to 28" }, { "name": "cycle day 0 is refused", "args": [ [], { "minor": 2000, "currency": "GBP" }, 0 ], "expectError": "cycleDay must be 1 to 28" }, { "name": "a negative cap is an error", "args": [ [], { "minor": -1, "currency": "GBP" }, 1 ], "expectError": "monthlyCap must not be negative" }, { "name": "an impossible date is an error", "args": [ [ { "date": "2026-02-30", "label": "Unarranged overdraft fee", "amount": { "minor": 500, "currency": "GBP" } } ], { "minor": 2000, "currency": "GBP" }, 1 ], "expectError": "not a real calendar date" } ]