197 lines
5.7 KiB
TypeScript
197 lines
5.7 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest'
|
||
import type { SgsSdkApiProvider } from '@/data/providers/sgsSdkApiProvider'
|
||
import { SdkGuideRouteRepository } from '@/repositories/SdkGuideRouteRepository'
|
||
|
||
const floorId = '2065808921272119298'
|
||
|
||
const createProvider = () => {
|
||
const planRoute = vi.fn().mockResolvedValue({
|
||
distance: 24,
|
||
segments: [{
|
||
floorId,
|
||
floorName: '1.0层',
|
||
transferType: 'WALK',
|
||
pathPoints: [
|
||
{ x: 2, y: 0, z: 3 },
|
||
{ x: 8, y: 0, z: 9 }
|
||
]
|
||
}]
|
||
})
|
||
|
||
return {
|
||
getManifest: vi.fn().mockResolvedValue({
|
||
mapId: '1',
|
||
capabilities: { routePlanning: true },
|
||
floors: [{
|
||
floorId,
|
||
floorCode: 'L1',
|
||
floorName: '1.0层'
|
||
}]
|
||
}),
|
||
getMapDiagnostics: vi.fn().mockResolvedValue({
|
||
floors: [{
|
||
floorId,
|
||
floorCode: 'L1',
|
||
floorName: '1.0层',
|
||
status: 'OK',
|
||
routePlanningReady: true,
|
||
navigablePlaceCount: 1
|
||
}]
|
||
}),
|
||
getNavigablePlaces: vi.fn().mockResolvedValue([{
|
||
id: 'route-node-service-desk',
|
||
sourceId: 'poi-service-desk',
|
||
anchorId: 'anchor-service-desk',
|
||
name: '服务台 出入口',
|
||
floorId,
|
||
floorCode: 'L1',
|
||
floorName: '1.0层',
|
||
x: 2,
|
||
y: 0,
|
||
z: 3,
|
||
nodeId: 'start-node'
|
||
}]),
|
||
planRoute
|
||
} as unknown as SgsSdkApiProvider
|
||
}
|
||
|
||
describe('SdkGuideRouteRepository', () => {
|
||
it('使用 manifest 路线能力判断就绪,不调用全图 diagnostics 慢接口', async () => {
|
||
const provider = createProvider()
|
||
const repository = new SdkGuideRouteRepository(provider)
|
||
|
||
const readiness = await repository.getRouteReadiness()
|
||
|
||
expect(readiness.ready).toBe(true)
|
||
expect(provider.getManifest).toHaveBeenCalledTimes(1)
|
||
expect(provider.getMapDiagnostics).not.toHaveBeenCalled()
|
||
})
|
||
|
||
it('允许未预绑定路网的地图对象作为终点,并向后端提交其定位坐标', async () => {
|
||
const provider = createProvider()
|
||
const repository = new SdkGuideRouteRepository(provider)
|
||
|
||
const route = await repository.findRoute('anchor-service-desk', {
|
||
poiId: 'space-stamp',
|
||
name: '文创盖章打卡',
|
||
floorId,
|
||
floorLabel: '1F',
|
||
categoryLabel: '购物空间',
|
||
positionGltf: [61.8, 1.2, 45.3]
|
||
})
|
||
|
||
expect(provider.planRoute).toHaveBeenCalledWith(expect.objectContaining({
|
||
startNodeId: 'start-node',
|
||
endFloorId: floorId,
|
||
endX: 61.8,
|
||
endY: 45.3,
|
||
endNodeId: null,
|
||
verticalTransferPolicy: 'PREFER_ELEVATOR_ESCALATOR'
|
||
}))
|
||
expect(route.end.name).toBe('文创盖章打卡')
|
||
expect(route.distanceMeters).toBe(24)
|
||
})
|
||
|
||
it('保留业务对象 ID,使地图标签可精确回连到对应接入点', async () => {
|
||
const repository = new SdkGuideRouteRepository(createProvider())
|
||
|
||
const targets = await repository.listRouteTargets()
|
||
|
||
expect(targets).toEqual([expect.objectContaining({
|
||
routeTargetId: 'anchor-service-desk',
|
||
poiId: 'route-node-service-desk',
|
||
sourceId: 'poi-service-desk',
|
||
routeNodeId: 'start-node'
|
||
})])
|
||
})
|
||
|
||
it('路线起点选择只读取当前楼层目标,不扫描全馆楼层', async () => {
|
||
const provider = createProvider()
|
||
const repository = new SdkGuideRouteRepository(provider)
|
||
|
||
const targets = await repository.listRouteTargetsForFloor(floorId)
|
||
|
||
expect(targets).toHaveLength(1)
|
||
expect(provider.getNavigablePlaces).toHaveBeenCalledTimes(1)
|
||
expect(provider.getNavigablePlaces).toHaveBeenCalledWith(floorId)
|
||
expect(provider.getMapDiagnostics).not.toHaveBeenCalled()
|
||
})
|
||
|
||
it('为只有 nodeId 的路网节点生成稳定兜底身份', async () => {
|
||
const provider = createProvider()
|
||
provider.getNavigablePlaces = vi.fn().mockResolvedValue([{
|
||
id: null,
|
||
sourceId: 'space-42',
|
||
name: '球幕影院节点',
|
||
floorId,
|
||
floorCode: 'L1',
|
||
floorName: '1.0层',
|
||
x: 1,
|
||
y: 0,
|
||
z: 2,
|
||
nodeId: 'node-only'
|
||
}])
|
||
const repository = new SdkGuideRouteRepository(provider)
|
||
|
||
const targets = await repository.listRouteTargets()
|
||
|
||
expect(targets).toEqual([expect.objectContaining({
|
||
routeTargetId: 'node-node-only:node-only',
|
||
poiId: 'node-node-only',
|
||
sourceId: 'space-42',
|
||
routeNodeId: 'node-only'
|
||
})])
|
||
})
|
||
|
||
it('允许地图对象坐标作为起点,由路线服务负责吸附到正式路网', async () => {
|
||
const provider = createProvider()
|
||
const repository = new SdkGuideRouteRepository(provider)
|
||
|
||
await repository.findRoute({
|
||
poiId: 'hall-unanchored',
|
||
name: '球幕影院',
|
||
floorId,
|
||
floorLabel: '1F',
|
||
positionGltf: [1.7, 3.3, -5.6]
|
||
}, {
|
||
poiId: 'space-stamp',
|
||
name: '文创盖章打卡',
|
||
floorId,
|
||
floorLabel: '1F',
|
||
positionGltf: [61.8, 1.2, 45.3]
|
||
})
|
||
|
||
expect(provider.planRoute).toHaveBeenCalledWith(expect.objectContaining({
|
||
startX: 1.7,
|
||
startY: -5.6,
|
||
startNodeId: null,
|
||
endNodeId: null
|
||
}))
|
||
})
|
||
|
||
it('将路线接口异常转换为不暴露内部请求细节的用户错误', async () => {
|
||
const provider = createProvider()
|
||
provider.planRoute = vi.fn().mockRejectedValue(new Error(
|
||
'SGS 数据接口请求失败: 500 /app-api/gis/sdk/routes/plan body="internal response"'
|
||
))
|
||
const repository = new SdkGuideRouteRepository(provider)
|
||
|
||
await expect(repository.findRoute({
|
||
poiId: 'start-poi',
|
||
name: '起点',
|
||
floorId,
|
||
floorLabel: '1F',
|
||
positionGltf: [1, 0, 2]
|
||
}, {
|
||
poiId: 'end-poi',
|
||
name: '终点',
|
||
floorId,
|
||
floorLabel: '1F',
|
||
positionGltf: [3, 0, 4]
|
||
})).rejects.toMatchObject({
|
||
code: 'ROUTE_SERVICE_UNAVAILABLE',
|
||
message: '路线服务暂时不可用,请稍后重试'
|
||
})
|
||
})
|
||
})
|