[
{"name": "Edinburgh: 5% of three nights' accommodation", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 15000, "currency": "GBP"}, {"minor": 15000, "currency": "GBP"}, {"minor": 15000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-EDH", "levy": {"minor": 2250, "currency": "GBP"}, "leviableNights": 3, "exemption": null, "rule": {"method": "percent", "basisPoints": 500, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": 5, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "Edinburgh: a week is levied on the first five nights only", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 12000, "currency": "GBP"}, {"minor": 12000, "currency": "GBP"}, {"minor": 12000, "currency": "GBP"}, {"minor": 12000, "currency": "GBP"}, {"minor": 12000, "currency": "GBP"}, {"minor": 12000, "currency": "GBP"}, {"minor": 12000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-EDH", "levy": {"minor": 3000, "currency": "GBP"}, "leviableNights": 5, "exemption": null, "rule": {"method": "percent", "basisPoints": 500, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": 5, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "Edinburgh: the first five nights' own prices, not an average of the stay", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}, {"minor": 10000, "currency": "GBP"}, {"minor": 20000, "currency": "GBP"}, {"minor": 20000, "currency": "GBP"}, {"minor": 20000, "currency": "GBP"}, {"minor": 30000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-EDH", "levy": {"minor": 4000, "currency": "GBP"}, "leviableNights": 5, "exemption": null, "rule": {"method": "percent", "basisPoints": 500, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": 5, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "Edinburgh: rounds half-up, 5% of 123.45 is 6.1725", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 12345, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-EDH", "levy": {"minor": 617, "currency": "GBP"}, "leviableNights": 1, "exemption": null, "rule": {"method": "percent", "basisPoints": 500, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": 5, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "Edinburgh: an exact half penny rounds up, 5% of 123.50 is 6.175", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 12350, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-EDH", "levy": {"minor": 618, "currency": "GBP"}, "leviableNights": 1, "exemption": null, "rule": {"method": "percent", "basisPoints": 500, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": 5, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "Edinburgh: the first day of the scheme", "args": ["GB-EDH", null, {"arrival": "2026-07-24", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-EDH", "levy": {"minor": 500, "currency": "GBP"}, "leviableNights": 1, "exemption": null, "rule": {"method": "percent", "basisPoints": 500, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": 5, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "Edinburgh: a stay that began the day before the scheme is not levied, even after it starts", "args": ["GB-EDH", null, {"arrival": "2026-07-23", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}, {"minor": 10000, "currency": "GBP"}, {"minor": 10000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-EDH", "levy": {"minor": 0, "currency": "GBP"}, "leviableNights": 0, "exemption": "not-in-force", "rule": null}},
{"name": "Edinburgh: booked and part-paid before 1 October 2025 is exempt", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2025-09-15", "firstPaidOn": "2025-09-15"}], "expect": {"locality": "GB-EDH", "levy": {"minor": 0, "currency": "GBP"}, "leviableNights": 0, "exemption": "booked-and-paid-before-scheme", "rule": {"method": "percent", "basisPoints": 500, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": 5, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "Edinburgh: booked before 1 October 2025 but not paid is levied", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2025-09-15", "firstPaidOn": null}], "expect": {"locality": "GB-EDH", "levy": {"minor": 500, "currency": "GBP"}, "leviableNights": 1, "exemption": null, "rule": {"method": "percent", "basisPoints": 500, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": 5, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "Edinburgh: booked before, first paid on 1 October 2025 is levied", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2025-09-30", "firstPaidOn": "2025-10-01"}], "expect": {"locality": "GB-EDH", "levy": {"minor": 500, "currency": "GBP"}, "leviableNights": 1, "exemption": null, "rule": {"method": "percent", "basisPoints": 500, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": 5, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "Edinburgh: a hostel bed is levied at the same 5%", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 3000, "currency": "GBP"}, {"minor": 3000, "currency": "GBP"}], "accommodation": "shared", "adults": 1, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-EDH", "levy": {"minor": 300, "currency": "GBP"}, "leviableNights": 2, "exemption": null, "rule": {"method": "percent", "basisPoints": 500, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": 5, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "Cardiff: 1.30 per person per night, children included in a hotel", "args": ["GB-CRF", null, {"arrival": "2027-05-01", "nightlyCharges": [{"minor": 9000, "currency": "GBP"}, {"minor": 9000, "currency": "GBP"}, {"minor": 9000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 1, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-CRF", "levy": {"minor": 1170, "currency": "GBP"}, "leviableNights": 3, "exemption": null, "rule": {"method": "per-person-night", "basisPoints": 0, "amount": {"minor": 130, "currency": "GBP"}, "maxNights": null, "exemptOverNights": 31, "under18Exempt": false}}},
{"name": "Cardiff: 75p per adult per night in a hostel, under-18s disregarded", "args": ["GB-CRF", null, {"arrival": "2027-05-01", "nightlyCharges": [{"minor": 2500, "currency": "GBP"}, {"minor": 2500, "currency": "GBP"}], "accommodation": "shared", "adults": 2, "children": 2, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-CRF", "levy": {"minor": 300, "currency": "GBP"}, "leviableNights": 2, "exemption": null, "rule": {"method": "per-person-night", "basisPoints": 0, "amount": {"minor": 75, "currency": "GBP"}, "maxNights": null, "exemptOverNights": 31, "under18Exempt": true}}},
{"name": "Cardiff: 31 nights is levied on every night", "args": ["GB-CRF", null, {"arrival": "2027-05-01", "nightlyCharges": [{"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}], "accommodation": "standard", "adults": 1, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-CRF", "levy": {"minor": 4030, "currency": "GBP"}, "leviableNights": 31, "exemption": null, "rule": {"method": "per-person-night", "basisPoints": 0, "amount": {"minor": 130, "currency": "GBP"}, "maxNights": null, "exemptOverNights": 31, "under18Exempt": false}}},
{"name": "Cardiff: a stay of more than 31 nights is not levied at all", "args": ["GB-CRF", null, {"arrival": "2027-05-01", "nightlyCharges": [{"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}, {"minor": 5000, "currency": "GBP"}], "accommodation": "standard", "adults": 1, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-CRF", "levy": {"minor": 0, "currency": "GBP"}, "leviableNights": 0, "exemption": "long-stay", "rule": {"method": "per-person-night", "basisPoints": 0, "amount": {"minor": 130, "currency": "GBP"}, "maxNights": null, "exemptOverNights": 31, "under18Exempt": false}}},
{"name": "Cardiff: before 1 April 2027 there is no levy", "args": ["GB-CRF", null, {"arrival": "2027-03-31", "nightlyCharges": [{"minor": 9000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-CRF", "levy": {"minor": 0, "currency": "GBP"}, "leviableNights": 0, "exemption": "not-in-force", "rule": null}},
{"name": "Cardiff: the first day of the levy", "args": ["GB-CRF", null, {"arrival": "2027-04-01", "nightlyCharges": [{"minor": 9000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "GB-CRF", "levy": {"minor": 260, "currency": "GBP"}, "leviableNights": 1, "exemption": null, "rule": {"method": "per-person-night", "basisPoints": 0, "amount": {"minor": 130, "currency": "GBP"}, "maxNights": null, "exemptOverNights": 31, "under18Exempt": false}}},
{"name": "a caller's own percentage levy with no caps", "args": ["custom", {"method": "percent", "basisPoints": 1000, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": null, "exemptOverNights": null, "under18Exempt": false}, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}, {"minor": 10000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "custom", "levy": {"minor": 2000, "currency": "GBP"}, "leviableNights": 2, "exemption": null, "rule": {"method": "percent", "basisPoints": 1000, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": null, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "a caller's own flat levy capped at 7 nights, in euros", "args": ["custom", {"method": "per-person-night", "basisPoints": 0, "amount": {"minor": 200, "currency": "EUR"}, "maxNights": 7, "exemptOverNights": null, "under18Exempt": false}, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 8000, "currency": "EUR"}, {"minor": 8000, "currency": "EUR"}, {"minor": 8000, "currency": "EUR"}, {"minor": 8000, "currency": "EUR"}, {"minor": 8000, "currency": "EUR"}, {"minor": 8000, "currency": "EUR"}, {"minor": 8000, "currency": "EUR"}, {"minor": 8000, "currency": "EUR"}, {"minor": 8000, "currency": "EUR"}, {"minor": 8000, "currency": "EUR"}], "accommodation": "standard", "adults": 2, "children": 1, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expect": {"locality": "custom", "levy": {"minor": 4200, "currency": "EUR"}, "leviableNights": 7, "exemption": null, "rule": {"method": "per-person-night", "basisPoints": 0, "amount": {"minor": 200, "currency": "EUR"}, "maxNights": 7, "exemptOverNights": null, "under18Exempt": false}}},
{"name": "an unknown locality is an error", "args": ["GB-LND", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expectError": "no visitor levy data for locality \"GB-LND\""},
{"name": "custom without a rule is an error", "args": ["custom", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expectError": "locality custom needs a customRule"},
{"name": "a rule with a known locality is an error", "args": ["GB-EDH", {"method": "percent", "basisPoints": 1000, "amount": {"minor": 0, "currency": "GBP"}, "maxNights": null, "exemptOverNights": null, "under18Exempt": false}, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expectError": "customRule is only used with locality custom"},
{"name": "a stay with no nights is an error", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expectError": "a stay needs at least one night"},
{"name": "nights in two currencies are an error", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}, {"minor": 10000, "currency": "EUR"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expectError": "currency mismatch"},
{"name": "a flat levy in another currency from the stay is an error", "args": ["custom", {"method": "per-person-night", "basisPoints": 0, "amount": {"minor": 200, "currency": "GBP"}, "maxNights": null, "exemptOverNights": null, "under18Exempt": false}, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 8000, "currency": "EUR"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expectError": "currency mismatch"},
{"name": "an unknown accommodation type is an error", "args": ["GB-CRF", null, {"arrival": "2027-05-01", "nightlyCharges": [{"minor": 9000, "currency": "GBP"}], "accommodation": "caravan", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expectError": "unknown accommodation type \"caravan\""},
{"name": "negative guests are an error", "args": ["GB-CRF", null, {"arrival": "2027-05-01", "nightlyCharges": [{"minor": 9000, "currency": "GBP"}], "accommodation": "standard", "adults": -1, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expectError": "adults must not be negative"},
{"name": "a negative nightly charge is an error", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": -100, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expectError": "nightly charges must not be negative"},
{"name": "an impossible booking date is an error", "args": ["GB-EDH", null, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-02-30", "firstPaidOn": "2026-03-01"}], "expectError": "is not a real calendar date"},
{"name": "an unknown levy method is an error", "args": ["custom", {"method": "per-room", "basisPoints": 0, "amount": {"minor": 200, "currency": "GBP"}, "maxNights": null, "exemptOverNights": null, "under18Exempt": false}, {"arrival": "2026-08-10", "nightlyCharges": [{"minor": 10000, "currency": "GBP"}], "accommodation": "standard", "adults": 2, "children": 0, "bookedOn": "2026-03-01", "firstPaidOn": "2026-03-01"}], "expectError": "unknown levy method \"per-room\""}
]