[
{"name": "10 units a day carried 15 days forward", "args": [{"date": "2026-01-01", "value": 10000}, {"date": "2026-01-31", "value": 10300}, "2026-02-15", 5, "half-up"], "expect": {"read": 10450, "units": 150, "rolledOver": false}},
{"name": "history across a rollover: 99900 to 00100 is 200 units", "args": [{"date": "2026-03-01", "value": 99900}, {"date": "2026-03-21", "value": 100}, "2026-03-31", 5, "half-up"], "expect": {"read": 200, "units": 100, "rolledOver": false}},
{"name": "the estimate itself rolls over past 99999", "args": [{"date": "2026-01-01", "value": 99000}, {"date": "2026-02-10", "value": 99800}, "2026-03-12", 5, "half-up"], "expect": {"read": 400, "units": 600, "rolledOver": true}},
{"name": "one rounding: 100 units over 30 days, 7 days ahead is 23.33, so 23", "args": [{"date": "2026-04-01", "value": 5000}, {"date": "2026-05-01", "value": 5100}, "2026-05-08", 5, "half-up"], "expect": {"read": 5123, "units": 23, "rolledOver": false}},
{"name": "rounding the daily average first would give 21; up gives 24", "args": [{"date": "2026-04-01", "value": 5000}, {"date": "2026-05-01", "value": 5100}, "2026-05-08", 5, "up"], "expect": {"read": 5124, "units": 24, "rolledOver": false}},
{"name": "an exact half unit under half-up rounds up", "args": [{"date": "2026-06-01", "value": 700}, {"date": "2026-06-03", "value": 705}, "2026-06-04", 4, "half-up"], "expect": {"read": 708, "units": 3, "rolledOver": false}},
{"name": "an exact half unit under half-even goes to the even unit", "args": [{"date": "2026-06-01", "value": 700}, {"date": "2026-06-03", "value": 705}, "2026-06-04", 4, "half-even"], "expect": {"read": 707, "units": 2, "rolledOver": false}},
{"name": "down never over-estimates", "args": [{"date": "2026-06-01", "value": 700}, {"date": "2026-06-03", "value": 705}, "2026-06-04", 4, "down"], "expect": {"read": 707, "units": 2, "rolledOver": false}},
{"name": "estimating on the day of the latest read adds nothing", "args": [{"date": "2026-01-01", "value": 10000}, {"date": "2026-01-31", "value": 10300}, "2026-01-31", 5, "half-up"], "expect": {"read": 10300, "units": 0, "rolledOver": false}},
{"name": "February 2024 has 29 days: 290 units is 10 a day", "args": [{"date": "2024-02-01", "value": 1000}, {"date": "2024-03-01", "value": 1290}, "2024-03-11", 5, "half-up"], "expect": {"read": 1390, "units": 100, "rolledOver": false}},
{"name": "a meter that did not move estimates no use", "args": [{"date": "2025-12-01", "value": 4242}, {"date": "2026-01-01", "value": 4242}, "2026-02-01", 4, "half-up"], "expect": {"read": 4242, "units": 0, "rolledOver": false}},
{"name": "a long gap over a year end", "args": [{"date": "2025-01-01", "value": 0}, {"date": "2025-12-31", "value": 3640}, "2026-12-31", 6, "half-up"], "expect": {"read": 7290, "units": 3650, "rolledOver": false}},
{"name": "reads in the wrong order are refused", "args": [{"date": "2026-02-01", "value": 100}, {"date": "2026-01-01", "value": 200}, "2026-03-01", 5, "half-up"], "expectError": "the earlier read must be dated before the latest read"},
{"name": "two reads on the same day give no average", "args": [{"date": "2026-02-01", "value": 100}, {"date": "2026-02-01", "value": 200}, "2026-03-01", 5, "half-up"], "expectError": "the earlier read must be dated before the latest read"},
{"name": "a target date before the latest read is refused", "args": [{"date": "2026-01-01", "value": 100}, {"date": "2026-02-01", "value": 200}, "2026-01-15", 5, "half-up"], "expectError": "onDate must not be before the latest read"},
{"name": "a read too big for the register is refused", "args": [{"date": "2026-01-01", "value": 100}, {"date": "2026-02-01", "value": 200000}, "2026-03-01", 5, "half-up"], "expectError": "reads must be whole numbers from 0 to 99999"},
{"name": "an unknown rounding mode is refused", "args": [{"date": "2026-01-01", "value": 100}, {"date": "2026-02-01", "value": 200}, "2026-03-01", 5, "nearest"], "expectError": "unknown rounding mode \"nearest\""}
]