| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- import assert from 'node:assert/strict'
- import { readFileSync } from 'node:fs'
- import vm from 'node:vm'
- import * as Cesium from 'cesium'
- import { toPipeNetwork, filterPipeNetwork } from '../../../src/modules/cockpit/domain/pipeNetwork.js'
- import { planModeLayers } from '../../../src/modules/cockpit/domain/modeLayerPlan.js'
- import { planViewMode } from '../../../src/modules/cockpit/domain/viewModes.js'
- import { syncPipeLayer, clearPipeLayer } from '../../../src/modules/cockpit/services/cesiumPipeLayer.js'
- // Offline only: real domain functions and Cesium entities; GPU picking returns controlled hits.
- // No network, server, browser, file mutations, or in-place mutation testing.
- const source = readFileSync(new URL('../../../src/modules/cockpit/services/cesiumEngine.js', import.meta.url), 'utf8')
- const code = source.replace(/^import\s+[^]*?from\s+['"][^'"\n]+['"][ \t]*$/gm, '')
- .replace(/^import\s+['"][^'"\n]+['"][ \t]*$/gm, '')
- .replace(/^export /gm, '')
- const context = vm.createContext({ ...Cesium, CesiumMath: Cesium.Math })
- vm.runInContext(`${code}\nglobalThis.engine = { pickPointMarkerAt, pickDeviceMarkerAt };`, context)
- const engine = context.engine
- let failed = 0
- let passed = 0
- async function check(name, action) {
- try { await action(); passed += 1; console.log(`PASS ${name}`) }
- catch (error) { failed += 1; console.error(`FAIL ${name}: ${error.message}`) }
- }
- const path = [[110.39, 28.44], [110.395, 28.445], [110.40, 28.45]]
- function feature(coordinates = path, properties = {}) {
- return { type: 'Feature', id: 'P-1', geometry: { type: 'LineString', coordinates }, properties: {
- code: 'P-1', industry: 'GAS', diameter: '400', material: '钢',
- coordinateValidation: { renderable: true }, ...properties,
- } }
- }
- const network = toPipeNetwork({ features: [feature()] })
- await check('完整路径、源管径和可选材质原样映射', () => {
- assert.deepEqual(network.pipes[0].positions, path)
- assert.equal(network.pipes[0].diameter, 400)
- assert.equal(network.pipes[0].material, '钢')
- const missing = toPipeNetwork({ features: [feature(path, { diameter: null, material: null })] }).pipes[0]
- assert.equal(missing.diameter, null)
- assert.equal(missing.material, '')
- })
- await check('任一坏顶点整条不上图,保留清单,不跨点重连', () => {
- for (const bad of [null, [], ['110.395', 28.445], [NaN, 28.445], [110.395, Infinity]]) {
- const result = toPipeNetwork({ features: [feature([path[0], bad, path[2]])] })
- assert.equal(result.pipes.length, 0)
- assert.equal(result.blocked.length, 1)
- assert.equal(result.blocked[0].code, 'P-1')
- assert.equal(result.blocked[0].reasonText, '无地图位置')
- }
- })
- await check('后端未准入即不上图,不用合法结构替代后端校核', () => {
- const result = toPipeNetwork({ features: [feature(path, { coordinateValidation: { renderable: false, reason: 'UNVERIFIED_COORDINATE' } })] })
- assert.equal(result.pipes.length, 0)
- assert.equal(result.blocked[0].reasonText, '坐标未经校验')
- })
- await check('行业筛选同样裁剪无地图位置清单,不修改原始网络', () => {
- const all = toPipeNetwork({ features: [feature(), feature(path, { code: 'W', industry: 'WATER_SUPPLY' }), feature([path[0]], { code: 'X', industry: 'WATER_SUPPLY' })] })
- assert.deepEqual(filterPipeNetwork(all, { industry: 'gas' }).pipes.map((p) => p.code), ['P-1'])
- assert.equal(filterPipeNetwork(all, { industry: 'gas' }).blocked.length, 0)
- const water = filterPipeNetwork(all, { industry: 'water' })
- assert.deepEqual(water.pipes.map((p) => p.code), ['W'])
- assert.equal(water.blocked.length, 1)
- assert.equal(filterPipeNetwork(all, { industry: 'all' }).pipes.length, 2)
- assert.equal(all.blocked.length, 1)
- })
- await check('四种模式均保留管线,引导态隐藏,切视图不裁管线', () => {
- for (const mode of ['overview', 'infrastructure', 'monitoring', 'warningResponse']) {
- const input = { mode, entered: true, objects: [], facilities: [], pipelines: network.pipes }
- assert.equal(planModeLayers(input).pipelines, network.pipes)
- assert.equal(planModeLayers({ ...input, entered: false }).pipelines.length, 0)
- }
- for (const mode of ['ground', 'pipeline']) {
- const plan = planViewMode(mode)
- assert.ok(plan.terrainAlpha > 0 && plan.tilesetAlpha > 0)
- assert.equal(plan.terrainAlpha < 1, mode === 'pipeline')
- assert.equal(plan.tilesetAlpha < 1, mode === 'pipeline')
- }
- })
- const pipes = new Cesium.CustomDataSource('cockpit-pipelines')
- const devices = new Cesium.CustomDataSource('cockpit-associated-device-markers')
- const pipe = pipes.entities.add({ id: 'pipe:P-1', properties: { kind: 'pipe', code: 'P-1' } })
- const component = pipes.entities.add({ id: 'component:C-1', properties: { kind: 'component', code: 'C-1' } })
- const point = new Cesium.Entity({ id: 'point:M', properties: { pointCode: 'M', pointKind: 'monitoring-point', pointStatus: 'NORMAL' } })
- const device = new Cesium.Entity({ id: 'device:D', properties: { deviceCode: 'D', monitoringPointCode: 'M', deviceStatus: 'NORMAL' } })
- const model = { kind: 'pipe-model-batch', key: 'batch', instances: ['pipe:P-1'] }
- const building = { id: 'building' }
- let hits = []
- let drills = 0
- let pickedPosition = null
- const viewer = {
- dataSources: { getByName: (name) => name === pipes.name ? [pipes] : name === devices.name ? [devices] : [] },
- canvas: { getBoundingClientRect: () => ({ left: 20, top: 30 }) },
- scene: {
- pick(position) { pickedPosition = position; return hits[0] },
- drillPick() { drills += 1; return hits },
- },
- }
- await check('普通点选不触发多层拾取,窗口坐标转画布坐标', () => {
- hits = [{ id: point }]; drills = 0
- assert.equal(engine.pickPointMarkerAt(viewer, 120, 230).code, 'M')
- assert.equal(drills, 0)
- assert.equal(pickedPosition.x, 100)
- assert.equal(pickedPosition.y, 200)
- })
- await check('管线、构件、模型都不能吞掉监测点', () => {
- for (const foreground of [pipe, component, model]) {
- hits = [{ id: foreground }, { id: point }]
- assert.equal(engine.pickPointMarkerAt(viewer, 120, 230).code, 'M')
- }
- })
- await check('管线遮挡设备时仍回设备身份与父点位', () => {
- hits = [{ id: model }, { id: pipe }, { id: device }]
- const hit = engine.pickDeviceMarkerAt(viewer, 120, 230)
- assert.equal(hit.code, 'D')
- assert.equal(hit.pointCode, 'M')
- })
- await check('不穿透建筑/其它对象,也不把单独管线当监测点或设备', () => {
- for (const list of [[{ id: building }, { id: point }], [{ id: pipe }, { id: building }, { id: point }], [{ id: model }]]) {
- hits = list
- assert.equal(engine.pickPointMarkerAt(viewer, 120, 230), null)
- assert.equal(engine.pickDeviceMarkerAt(viewer, 120, 230), null)
- }
- })
- await check('名字相似但不属于管线源的对象不能被穿透', () => {
- hits = [{ id: new Cesium.Entity({ id: 'pipe:foreign' }) }, { id: point }]
- assert.equal(engine.pickPointMarkerAt(viewer, 120, 230), null)
- })
- await check('无多层拾取能力时不伪造命中', () => {
- hits = [{ id: pipe }, { id: point }]
- const limited = { ...viewer, scene: { pick: viewer.scene.pick } }
- assert.equal(engine.pickPointMarkerAt(limited, 120, 230), null)
- })
- await check('真实Cesium折线写入且相同输入不重赋值,筛空后移除', async () => {
- const target = { dataSources: new Cesium.DataSourceCollection() }
- const first = await syncPipeLayer(target, network)
- assert.equal(first.pipes, 1)
- const entity = target.dataSources.getByName(pipes.name)[0].entities.getById('pipe:P-1')
- assert.equal(entity.polyline.positions.getValue().length, path.length)
- const positions = entity.polyline.positions
- const second = await syncPipeLayer(target, network)
- assert.equal(second.unchanged, 1)
- assert.equal(entity.polyline.positions, positions)
- const empty = await syncPipeLayer(target, { pipes: [], components: [] })
- assert.equal(empty.removed, 1)
- await clearPipeLayer(target)
- })
- console.log(`T22: ${passed} passed, ${failed} failed`)
- process.exitCode = failed ? 1 : 0
|