Files
TREK/server/tests/unit/services/timezoneService.test.ts
T
jubnl 91f88270cc feat(mcp): expose public transit planning tools (#1558)
* feat(mcp): add public transit planning tools

* refactor(transit): reuse local time conversion

* fix(mcp): harden transit journey validation

* refactor(transit): centralize itinerary processing

* fix(mcp): return the transit itineraries the provider actually offers

search_transit_routes validated each itinerary leg's mode against
SCHEDULED_TRANSIT_MODES, but that constant is the request-side filter
whitelist — the modes a caller may ask for — not the modes MOTIS can return.
Its default TRANSIT mode expands to TRAM,FERRY,AIRPLANE,BUS,COACH,RAIL,ODM,
RIDE_SHARING,FUNICULAR,AERIAL_LIFT,OTHER, and street legs can be BIKE/CAR/
RENTAL. Any itinerary carrying one of those failed the parse and was dropped
by the flatMap, so the tool reported fewer routes than exist — or none at all.
Against the live provider, Trondheim → Ålesund returns 5 itineraries and 3 of
them contain an AIRPLANE leg, so the tool silently discarded them; the web app
shows all 5, because it treats mode as a free string and renders anything
non-WALK as a transit leg.

Accept any mode token on a leg and keep the existing "at least one non-WALK
leg" rule as the real gate, which restores parity with the web app. Everything
downstream keeps its mode !== 'WALK' semantics, so a journey created over MCP
is identical to one created in the app.

Dropping an itinerary is still possible when provider data fails the remaining
consistency rules, but it is indistinguishable from "no routes exist" — so
search_transit_routes now reports a `dropped` count alongside the results.

---------

Co-authored-by: Uzini <43294422+Uziniii@users.noreply.github.com>
2026-07-14 20:25:30 +02:00

18 lines
768 B
TypeScript

import { localParts, resolveTimeZone } from '../../../src/services/timezoneService';
import { describe, expect, it } from 'vitest';
describe('timezoneService', () => {
it('resolves valid coordinates and rejects missing or invalid values', () => {
expect(resolveTimeZone(35.6762, 139.6503)).toBe('Asia/Tokyo');
expect(resolveTimeZone(null, 139.6503)).toBeNull();
expect(resolveTimeZone(91, 139.6503)).toBeNull();
});
it('converts instants to local date and time', () => {
expect(localParts('2026-12-03T00:00:00Z', 'Asia/Tokyo')).toEqual({ date: '2026-12-03', time: '09:00' });
expect(localParts('invalid', 'Asia/Tokyo')).toEqual({ date: null, time: null });
expect(localParts(null, null)).toEqual({ date: null, time: null });
});
});