mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(maps): repair the quirks preserved by the DI fold
This commit is contained in:
@@ -51,9 +51,10 @@ import { MapsService } from '../../../src/nest/maps/maps.service';
|
||||
// test registry builds a real MapsService over the mocked db proxy, so stub the
|
||||
// provider methods on the prototype (no auto-restore in the vitest config —
|
||||
// these survive across tests, exactly like the old module mock did).
|
||||
const getPlaceDetailsSpy = vi
|
||||
.spyOn(MapsService.prototype, 'getPlaceDetails')
|
||||
.mockResolvedValue({ name: 'Eiffel Tower', address: 'Paris' } as never);
|
||||
vi.spyOn(MapsService.prototype, 'getPlaceDetails').mockResolvedValue({
|
||||
name: 'Eiffel Tower',
|
||||
address: 'Paris',
|
||||
} as never);
|
||||
vi.spyOn(MapsService.prototype, 'reverseGeocode').mockResolvedValue({ name: 'Paris', address: 'France' });
|
||||
vi.spyOn(MapsService.prototype, 'resolveGoogleMapsUrl').mockResolvedValue({
|
||||
lat: 48.8566,
|
||||
@@ -311,17 +312,9 @@ describe('Tool: get_place_details', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns isError when service returns null', async () => {
|
||||
getPlaceDetailsSpy.mockResolvedValueOnce(null as never);
|
||||
const { user } = createUser(testDb);
|
||||
await withHarness(user.id, async (h) => {
|
||||
const result = await h.client.callTool({
|
||||
name: 'get_place_details',
|
||||
arguments: { placeId: 'nonexistent-place-id' },
|
||||
});
|
||||
expect(result.isError).toBe(true);
|
||||
});
|
||||
});
|
||||
// The former "isError when service returns null" case pinned a dead branch —
|
||||
// MapsService.getPlaceDetails throws or returns an object, never null — and
|
||||
// died with the guard in the fix(maps) quirk pass.
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Unit tests for the DI-native MapsService — MAPS-001 through MAPS-106.
|
||||
* Unit tests for the DI-native MapsService — MAPS-001 through MAPS-110.
|
||||
* Moved 1:1 from the legacy tests/unit/services/mapsService.test.ts when the
|
||||
* maps domain folded into src/nest/maps/ (case IDs preserved; the wrapper
|
||||
* suite's delegation cases died with the delegation itself — the kill-switch,
|
||||
@@ -572,6 +572,19 @@ describe('resolveGoogleMapsUrl coordinate extraction (ReDoS guards)', () => {
|
||||
const result = await svc.resolveGoogleMapsUrl('https://www.google.com/maps/@48.8606,2.3376,15z');
|
||||
expect(result.name).toBe('Louvre Museum');
|
||||
});
|
||||
|
||||
it('MAPS-110: a non-ok reverse-geocode answer degrades to URL-derived values instead of failing', async () => {
|
||||
// The coordinates are already extracted from the URL — a Nominatim 5xx must
|
||||
// not turn the resolution into a 400.
|
||||
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: false, status: 503 }));
|
||||
const result = await svc.resolveGoogleMapsUrl(
|
||||
'https://www.google.com/maps/place/Eiffel+Tower/@48.8584,2.2945,15z',
|
||||
);
|
||||
expect(result.lat).toBeCloseTo(48.8584, 3);
|
||||
expect(result.lng).toBeCloseTo(2.2945, 3);
|
||||
expect(result.name).toBe('Eiffel Tower');
|
||||
expect(result.address).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// ── searchNominatim (fetch-dependent) ────────────────────────────────────────
|
||||
@@ -622,6 +635,24 @@ describe('searchNominatim (fetch stubbed)', () => {
|
||||
const results = await svc.searchNominatim('London');
|
||||
expect((results[0] as any).name).toBe('London');
|
||||
});
|
||||
|
||||
it('MAPS-108: keeps "0" coordinates and nulls only unparseable ones', async () => {
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => [
|
||||
{ osm_type: 'node', osm_id: '3', lat: '0', lon: '0', name: 'Null Island' },
|
||||
{ osm_type: 'node', osm_id: '4', lat: 'not-a-number', lon: '', name: 'Broken' },
|
||||
],
|
||||
}),
|
||||
);
|
||||
const results = await svc.searchNominatim('null island');
|
||||
expect((results[0] as any).lat).toBe(0);
|
||||
expect((results[0] as any).lng).toBe(0);
|
||||
expect((results[1] as any).lat).toBeNull();
|
||||
expect((results[1] as any).lng).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// ── fetchOverpassDetails (fetch stubbed) ─────────────────────────────────────
|
||||
@@ -1068,6 +1099,23 @@ describe('searchPlaces (fetch stubbed)', () => {
|
||||
expect(place.website).toBeNull();
|
||||
expect(place.phone).toBeNull();
|
||||
});
|
||||
|
||||
it('MAPS-107: keeps 0 coordinates (equator / prime meridian) instead of nulling them', async () => {
|
||||
mockDbGet.mockReturnValueOnce({ maps_api_key: 'some-key' });
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
places: [{ id: 'gid-null-island', displayName: { text: 'Null Island' }, location: { latitude: 0, longitude: 0 } }],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
const result = await svc.searchPlaces(1, 'null island');
|
||||
const place = result.places[0] as any;
|
||||
expect(place.lat).toBe(0);
|
||||
expect(place.lng).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
// ── autocompletePlaces (fetch stubbed) ──────────────────────────────────────
|
||||
@@ -1410,6 +1458,23 @@ describe('getPlaceDetails (fetch stubbed)', () => {
|
||||
expect(String(fetchMock.mock.calls[2][0])).toContain('languageCode=de');
|
||||
});
|
||||
|
||||
it('MAPS-109: defaults to English when no language is given (the legacy de default was a leftover)', async () => {
|
||||
const fetchMock = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ id: 'ChIJ1', displayName: { text: 'X' } }),
|
||||
});
|
||||
mockDbGet.mockReturnValue({ maps_api_key: 'gkey' });
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
await svc.getPlaceDetails(1, 'ChIJ-nolang');
|
||||
expect(String(fetchMock.mock.calls[0][0])).toContain('languageCode=en');
|
||||
|
||||
// refresh=true skips the expanded-cache read (the blanket mockDbGet return
|
||||
// would otherwise be mistaken for a cached row).
|
||||
await svc.getPlaceDetailsExpanded(1, 'ChIJ-nolang-exp', undefined, true);
|
||||
expect(String(fetchMock.mock.calls[1][0])).toContain('languageCode=en');
|
||||
});
|
||||
|
||||
it('MAPS-041c: throws with status when Google API returns non-ok response', async () => {
|
||||
mockDbGet.mockReturnValueOnce({ maps_api_key: 'gkey' });
|
||||
vi.stubGlobal(
|
||||
|
||||
Reference in New Issue
Block a user