import { describe, expect, it } from "vitest"; import { buildTomorrowWeatherBrief, buildWeatherBrief } from "@/lib/weather-brief"; import type { WeatherForecast, HourlyForecast } from "@/types/forecast"; import type { WeatherWarning } from "@/types/imgw"; import { getWeatherCapabilities } from "@/types/weather-region"; function hour(overrides: Partial = {}): HourlyForecast { return { time: "2026-06-14T08:00", temperature: 10, feelsLike: 9, precipitationProbability: 20, precipitation: 0, weatherCode: 3, windSpeed: 4, source: "open-meteo", ...overrides, }; } function forecast(hours: HourlyForecast[]): WeatherForecast { return { latitude: 52.23, longitude: 21.01, region: "PL", timezone: "Europe/Warsaw", hourly: hours, daily: [], sources: ["open-meteo"], capabilities: getWeatherCapabilities("PL"), unavailableReasons: [], }; } function warning(overrides: Partial = {}): WeatherWarning { return { id: "meteo-1", kind: "meteo", level: 2, title: "Thunderstorms", description: null, comment: null, validFrom: "2026-06-15T06:00:00.000Z", validTo: "2026-06-15T18:00:00.000Z", publishedAt: null, probability: 80, areas: [], terytCodes: ["146501"], provinces: ["mazowieckie"], office: null, ...overrides, }; } describe("weather briefs", () => { it("uses selected temperature and wind units in the morning brief", () => { const brief = buildWeatherBrief({ forecast: forecast([ hour({ time: "2026-06-14T08:00", temperature: 10, windSpeed: 4, precipitationProbability: 80 }), hour({ time: "2026-06-14T09:00", temperature: 12, windSpeed: 5, precipitationProbability: 30 }), ]), warnings: [], province: "mazowieckie", countyTeryt: "1465", locationName: "Warsaw", language: "en", temperatureUnit: "f", windSpeedUnit: "mph", now: new Date("2026-06-14T06:00:00.000Z"), }); expect(brief).not.toBeNull(); expect(brief?.pushBody).toContain("50°F-54°F"); expect(brief?.pushBody).toContain("wind 11 mph"); expect(brief?.pushBody).not.toContain("km/h"); }); it("warns about tomorrow storms from model weather codes without IMGW warning", () => { const brief = buildTomorrowWeatherBrief({ forecast: forecast([ hour({ time: "2026-06-15T08:00", temperature: 14, weatherCode: 3, precipitationProbability: 20 }), hour({ time: "2026-06-15T15:00", temperature: 21, weatherCode: 95, precipitationProbability: 30 }), ]), warnings: [], province: "mazowieckie", countyTeryt: "1465", locationName: "Warsaw", language: "en", now: new Date("2026-06-14T16:00:00.000Z"), }); expect(brief?.severity).toBe("warning"); expect(brief?.headline).toBe("Thunderstorms are possible tomorrow"); expect(brief?.pushBody).toContain("thunderstorms"); expect(brief?.pushBody).not.toContain("IMGW"); }); it("prefers relevant IMGW county warning over province-wide context", () => { const brief = buildTomorrowWeatherBrief({ forecast: forecast([hour({ time: "2026-06-15T12:00", weatherCode: 3 })]), warnings: [ warning({ title: "Radom storm", terytCodes: ["142501"] }), warning({ title: "Warsaw storm", terytCodes: ["146501"] }), ], province: "mazowieckie", countyTeryt: "1465", locationName: "Warsaw", language: "en", now: new Date("2026-06-14T16:00:00.000Z"), }); expect(brief?.headline).toBe("Tomorrow: IMGW Warsaw storm"); expect(brief?.pushBody).toContain("IMGW: Warsaw storm"); expect(brief?.pushBody).not.toContain("Radom storm"); }); });