import { describe, expect, it, vi } from "vitest"; import { createCache } from "../src/services/cache.js"; import type { FetchLike } from "../src/services/http.js"; import { getNearestTideSummary, normalizeNearestTideSummary } from "../src/services/tides.js"; describe("BSH tide normalization", () => { it("selects the nearest station and upcoming events", () => { const summary = normalizeNearestTideSummary( { features: [ { geometry: { type: "Point", coordinates: [12.1, 54.2] }, properties: { gauge_label: "Demo Pegel", forecast_timestamp: "2026-07-09 09:00:00+02:00", high_water_low_water: [ { event_timestamp: "2026-07-09 10:00:00+02:00", event: "HW", forecast_value: 620, forecast_deviation: "+0,2 m" }, { event_timestamp: "2026-07-09 15:30:00+02:00", event: "NW", tidal_prediction_value: "370" } ], curve: [ { timestamp: "2026-07-09 10:00:00+02:00", tidal_prediction: "620", measurement: "618" } ] } } ] }, { lat: 54.2, lon: 12.1 }, new Date("2026-07-09T08:00:00+02:00") ); expect(summary?.station).toBe("Demo Pegel"); expect(summary?.nextHigh?.heightM).toBe(6.2); expect(summary?.nextLow?.heightM).toBe(3.7); expect(summary?.waterLevelCurve[0]?.predictedM).toBe(6.2); }); it("uses params.at instead of wall-clock time when filtering upcoming tide events", async () => { const cache = createCache(); const fetcher = vi.fn(async () => new Response( JSON.stringify({ features: [ { geometry: { type: "Point", coordinates: [7.18, 53.34] }, properties: { gauge_label: "Emden", forecast_timestamp: "2030-01-01T09:00:00.000Z", high_water_low_water: [ { event_timestamp: "2030-01-01T10:00:00.000Z", event: "HW", forecast_value: 610 }, { event_timestamp: "2030-01-01T13:00:00.000Z", event: "NW", forecast_value: 350 }, { event_timestamp: "2030-01-01T16:00:00.000Z", event: "HW", forecast_value: 625 } ] } } ] }), { status: 200, headers: { "content-type": "application/json" } } )) as unknown as FetchLike; try { const summary = await getNearestTideSummary( { lat: 53.34, lon: 7.18, at: "2030-01-01T12:00:00.000Z" }, { cache, fetcher } ); expect(fetcher).toHaveBeenCalledTimes(1); expect(summary?.nextLow).toEqual( expect.objectContaining({ time: "2030-01-01T13:00:00.000Z", heightM: 3.5 }) ); expect(summary?.nextHigh).toEqual( expect.objectContaining({ time: "2030-01-01T16:00:00.000Z", heightM: 6.25 }) ); } finally { await cache.close(); } }); });