// 锚点迁移对抗式验证(验收清单第 2 项): // 1. 新断言(verticalOrigin=BOTTOM + 尺寸随母版比例)在「未迁移的旧实现」上必须失败; // 2. 旧断言(CENTER + 方形 18×18)在「已迁移的新实现」上也必须失败; // 两向都能失败 → 迁移是真实的,不是「改完还是绿的」。 import { readFileSync } from 'node:fs' import vm from 'node:vm' // —— 加载引擎模块的精简副本(跳过 cesium import;用 stub 顶住) —— const src = readFileSync('src/modules/cockpit/services/cesiumEngine.js', 'utf8') // 砍掉静态 import(stub 由沙箱注入) .replace(/^import[\s\S]*?from\s+['"][^'"]+['"];?\s*$/gm, '') // 砍掉 Cesium 常量解构(沙箱注入) .replace(/^const\s*\{[\s\S]*?\}\s*=\s*Cesium;?/m, '') function loadEngine() { const created = [] const ctx = vm.createContext({ // Cesium stub(含 BOTTOM,供新锚点断言) VerticalOrigin: { CENTER: 'CENTER', BOTTOM: 'BOTTOM' }, HeightReference: { CLAMP_TO_GROUND: 'CLAMP_TO_GROUND' }, Cartesian2: class { constructor(x, y) { this.x = x; this.y = y } }, Cartesian3: { fromDegrees: (a, b) => ({ lon: a, lat: b }) }, Color: { WHITE: { withAlpha: (a) => ({ alpha: a }) }, BLACK: { withAlpha: (a) => ({ alpha: a }) } }, LabelStyle: { FILL_AND_OUTLINE: 0 }, DistanceDisplayCondition: class {}, // 引擎模块引用的其他域模块(按需最小 stub;本验证只走 syncPointMarkers) EMPHASIS_STYLE: {}, shouldEmphasize: () => false, MAP_LABEL_STYLE: {}, mapLabelText: () => ({ text: '', font: '', pixelOffset: {} }), createTilesetLoadingController: () => ({}), tilesetOptions: {}, createRegionLoadingController: () => ({}), contentAdmission: {}, removeSceneTileset: () => {}, DEVICE_EFFECT: {}, RING_LAYERS: 0, blinkStrengthAt: () => 0, glowScaleAt: () => 0, glowStrengthAt: () => 0, hasGlow: () => false, pulseStrengthAt: () => 0, ringEntityCount: () => 0, ringFrameAt: () => 0, spinAngleAt: () => 0, GRADE_DEFAULTS: {}, parseHexColor: () => null, planToDataset: (plan) => ({ markerMaster: plan.master, markerInner: plan.inner.enabled ? plan.inner.icon : '', markerAnchor: `${plan.anchor.x},${plan.anchor.y}`, markerDesaturate: plan.desaturate ? 'yes' : 'no' }), renderMarker: async () => 'data:,', // 引擎内部可能引用的 DOM/全局 console, setTimeout, clearTimeout, Math, JSON, Number, Object, Array, Map, Set, WeakMap, Promise, document: undefined, __created: created, pointMarkerSource: () => ({ entities: { removeAll() {}, remove() {}, add(e) { created.push(e); return e }, values: [] } }), deviceMarkerSource: () => ({ entities: { removeAll() {}, add(e) { created.push(e); return e }, values: [] } }), deviceBaseStyle: { clear() {}, set() {}, get() { return null } }, stopDeviceEffects() {}, applyDeviceEffects() {}, ringEntities: new Map(), blinkEntities: new Map(), spinEntities: new Map(), }) try { vm.runInContext(src + '\nglobalThis.__engine = { syncPointMarkers };', ctx) } catch (e) { console.log('(引擎加载跳过部分依赖错误,尝试仅提取 syncPointMarkers)', e.message.slice(0, 80)) // 提取函数体 + 其紧邻的模块级依赖(markerEntityIndex / markerFingerprint)单独执行 const idxDecl = /const markerEntityIndex = new WeakMap\(\)[^\n]*/.exec(src)[0] const fpFn = /function markerFingerprint\(marker\)[\s\S]*?\n\}/.exec(src)[0] const fnMatch = src.match(/export function syncPointMarkers[\s\S]*?\n\}/) vm.runInContext( idxDecl + '\n' + fpFn + '\n' + 'globalThis.__engine = { syncPointMarkers: ' + fnMatch[0].replace('export function syncPointMarkers', 'function syncPointMarkers') + ' };', ctx, ) } return { syncPointMarkers: ctx.__engine.syncPointMarkers, created } } const marker = { key: 'MP-001|monitoring-point', kind: 'monitoring-point', code: 'MP-001', name: '', deviceCodes: [], status: 'ALARM', visualKey: 'ALARM', tone: 'lv1', shape: 'triangle', label: '报警', longitude: 110.4, latitude: 28.4, } const plan = { master: 'solid', inner: { icon: '', enabled: false, diameterRatio: 0.6, center: { x: 0.499, y: 0.397 } }, size: { width: 27, height: 33.93 }, anchor: { x: 0.4987, y: 1.0 }, tint: 'lv1', desaturate: false, labelOffsetY: -39.93, labelMaxWidth: 43.2, draw: { width: 86.06, height: 92.99, pad: 2.86 }, } let pass = 0 let fail = 0 const check = (cond, label) => { if (cond) { pass++; console.log(` ✓ ${label}`) } else { fail++; console.log(` ✗ ${label}`) } } console.log('① 新断言 × 新实现(传 planOf):BOTTOM 锚定 + 母版比例尺寸') { const { syncPointMarkers, created } = loadEngine() syncPointMarkers({}, [marker], () => '#ff3b52', () => 'data:,', () => plan, () => '#ff3b52') const bb = created[0].billboard check(bb.verticalOrigin === 'BOTTOM', `verticalOrigin = BOTTOM(实际 ${bb.verticalOrigin})`) check(bb.width === 86.06 && bb.height === 92.99, `尺寸 = draw 86.06×92.99(实际 ${bb.width}×${bb.height})`) check(created[0].properties.markerMaster && created[0].properties.markerMaster === 'solid', '宿主读数含 markerMaster=solid') } console.log('② 新断言 × 旧实现(不传 planOf):必须仍 CENTER/18×18 —— 新断言对旧实现失败即「能查到未迁移」') { const { syncPointMarkers, created } = loadEngine() syncPointMarkers({}, [marker], () => '#ff3b52', () => 'data:,') const bb = created[0].billboard check(bb.verticalOrigin === 'CENTER', `旧实现保持 CENTER(实际 ${bb.verticalOrigin})—— 新断言若在此判 BOTTOM 会假红`) check(!(bb.width === 27 && bb.height === 33.93), `旧实现尺寸非 18×22.63(实际 ${bb.width}×${bb.height})—— 新断言在此失败是预期`) } console.log('③ 旧断言 × 新实现:CENTER/18×18 的旧断言对新实现必须失败(防「改完仍是绿的」)') { const { syncPointMarkers, created } = loadEngine() syncPointMarkers({}, [marker], () => '#ff3b52', () => 'data:,', () => plan, () => '#ff3b52') const bb = created[0].billboard check(bb.verticalOrigin !== 'CENTER', `新实现 verticalOrigin ≠ CENTER(实际 ${bb.verticalOrigin})→ 旧 CENTER 断言会失败`) check(!(bb.width === 18 && bb.height === 18), `新实现尺寸 ≠ 18×18(实际 ${bb.width}×${bb.height})→ 旧方形断言会失败`) } console.log(`\n对抗式验证:${pass} 过 / ${fail} 败`) if (fail) process.exitCode = 1