169 lines
5.1 KiB
TypeScript
169 lines
5.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { MarineForecast, RouteResult } from "@watermaps/shared";
|
|
import { createRouteWeatherReport, summarizeRouteWeather } from "../src/routeWeatherReport";
|
|
|
|
describe("route weather report", () => {
|
|
it("samples start, middle and destination forecasts", async () => {
|
|
const requested: Array<{ lat: number; lon: number }> = [];
|
|
const report = await createRouteWeatherReport(routeFixture, async (coordinate) => {
|
|
requested.push(coordinate);
|
|
return forecast({
|
|
windSpeed: coordinate.lon > 7.1 ? 18 : 10,
|
|
waveHeightM: coordinate.lon > 7.1 ? 1.2 : 0.4
|
|
});
|
|
});
|
|
|
|
expect(requested).toHaveLength(3);
|
|
expect(report.maxWindSpeedKn).toBe(18);
|
|
expect(report.maxWaveHeightM).toBe(1.2);
|
|
expect(report.severity).toBe("caution");
|
|
expect(report.summary).toContain("Aufmerksam fahren");
|
|
expect(report.bridgeReport).toBeNull();
|
|
});
|
|
|
|
it("projects forecasts along the route timeline and adjusts ETA for along-route current", async () => {
|
|
const requestedTimes: Array<string | undefined> = [];
|
|
const departureTime = "2026-07-20T06:00:00.000Z";
|
|
const timedRoute: RouteResult = {
|
|
...routeFixture,
|
|
geometry: {
|
|
type: "LineString",
|
|
coordinates: [[7, 52], [7.2, 52], [7.4, 52]]
|
|
},
|
|
distanceNm: 20,
|
|
departureTime,
|
|
durationMinutes: 240,
|
|
eta: "2026-07-20T10:00:00.000Z"
|
|
};
|
|
|
|
const report = await createRouteWeatherReport(
|
|
timedRoute,
|
|
{ draughtM: 1.4, safetyReserveM: 0.5, cruiseSpeedKn: 5 },
|
|
async (_coordinate, at) => {
|
|
requestedTimes.push(at);
|
|
return forecast({
|
|
oceanCurrentSpeedKn: 1,
|
|
oceanCurrentDirectionDeg: 90,
|
|
forecastTime: at
|
|
});
|
|
}
|
|
);
|
|
|
|
expect(requestedTimes).toEqual([
|
|
"2026-07-20T06:00:00.000Z",
|
|
"2026-07-20T08:00:00.000Z",
|
|
"2026-07-20T10:00:00.000Z"
|
|
]);
|
|
expect(report.samples.map((sample) => sample.plannedTime)).toEqual(requestedTimes);
|
|
expect(report.samples.map((sample) => sample.currentAlongRouteKn)).toEqual([1, 1, 1]);
|
|
expect(report.departureTime).toBe(departureTime);
|
|
expect(report.averageAlongRouteCurrentKn).toBe(1);
|
|
expect(report.currentAdjustmentMinutes).toBe(-40);
|
|
expect(report.adjustedEta).toBe("2026-07-20T09:20:00.000Z");
|
|
});
|
|
|
|
it("adds a bridge report and marks a route as blocked by a low bridge", async () => {
|
|
const report = await createRouteWeatherReport(
|
|
routeFixture,
|
|
{ draughtM: 1.4, safetyReserveM: 0.5, airDraftM: 3 },
|
|
async () => forecast({}),
|
|
async (params) => {
|
|
expect(params.layers).toEqual(["bridges"]);
|
|
return {
|
|
type: "FeatureCollection",
|
|
features: [
|
|
{
|
|
type: "Feature",
|
|
id: "bridge-low",
|
|
properties: {
|
|
layer: "bridges",
|
|
name: "Niedrige Brücke",
|
|
clearance_m: 2.7,
|
|
clearance_label: "H 2.7 m",
|
|
label: "Niedrige Brücke H 2.7 m",
|
|
source: "OSM/Geofabrik"
|
|
},
|
|
geometry: {
|
|
type: "LineString",
|
|
coordinates: [
|
|
[6.99, 53.5],
|
|
[7.21, 53.5]
|
|
]
|
|
}
|
|
},
|
|
{
|
|
type: "Feature",
|
|
id: "bridge-high",
|
|
properties: {
|
|
layer: "bridges",
|
|
name: "Hohe Brücke",
|
|
clearance_m: 4.2,
|
|
clearance_label: "H 4.2 m",
|
|
label: "Hohe Brücke H 4.2 m",
|
|
source: "OSM/Geofabrik"
|
|
},
|
|
geometry: {
|
|
type: "LineString",
|
|
coordinates: [
|
|
[7.18, 53.68],
|
|
[7.22, 53.68]
|
|
]
|
|
}
|
|
}
|
|
]
|
|
};
|
|
}
|
|
);
|
|
|
|
expect(report.bridgeReport?.severity).toBe("critical");
|
|
expect(report.bridgeReport?.tooLowCount).toBe(1);
|
|
expect(report.bridgeReport?.checkedCount).toBe(2);
|
|
expect(report.bridgeReport?.summary).toContain("Nicht passierbar");
|
|
expect(report.bridgeReport?.bridges[0]?.name).toBe("Niedrige Brücke");
|
|
});
|
|
|
|
it("marks critical weather when wind or wave thresholds are exceeded", () => {
|
|
const report = summarizeRouteWeather([
|
|
{
|
|
label: "Mitte",
|
|
coordinate: { lat: 53.5, lon: 7.1 },
|
|
forecast: forecast({ windSpeed: 28, waveHeightM: 1.1 })
|
|
}
|
|
]);
|
|
|
|
expect(report.severity).toBe("critical");
|
|
expect(report.summary).toContain("Kritische Bedingungen");
|
|
});
|
|
});
|
|
|
|
const routeFixture: RouteResult = {
|
|
geometry: {
|
|
type: "LineString",
|
|
coordinates: [
|
|
[7, 53.3],
|
|
[7.1, 53.5],
|
|
[7.2, 53.7]
|
|
]
|
|
},
|
|
distanceNm: 25,
|
|
eta: null,
|
|
warnings: [],
|
|
dataSources: [],
|
|
routingMode: "fairway"
|
|
};
|
|
|
|
function forecast(overrides: Partial<MarineForecast>): MarineForecast {
|
|
return {
|
|
waveHeightM: 0.4,
|
|
waveDirectionDeg: 280,
|
|
wavePeriodS: 4,
|
|
windSpeed: 10,
|
|
windDirectionDeg: 260,
|
|
weatherCode: 2,
|
|
temperatureC: 18,
|
|
source: "Test",
|
|
updatedAt: "2026-07-13T12:00:00.000Z",
|
|
...overrides
|
|
};
|
|
}
|