-- Checks for lib/weather.lua. -- Run from the repo root: lua test/test_weather.lua -- The domain tables are what silently misreport when a boundary is off by one, -- so every range boundary gets an assertion. The card itself is verified by -- screenshot. package.path = './?.lua;' .. package.path local weather = require 'lib.weather' -- === Condition id to icon ================================================= -- OWM ids group by hundreds, but the boundaries are not round numbers: the -- ranges come from the polybar script that ran against this API for years. -- Each assertion pairs the last id in a range with the first id of the next, -- which is where an off-by-one would hide. -- Sunrise 1500, sunset 1900, so DAY must sit INSIDE that window and NIGHT -- outside it. Naming them without checking them against the window is how the -- first draft of this test made every timestamp evaluate as night. local DAY, NIGHT = 1700, 2000 local function icon(id, now) return weather.icon(id, now, 1500, 1900) -- sunrise 1500, sunset 1900 end -- Thunderstorm: everything up to 232. assert(icon(200, DAY) == weather.ICON.thunder_day, 'id 200 day') assert(icon(232, DAY) == weather.ICON.thunder_day, 'id 232 is still thunder') assert(icon(232, NIGHT) == weather.ICON.thunder_night, 'id 232 night') -- Light drizzle: 233..311. assert(icon(233, DAY) == weather.ICON.drizzle_day, 'id 233 leaves thunder') assert(icon(311, DAY) == weather.ICON.drizzle_day, 'id 311 is still light drizzle') -- Heavy drizzle: 312..321. assert(icon(312, DAY) == weather.ICON.drizzle_heavy_day, 'id 312 is heavy drizzle') assert(icon(321, DAY) == weather.ICON.drizzle_heavy_day, 'id 321 is still heavy drizzle') -- Rain: 322..531. assert(icon(322, DAY) == weather.ICON.rain_day, 'id 322 is rain') assert(icon(531, DAY) == weather.ICON.rain_day, 'id 531 is still rain') -- Snow: 532..622. One icon, no day/night variant. assert(icon(600, DAY) == weather.ICON.snow, 'id 600 is snow') assert(icon(622, NIGHT) == weather.ICON.snow, 'snow is the same at night') -- Fog: 623..771. assert(icon(741, DAY) == weather.ICON.fog, 'id 741 is fog') assert(icon(771, DAY) == weather.ICON.fog, 'id 771 is still fog') -- Tornado is a single id, not a range. assert(icon(781, DAY) == weather.ICON.tornado, 'id 781 is tornado') -- Clear and few clouds each have a day and a night face. assert(icon(800, DAY) == weather.ICON.clear_day, 'id 800 day is the sun') assert(icon(800, NIGHT) == weather.ICON.clear_night, 'id 800 night is the moon') assert(icon(801, DAY) == weather.ICON.few_day, 'id 801 day') assert(icon(801, NIGHT) == weather.ICON.few_night, 'id 801 night') -- Overcast: 802..804, no night variant. assert(icon(804, DAY) == weather.ICON.overcast, 'id 804 is overcast') -- Anything outside the known ids must be visibly wrong, not silently sunny. assert(icon(999, DAY) == weather.ICON.unknown, 'unknown id gets the error glyph') assert(weather.icon(nil, DAY, 1500, 1900) == weather.ICON.unknown, 'nil id') -- Nothing may raise. A missing timestamp with a known sun, or a known -- timestamp with no sun, both have to return a glyph rather than an error, -- because an error here is a blank dashboard with no message. assert(weather.icon(800, nil, 1500, 1900) == weather.ICON.clear_day, 'a nil now must not raise, and falls back to the day face') assert(weather.icon(800, DAY, nil, nil) == weather.ICON.clear_day, 'no sun times must not raise') -- The codepoint VALUES are deliberately not asserted. A test comparing -- M.ICON.snow against a literal would only prove the same escape was typed -- twice; whether E31A actually draws a snow cloud in the target font is not -- something an assertion can see. That check is the glyph sheet rendered in -- the plan's Step 5, and it is how a wrong-but-present codepoint was caught -- before this file existed. print('test_weather: all assertions passed')