test: add weather logic test suite
All checks were successful
CI / Lint, test, typecheck and build (push) Successful in 9m58s

This commit is contained in:
zv
2026-06-14 20:44:37 +02:00
parent ee55521803
commit bdba3c709c
11 changed files with 1357 additions and 10 deletions

117
tests/weather-brief.test.ts Normal file
View File

@@ -0,0 +1,117 @@
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> = {}): 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> = {}): 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");
});
});