import { afterEach, describe, expect, it, vi } from 'vitest' import type { SgsSdkApiProvider, SgsSdkManifestPayload } from '@/data/providers/sgsSdkApiProvider' import { SgsSdkGuideRepository } from '@/repositories/GuideRepository' import { toMuseumPoiFromSgs } from '@/data/adapters/sgsSdkGuideAdapter' const manifest: SgsSdkManifestPayload = { mapId: 'museum', mapName: '深圳自然博物馆', floors: [ { floorId: '101', floorCode: 'L1', floorName: '1F', sortOrder: 1 } ] } const floorPoi = { id: 'poi-toilet', name: '一层卫生间', type: 'toilet', typeName: '卫生间', floorId: '101', position: { x: 4, y: 12, z: 8 } } const theaterSpace = { id: 'space-theater', name: '球幕影院', type: 'theater', floorId: '101', boundaryWkt: 'POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))' } const createProvider = (overrides: Partial = {}) => ({ getManifest: vi.fn().mockResolvedValue(manifest), getFloorPois: vi.fn().mockResolvedValue([floorPoi]), getFloorBusinessPois: vi.fn().mockResolvedValue([]), getFloorSpaces: vi.fn().mockResolvedValue([theaterSpace]), getNavigablePlaces: vi.fn().mockResolvedValue([]), ...overrides }) as unknown as SgsSdkApiProvider afterEach(() => { vi.restoreAllMocks() }) describe('SgsSdkGuideRepository 点位搜索', () => { it('源数据仅有 floorCode 时回填 manifest 的稳定 floorId', () => { const poi = toMuseumPoiFromSgs({ ...floorPoi, id: 'poi-floor-code-only', floorId: undefined, floorCode: 'L1' }, manifest.floors) expect(poi.floorId).toBe('101') expect(poi.floorLabel).toBe('1F') }) it('SDK null 坐标不会被误转换为模型原点', () => { const poi = toMuseumPoiFromSgs({ ...floorPoi, id: 'poi-with-null-position', position: { x: null, y: null, z: null } }, manifest.floors) expect(poi.positionGltf).toBeUndefined() }) it('合并普通点位与空间点位,并保留影院真实分类和楼层高度', async () => { const repository = new SgsSdkGuideRepository(createProvider()) const results = await repository.searchPois() const theaterResults = results.filter((poi) => poi.name === '球幕影院') expect(theaterResults).toHaveLength(1) expect(theaterResults[0]).toMatchObject({ id: 'space-space-theater', floorId: '101', floorLabel: '1F', primaryCategory: { id: 'space_theater' }, positionGltf: [5, 12, 5] }) expect(results.some((poi) => poi.id === 'poi-toilet')).toBe(true) }) it('部分来源失败时继续返回可用点位,并在后续搜索重试失败来源', async () => { const getFloorSpaces = vi.fn() .mockRejectedValueOnce(new Error('spaces offline')) .mockRejectedValueOnce(new Error('spaces offline')) .mockResolvedValue([theaterSpace]) const provider = createProvider({ getFloorSpaces }) const repository = new SgsSdkGuideRepository(provider) vi.spyOn(console, 'warn').mockImplementation(() => undefined) const firstResults = await repository.searchPois() expect(firstResults.map((poi) => poi.id)).toEqual(['poi-toilet']) const secondResults = await repository.searchPois() expect(secondResults.some((poi) => poi.id === 'space-space-theater')).toBe(true) expect(getFloorSpaces).toHaveBeenCalledTimes(4) }) it('补充来源失败不会阻止核心点位结果缓存', async () => { const getFloorPois = vi.fn().mockResolvedValue([floorPoi]) const getFloorSpaces = vi.fn().mockResolvedValue([theaterSpace]) const getFloorBusinessPois = vi.fn().mockRejectedValue(new Error('business offline')) const provider = createProvider({ getFloorPois, getFloorSpaces, getFloorBusinessPois }) const repository = new SgsSdkGuideRepository(provider) vi.spyOn(console, 'warn').mockImplementation(() => undefined) await repository.listPois() await repository.listPois() expect(getFloorPois).toHaveBeenCalledTimes(1) expect(getFloorSpaces).toHaveBeenCalledTimes(1) expect(getFloorBusinessPois).toHaveBeenCalledTimes(1) }) it('楼层高度来源短暂失败时不缓存缺失的空间点位', async () => { const getFloorPois = vi.fn() .mockRejectedValueOnce(new Error('pois offline')) .mockRejectedValueOnce(new Error('pois offline')) .mockResolvedValue([floorPoi]) const provider = createProvider({ getFloorPois }) const repository = new SgsSdkGuideRepository(provider) vi.spyOn(console, 'warn').mockImplementation(() => undefined) await expect(repository.searchPois()).rejects.toThrow('点位数据加载失败,请检查网络后重试') const recoveredResults = await repository.searchPois() expect(recoveredResults.find((poi) => poi.id === 'space-space-theater')?.positionGltf) .toEqual([5, 12, 5]) expect(getFloorPois).toHaveBeenCalledTimes(4) }) it('所有核心楼层与空间请求失败时返回明确的可重试错误', async () => { const provider = createProvider({ getFloorPois: vi.fn().mockRejectedValue(new Error('pois offline')), getFloorSpaces: vi.fn().mockRejectedValue(new Error('spaces offline')) }) const repository = new SgsSdkGuideRepository(provider) vi.spyOn(console, 'warn').mockImplementation(() => undefined) await expect(repository.searchPois()).rejects.toThrow('点位数据加载失败,请检查网络后重试') }) })