[
{
"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"
}
]