[
{"name": "three nights across the season change: two low weekdays and a high night, 5% off for 3 nights", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [{"minNights": 3, "basisPoints": 500}, {"minNights": 7, "basisPoints": 1000}], "2026-03-30", 3], "expect": {"nights": [{"date": "2026-03-30", "season": "low", "rate": {"minor": 9000, "currency": "GBP"}}, {"date": "2026-03-31", "season": "low", "rate": {"minor": 9000, "currency": "GBP"}}, {"date": "2026-04-01", "season": "high", "rate": {"minor": 14000, "currency": "GBP"}}], "subtotal": {"minor": 32000, "currency": "GBP"}, "discountBasisPoints": 500, "discount": {"minor": 1600, "currency": "GBP"}, "total": {"minor": 30400, "currency": "GBP"}}},
{"name": "Friday and Saturday nights at the weekend rate, too short for a discount", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [{"minNights": 3, "basisPoints": 500}, {"minNights": 7, "basisPoints": 1000}], "2026-03-27", 2], "expect": {"nights": [{"date": "2026-03-27", "season": "low weekend", "rate": {"minor": 11000, "currency": "GBP"}}, {"date": "2026-03-28", "season": "low weekend", "rate": {"minor": 11000, "currency": "GBP"}}], "subtotal": {"minor": 22000, "currency": "GBP"}, "discountBasisPoints": 0, "discount": {"minor": 0, "currency": "GBP"}, "total": {"minor": 22000, "currency": "GBP"}}},
{"name": "a week: Sunday night is a weekday night, and 7 nights earns 10% rather than 5%", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [{"minNights": 3, "basisPoints": 500}, {"minNights": 7, "basisPoints": 1000}], "2026-03-26", 7], "expect": {"nights": [{"date": "2026-03-26", "season": "low", "rate": {"minor": 9000, "currency": "GBP"}}, {"date": "2026-03-27", "season": "low weekend", "rate": {"minor": 11000, "currency": "GBP"}}, {"date": "2026-03-28", "season": "low weekend", "rate": {"minor": 11000, "currency": "GBP"}}, {"date": "2026-03-29", "season": "low", "rate": {"minor": 9000, "currency": "GBP"}}, {"date": "2026-03-30", "season": "low", "rate": {"minor": 9000, "currency": "GBP"}}, {"date": "2026-03-31", "season": "low", "rate": {"minor": 9000, "currency": "GBP"}}, {"date": "2026-04-01", "season": "high", "rate": {"minor": 14000, "currency": "GBP"}}], "subtotal": {"minor": 72000, "currency": "GBP"}, "discountBasisPoints": 1000, "discount": {"minor": 7200, "currency": "GBP"}, "total": {"minor": 64800, "currency": "GBP"}}},
{"name": "one Saturday night", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [{"minNights": 3, "basisPoints": 500}, {"minNights": 7, "basisPoints": 1000}], "2026-03-28", 1], "expect": {"nights": [{"date": "2026-03-28", "season": "low weekend", "rate": {"minor": 11000, "currency": "GBP"}}], "subtotal": {"minor": 11000, "currency": "GBP"}, "discountBasisPoints": 0, "discount": {"minor": 0, "currency": "GBP"}, "total": {"minor": 11000, "currency": "GBP"}}},
{"name": "the last night of a season is inclusive", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [], "2026-03-31", 1], "expect": {"nights": [{"date": "2026-03-31", "season": "low", "rate": {"minor": 9000, "currency": "GBP"}}], "subtotal": {"minor": 9000, "currency": "GBP"}, "discountBasisPoints": 0, "discount": {"minor": 0, "currency": "GBP"}, "total": {"minor": 9000, "currency": "GBP"}}},
{"name": "a stay over 29 February prices the leap day, not 1 March", "args": [[{"season": "winter", "firstNight": "2028-02-01", "lastNight": "2028-02-29", "daysOfWeek": [], "rate": {"minor": 10000, "currency": "GBP"}}, {"season": "spring", "firstNight": "2028-03-01", "lastNight": "2028-03-31", "daysOfWeek": [], "rate": {"minor": 12000, "currency": "GBP"}}], [], "2028-02-28", 2], "expect": {"nights": [{"date": "2028-02-28", "season": "winter", "rate": {"minor": 10000, "currency": "GBP"}}, {"date": "2028-02-29", "season": "winter", "rate": {"minor": 10000, "currency": "GBP"}}], "subtotal": {"minor": 20000, "currency": "GBP"}, "discountBasisPoints": 0, "discount": {"minor": 0, "currency": "GBP"}, "total": {"minor": 20000, "currency": "GBP"}}},
{"name": "the discount rounds half-up once on the stay: 3.33% of 220.00 is 7.326", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [{"minNights": 2, "basisPoints": 333}], "2026-03-27", 2], "expect": {"nights": [{"date": "2026-03-27", "season": "low weekend", "rate": {"minor": 11000, "currency": "GBP"}}, {"date": "2026-03-28", "season": "low weekend", "rate": {"minor": 11000, "currency": "GBP"}}], "subtotal": {"minor": 22000, "currency": "GBP"}, "discountBasisPoints": 333, "discount": {"minor": 733, "currency": "GBP"}, "total": {"minor": 21267, "currency": "GBP"}}},
{"name": "discounts may be listed in any order", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [{"minNights": 7, "basisPoints": 1000}, {"minNights": 3, "basisPoints": 500}], "2026-03-30", 3], "expect": {"nights": [{"date": "2026-03-30", "season": "low", "rate": {"minor": 9000, "currency": "GBP"}}, {"date": "2026-03-31", "season": "low", "rate": {"minor": 9000, "currency": "GBP"}}, {"date": "2026-04-01", "season": "high", "rate": {"minor": 14000, "currency": "GBP"}}], "subtotal": {"minor": 32000, "currency": "GBP"}, "discountBasisPoints": 500, "discount": {"minor": 1600, "currency": "GBP"}, "total": {"minor": 30400, "currency": "GBP"}}},
{"name": "a night with no rate is an error", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [{"minNights": 3, "basisPoints": 500}, {"minNights": 7, "basisPoints": 1000}], "2026-09-30", 2], "expectError": "no rate for the night of 2026-10-01"},
{"name": "two rows for one night are an error, not a guess", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}, {"season": "easter", "firstNight": "2026-04-03", "lastNight": "2026-04-06", "daysOfWeek": [], "rate": {"minor": 18000, "currency": "GBP"}}], [{"minNights": 3, "basisPoints": 500}, {"minNights": 7, "basisPoints": 1000}], "2026-04-02", 2], "expectError": "more than one rate for the night of 2026-04-03"},
{"name": "no nights is an error", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [{"minNights": 3, "basisPoints": 500}, {"minNights": 7, "basisPoints": 1000}], "2026-03-30", 0], "expectError": "nights must be at least 1"},
{"name": "an empty rate table is an error", "args": [[], [{"minNights": 3, "basisPoints": 500}, {"minNights": 7, "basisPoints": 1000}], "2026-03-30", 1], "expectError": "the rate table is empty"},
{"name": "a weekday of 8 is an error", "args": [[{"season": "x", "firstNight": "2026-01-01", "lastNight": "2026-12-31", "daysOfWeek": [8], "rate": {"minor": 100, "currency": "GBP"}}], [], "2026-03-30", 1], "expectError": "daysOfWeek must be 1 (Monday) to 7 (Sunday)"},
{"name": "a row that ends before it starts is an error", "args": [[{"season": "x", "firstNight": "2026-12-31", "lastNight": "2026-01-01", "daysOfWeek": [], "rate": {"minor": 100, "currency": "GBP"}}], [], "2026-03-30", 1], "expectError": "rate \"x\" ends before it starts"},
{"name": "two discounts for the same length are an error", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [{"minNights": 3, "basisPoints": 500}, {"minNights": 3, "basisPoints": 700}], "2026-03-30", 3], "expectError": "two stay discounts start at 3 nights"},
{"name": "a discount over 100% is an error", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [{"minNights": 3, "basisPoints": 10001}], "2026-03-30", 3], "expectError": "basisPoints must be a whole number from 0 to 10000"},
{"name": "mixed currencies are an error", "args": [[{"season": "a", "firstNight": "2026-01-01", "lastNight": "2026-01-01", "daysOfWeek": [], "rate": {"minor": 100, "currency": "GBP"}}, {"season": "b", "firstNight": "2026-01-02", "lastNight": "2026-01-02", "daysOfWeek": [], "rate": {"minor": 100, "currency": "EUR"}}], [], "2026-01-01", 2], "expectError": "currency mismatch"},
{"name": "an impossible arrival date is an error", "args": [[{"season": "low", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [1, 2, 3, 4, 7], "rate": {"minor": 9000, "currency": "GBP"}}, {"season": "low weekend", "firstNight": "2026-01-01", "lastNight": "2026-03-31", "daysOfWeek": [5, 6], "rate": {"minor": 11000, "currency": "GBP"}}, {"season": "high", "firstNight": "2026-04-01", "lastNight": "2026-09-30", "daysOfWeek": [], "rate": {"minor": 14000, "currency": "GBP"}}], [{"minNights": 3, "basisPoints": 500}, {"minNights": 7, "basisPoints": 1000}], "2026-02-29", 1], "expectError": "is not a real calendar date"}
]