Skip to content

Commit 0c8dbeb

Browse files
committed
fix: transit and other bug fixes
1 parent 332b289 commit 0c8dbeb

31 files changed

Lines changed: 496 additions & 109 deletions

apps/api/src/routes/__tests__/transit.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ const MOCK_STOP = {
112112
lat: 52.525,
113113
lng: 13.369,
114114
modes: ["rail"],
115-
provider: "transitous",
115+
provider: "mo",
116116
};
117117

118118
const MOCK_DEPARTURE = {
@@ -123,7 +123,7 @@ const MOCK_DEPARTURE = {
123123
headsign: "Hamburg Hbf",
124124
canceled: false,
125125
route: { id: "r:1", shortName: "ICE 1", longName: "ICE Berlin-Hamburg", mode: "rail" },
126-
providers: ["transitous"],
126+
providers: ["mo"],
127127
};
128128

129129
const MOCK_ROUTE = {
@@ -140,7 +140,7 @@ const MOCK_ALERT = {
140140
title: "Delay on ICE 1",
141141
description: "Due to construction work",
142142
severity: "warning",
143-
providers: ["transitous"],
143+
providers: ["mo"],
144144
affectedRouteIds: [],
145145
affectedStopIds: [],
146146
};
@@ -160,7 +160,7 @@ const MOCK_PLAN = {
160160
const MOCK_VEHICLE_JOURNEY = {
161161
id: "db:trip_123",
162162
name: "ICE 1",
163-
provider: "transitous",
163+
provider: "mo",
164164
stops: [],
165165
};
166166

apps/api/src/services/__tests__/motis-geocoding.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ describe("autocomplete", () => {
128128
lat: 52.52,
129129
lng: 13.37,
130130
modes: ["bus", "rail"],
131-
provider: "transitous",
131+
provider: "mo",
132132
});
133133
expect(results[0].rawCategory).toBe("train_station");
134134
});

apps/api/src/services/integration-health.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,15 @@ export async function executeIntegrationHealthCheck(
118118
};
119119
}
120120

121-
// URL template interpolation
121+
// URL template interpolation (fall back to static url if template produces a broken URL)
122122
let checkUrl: string;
123123
if (hc.urlTemplate) {
124-
checkUrl = (hc.urlTemplate as string).replace(
124+
const resolved = (hc.urlTemplate as string).replace(
125125
/\$\{(\w+)\}/g,
126126
(_, key: string) => process.env[key] ?? "",
127127
);
128+
const hasHost = /^https?:\/\/[^/]/.test(resolved);
129+
checkUrl = hasHost ? resolved : ((hc.url as string | undefined) ?? resolved);
128130
} else if (hc.url) {
129131
checkUrl = hc.url as string;
130132
} else {

apps/api/src/services/transit/__tests__/dedup.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ describe("deduplicateStops", () => {
210210
const stop2 = makeStop({
211211
id: "mo:2",
212212
name: "Berlin Hbf",
213-
provider: "transitous",
213+
provider: "mo",
214214
lat: 52.5251,
215215
lng: 13.3691,
216216
});
@@ -262,7 +262,7 @@ describe("deduplicateStops", () => {
262262
makeStop({
263263
id: "mo:1",
264264
name: "Berlin Hbf",
265-
provider: "transitous",
265+
provider: "mo",
266266
lat: 52.525,
267267
lng: 13.369,
268268
}),
@@ -299,7 +299,7 @@ describe("deduplicateStops", () => {
299299
makeStop({
300300
id: "mo:1",
301301
name: "Berlin Hauptbahnhof",
302-
provider: "transitous",
302+
provider: "mo",
303303
lat: 52.5251,
304304
lng: 13.3691,
305305
}),
@@ -314,7 +314,7 @@ describe("deduplicateStops", () => {
314314
makeStop({
315315
id: "mo:2",
316316
name: "Friedrichstraße",
317-
provider: "transitous",
317+
provider: "mo",
318318
lat: 52.5206,
319319
lng: 13.3868,
320320
}),
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import type { TransitProvider } from "@openmapx/core";
2+
import { describe, expect, it, vi } from "vitest";
3+
import { TransitOrchestrator } from "../orchestrator";
4+
5+
function makeProvider(overrides: Partial<TransitProvider>): TransitProvider {
6+
return {
7+
id: "test-provider",
8+
prefix: "tp:",
9+
coverage: { bbox: [-180, -90, 180, 90] },
10+
priority: 1,
11+
capabilities: {
12+
stops: true,
13+
departures: true,
14+
arrivals: true,
15+
search: true,
16+
tripPlanning: true,
17+
alerts: true,
18+
vehicles: true,
19+
},
20+
...overrides,
21+
};
22+
}
23+
24+
describe("TransitOrchestrator.getRoutesForStop", () => {
25+
it("derives routes from departures when provider route lookup is missing", async () => {
26+
const departures = vi.fn().mockResolvedValue([
27+
{
28+
tripId: "trip-1",
29+
route: {
30+
id: "tp:r-1",
31+
shortName: "Bus 1",
32+
longName: "Bus 1",
33+
mode: "bus",
34+
color: "00AA00",
35+
},
36+
headsign: "Downtown",
37+
scheduledAt: "2026-04-06T10:00:00Z",
38+
},
39+
{
40+
tripId: "trip-2",
41+
route: {
42+
id: "tp:r-2",
43+
shortName: "12345",
44+
longName: "Trip Number",
45+
mode: "bus",
46+
color: "AA0000",
47+
},
48+
headsign: "Somewhere",
49+
scheduledAt: "2026-04-06T10:05:00Z",
50+
},
51+
]);
52+
53+
const orchestrator = new TransitOrchestrator();
54+
orchestrator.register(
55+
makeProvider({
56+
id: "test-provider",
57+
prefix: "tp:",
58+
getDepartures: departures,
59+
}),
60+
);
61+
62+
const routes = await orchestrator.getRoutesForStop("tp:stop-1");
63+
64+
expect(departures).toHaveBeenCalledWith("tp:stop-1", 720);
65+
expect(routes).toHaveLength(1);
66+
expect(routes[0]).toMatchObject({
67+
id: "tp:r-1",
68+
shortName: "Bus 1",
69+
longName: "Bus 1",
70+
mode: "bus",
71+
color: "00AA00",
72+
});
73+
});
74+
75+
it("uses provider route lookup when available and non-empty", async () => {
76+
const getRoutesForStop = vi.fn().mockResolvedValue([
77+
{
78+
id: "tp:r-1",
79+
shortName: "U1",
80+
longName: "Main Line",
81+
mode: "subway",
82+
operatorName: "",
83+
},
84+
]);
85+
const departures = vi.fn().mockResolvedValue([]);
86+
87+
const orchestrator = new TransitOrchestrator();
88+
orchestrator.register(
89+
makeProvider({
90+
id: "test-provider",
91+
prefix: "tp:",
92+
getRoutesForStop,
93+
getDepartures: departures,
94+
}),
95+
);
96+
97+
const routes = await orchestrator.getRoutesForStop("tp:stop-1");
98+
99+
expect(routes).toHaveLength(1);
100+
expect(getRoutesForStop).toHaveBeenCalledWith("tp:stop-1");
101+
expect(departures).not.toHaveBeenCalled();
102+
});
103+
});
104+
105+
describe("TransitOrchestrator.getRouteStops", () => {
106+
it("derives stop sequence from departure trip detail when provider route-stops lookup is missing", async () => {
107+
const departures = vi.fn().mockResolvedValue([
108+
{
109+
tripId: "tp:trip-1",
110+
route: {
111+
id: "tp:r-1",
112+
shortName: "Bus 1",
113+
longName: "Bus 1",
114+
mode: "bus",
115+
color: "00AA00",
116+
},
117+
headsign: "Downtown",
118+
scheduledAt: "2026-04-06T10:00:00Z",
119+
},
120+
]);
121+
122+
const getVehicleJourney = vi.fn().mockResolvedValue({
123+
id: "tp:trip-1",
124+
name: "Bus 1",
125+
provider: "test-provider",
126+
stops: [
127+
{ stopId: "tp:s1", name: "First", lat: 50.1, lng: 6.1, platform: "1" },
128+
{ stopId: "tp:s2", name: "Second", lat: 50.2, lng: 6.2, platform: "2" },
129+
],
130+
});
131+
132+
const orchestrator = new TransitOrchestrator();
133+
orchestrator.register(
134+
makeProvider({
135+
id: "test-provider",
136+
prefix: "tp:",
137+
getDepartures: departures,
138+
getVehicleJourney,
139+
}),
140+
);
141+
142+
const stops = await orchestrator.getRouteStops("tp:r-1", "tp:hint-stop");
143+
144+
expect(departures).toHaveBeenCalledWith("tp:hint-stop", 720);
145+
expect(getVehicleJourney).toHaveBeenCalledWith("tp:trip-1");
146+
expect(stops).toHaveLength(2);
147+
expect(stops[0]).toMatchObject({
148+
id: "tp:s1",
149+
name: "First",
150+
lat: 50.1,
151+
lng: 6.1,
152+
platformCode: "1",
153+
});
154+
});
155+
156+
it("uses provider route-stops endpoint when available and non-empty", async () => {
157+
const getRouteStops = vi.fn().mockResolvedValue([
158+
{
159+
id: "tp:s1",
160+
name: "First",
161+
lat: 50.1,
162+
lng: 6.1,
163+
modes: ["bus"],
164+
provider: "test-provider",
165+
},
166+
]);
167+
const departures = vi.fn().mockResolvedValue([]);
168+
const getVehicleJourney = vi.fn().mockResolvedValue(null);
169+
170+
const orchestrator = new TransitOrchestrator();
171+
orchestrator.register(
172+
makeProvider({
173+
id: "test-provider",
174+
prefix: "tp:",
175+
getRouteStops,
176+
getDepartures: departures,
177+
getVehicleJourney,
178+
}),
179+
);
180+
181+
const stops = await orchestrator.getRouteStops("tp:r-1", "tp:hint-stop");
182+
183+
expect(stops).toHaveLength(1);
184+
expect(getRouteStops).toHaveBeenCalledWith("tp:r-1", "tp:hint-stop");
185+
expect(departures).not.toHaveBeenCalled();
186+
expect(getVehicleJourney).not.toHaveBeenCalled();
187+
});
188+
});

0 commit comments

Comments
 (0)