import { describe, it, expect, vi, beforeEach } from 'vitest'; const { amapPlaces } = vi.hoisted(() => ({ amapPlaces: { searchAmapPlaces: vi.fn(), autocompleteAmapPlaces: vi.fn(), getAmapPlaceDetails: vi.fn(), getAmapPlaceDetailsExpanded: vi.fn(), reverseAmapGeocode: vi.fn(), searchAmapPois: vi.fn(), resolveAmapUri: vi.fn(), }, })); vi.mock('../../../src/services/amap/amap.places', () => amapPlaces); const { serveFilePath } = vi.hoisted(() => ({ serveFilePath: vi.fn() })); vi.mock('../../../src/services/placePhotoCache', () => ({ serveFilePath })); import { MapsService } from '../../../src/nest/maps/maps.service'; import type { DatabaseService } from '../../../src/nest/database/database.service'; /** A DatabaseService stub whose get() returns the row the test wants. */ function makeDb(row?: { value: string }) { const get = vi.fn(() => row); const db = { get } as unknown as DatabaseService; return { db, get }; } function svc(row?: { value: string }) { return new MapsService(makeDb(row).db); } beforeEach(() => vi.clearAllMocks()); describe('MapsService', () => { describe('kill-switch settings reads', () => { it('reports a switch disabled when the stored value is exactly "false"', () => { expect(svc({ value: 'false' }).autocompleteDisabled()).toBe(true); expect(svc({ value: 'false' }).detailsDisabled()).toBe(true); expect(svc({ value: 'false' }).photosDisabled()).toBe(true); }); it('reports enabled when the value is "true"', () => { expect(svc({ value: 'true' }).autocompleteDisabled()).toBe(false); expect(svc({ value: 'true' }).detailsDisabled()).toBe(false); expect(svc({ value: 'true' }).photosDisabled()).toBe(false); }); it('reports enabled when the setting row is absent', () => { expect(svc(undefined).autocompleteDisabled()).toBe(false); expect(svc(undefined).detailsDisabled()).toBe(false); expect(svc(undefined).photosDisabled()).toBe(false); }); it('queries the matching app_settings key', () => { const { db, get } = makeDb({ value: 'true' }); const s = new MapsService(db); s.autocompleteDisabled(); expect(get).toHaveBeenCalledWith(expect.stringContaining('app_settings'), 'places_autocomplete_enabled'); s.detailsDisabled(); expect(get).toHaveBeenCalledWith(expect.any(String), 'places_details_enabled'); s.photosDisabled(); expect(get).toHaveBeenCalledWith(expect.any(String), 'places_photos_enabled'); }); }); describe('delegation to the AMap adapter', () => { it('search forwards userId, query, lang and bias', () => { amapPlaces.searchAmapPlaces.mockResolvedValue({ places: [], source: 'amap' }); const bias = { lat: 1, lng: 2, radius: 5 }; svc().search(3, 'berlin', 'de', bias); expect(amapPlaces.searchAmapPlaces).toHaveBeenCalledWith(expect.anything(), 'berlin', 'de', bias); }); it('search works without optional args', () => { svc().search(3, 'berlin'); expect(amapPlaces.searchAmapPlaces).toHaveBeenCalledWith(expect.anything(), 'berlin', undefined, undefined); }); it('autocomplete forwards through', () => { const bias = { low: { lat: 1, lng: 2 }, high: { lat: 3, lng: 4 } }; svc().autocomplete(3, 'be', 'en', bias); expect(amapPlaces.autocompleteAmapPlaces).toHaveBeenCalledWith(expect.anything(), 'be', 'en', bias); }); it('details forwards through', () => { svc().details(3, 'p1', 'de'); expect(amapPlaces.getAmapPlaceDetails).toHaveBeenCalledWith(expect.anything(), 'p1', 'de'); }); it('detailsExpanded forwards refresh through', () => { svc().detailsExpanded(3, 'p1', 'de', true); expect(amapPlaces.getAmapPlaceDetailsExpanded).toHaveBeenCalledWith(expect.anything(), 'p1', 'de', true); }); it('photo returns an empty AMap-compatible result without calling legacy providers', async () => { await expect(svc().photo(3, 'p1', 1.5, 2.5, 'Spot')).resolves.toEqual({ photoUrl: null }); }); it('reverse forwards through', () => { svc().reverse('1', '2', 'de'); expect(amapPlaces.reverseAmapGeocode).toHaveBeenCalledWith(expect.anything(), '1', '2', 'de'); }); it('resolveUrl forwards through', () => { svc().resolveUrl('https://uri.amap.com/marker?position=116.39747,39.908823'); expect(amapPlaces.resolveAmapUri).toHaveBeenCalledWith('https://uri.amap.com/marker?position=116.39747,39.908823'); }); it('pois forwards category and bbox through', () => { const bbox = { south: 1, west: 2, north: 3, east: 4 }; svc().pois('cafe', bbox); expect(amapPlaces.searchAmapPois).toHaveBeenCalledWith(expect.anything(), 'cafe', bbox); }); }); describe('photoBytesPath', () => { it('returns the cached file path from placePhotoCache', () => { serveFilePath.mockReturnValue('/cache/p1.jpg'); expect(svc().photoBytesPath('p1')).toBe('/cache/p1.jpg'); expect(serveFilePath).toHaveBeenCalledWith('p1'); }); it('returns null when nothing is cached', () => { serveFilePath.mockReturnValue(null); expect(svc().photoBytesPath('p1')).toBeNull(); }); }); });