| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- /**
- * 票 71 变异闸:证明契约测试对「未转工单的报警 > 工单处置 > 预警」这条序**有牙**。
- *
- * 每条变异:按字节把源码改成规则失效的形态 → 跑 CockpitMonitoringPointStatusOrderTest →
- * 必须转红 → 再按字节还原。全部转红、还原后复绿,闸门才算通过。
- *
- * 沿用票 52 变异闸的两条教训:
- * 1. 必须 '-pl zksy-admin -am' 走 reactor 重新编译,否则 zksy-system 从本地仓库 jar 解析,源码变异进不了测试;
- * 2. Windows 上 mvn 是 mvn.cmd,显式给路径;「构建没跑到测试」不能算打红。
- *
- * 用法:node .scratch/cockpit-real-data-wayfinding/verify/t71-mutations.mjs
- */
- import { execSync } from 'node:child_process'
- import { readFileSync, writeFileSync } from 'node:fs'
- const BACKEND = 'E:/pipe-ner-server'
- const MODULE = 'pipe-network-service'
- const TEST = 'CockpitMonitoringPointStatusOrderTest'
- const MVN = 'D:/Maven/apache-maven-3.9.15/bin/mvn.cmd'
- const RESOLVER = `${BACKEND}/${MODULE}/zksy-system/src/main/java/com/zksy/cockpit/point/CockpitMonitoringPointStatusResolver.java`
- const ENUM = `${BACKEND}/${MODULE}/zksy-system/src/main/java/com/zksy/cockpit/point/CockpitMonitoringPointStatus.java`
- const read = (p) => readFileSync(p, 'utf8')
- const write = (p, s) => writeFileSync(p, s)
- const toLf = (s) => s.replace(/\r\n/g, '\n')
- const ORDER_BLOCK = ` // 「未转工单的报警」已在上面拦截;剩下的「有未结束工单」压过「有未解决预警」。
- if (device.isWorkOrderInProgress()) {
- return CockpitMonitoringPointStatus.WORK_ORDER;
- }
- if (device.isWarning()) {
- return CockpitMonitoringPointStatus.WARNING;
- }`
- const mutations = [
- {
- id: 'M1',
- name: '退回原序:预警重新压过工单(两个分支换回)',
- file: RESOLVER,
- from: ORDER_BLOCK,
- to: ` if (device.isWarning()) {
- return CockpitMonitoringPointStatus.WARNING;
- }
- if (device.isWorkOrderInProgress()) {
- return CockpitMonitoringPointStatus.WORK_ORDER;
- }`,
- },
- {
- id: 'M2',
- name: '报警分支忽略承接事实(全部报警恒判 ALARM)',
- file: RESOLVER,
- from: ` return device.isAlarmsCoveredByOpenWorkOrders()
- ? CockpitMonitoringPointStatus.WORK_ORDER
- : CockpitMonitoringPointStatus.ALARM;`,
- to: ` return CockpitMonitoringPointStatus.ALARM;`,
- },
- {
- id: 'M3',
- name: '删掉非报警分支的工单判断(工单不再压预警)',
- file: RESOLVER,
- from: ` if (device.isWorkOrderInProgress()) {
- return CockpitMonitoringPointStatus.WORK_ORDER;
- }
- if (device.isWarning()) {`,
- to: ` if (device.isWarning()) {`,
- },
- {
- id: 'M4',
- name: '偷偷改枚举优先级(Q2 被破坏:WORK_ORDER 提到 WARNING 之前)',
- file: ENUM,
- from: ` WARNING(2, "预警"),
- WORK_ORDER(3, "工单处置"),`,
- to: ` WARNING(3, "预警"),
- WORK_ORDER(2, "工单处置"),`,
- },
- ]
- /** 跑一次测试;返回 { ran, green } —— ran=false 表示构建没跑到测试(不能算证据)。 */
- const runTest = () => {
- let out = ''
- try {
- out = execSync(
- `"${MVN}" -o -pl zksy-admin -am test -Dtest=${TEST} -DfailIfNoTests=false`,
- { cwd: `${BACKEND}/${MODULE}`, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] })
- } catch (error) {
- out = `${error.stdout ?? ''}${error.stderr ?? ''}`
- }
- const summary = out.match(/Tests run: (\d+), Failures: (\d+), Errors: (\d+)/g)
- if (!summary || summary.length === 0) {
- return { ran: false, green: false, out }
- }
- const last = summary[summary.length - 1]
- const [, total, failures, errors] = last.match(/Tests run: (\d+), Failures: (\d+), Errors: (\d+)/)
- return { ran: true, green: Number(failures) === 0 && Number(errors) === 0 && Number(total) > 0, out }
- }
- const baseline = runTest()
- if (!baseline.ran || !baseline.green) {
- console.log('基线不绿,闸门无法判定:先修好源码/测试再跑变异。')
- process.exit(1)
- }
- console.log('基线绿(未变异时测试全过),开始变异…\n')
- let red = 0
- let broken = 0
- const originals = new Map()
- for (const m of mutations) {
- if (!originals.has(m.file)) originals.set(m.file, read(m.file))
- const original = originals.get(m.file)
- const crlf = original.includes('\r\n')
- const norm = toLf(original)
- if (!norm.includes(m.from)) {
- console.log(`SKIP ${m.id} — anchor 未命中(源码已变,需同步变异脚本):${m.name}`)
- broken += 1
- continue
- }
- write(m.file, crlf ? norm.replace(m.from, m.to).replace(/\n/g, '\r\n') : norm.replace(m.from, m.to))
- const result = runTest()
- if (!result.ran) {
- console.log(`FAIL ${m.id} — 构建没跑到测试,不能算打红:${m.name}`)
- broken += 1
- } else if (!result.green) {
- red += 1
- console.log(`PASS ${m.id} — 变异被打红:${m.name}`)
- } else {
- console.log(`FAIL ${m.id} — 变异未被抓住(测试无牙):${m.name}`)
- }
- write(m.file, original)
- if (read(m.file) !== original) {
- console.log(`FAIL ${m.id} — 还原后字节不一致`)
- broken += 1
- }
- }
- const final = runTest()
- console.log(`\n变异打红 ${red}/${mutations.length};还原后复绿:${final.green ? '是' : '否'};异常 ${broken}`)
- process.exit(red === mutations.length && final.green && broken === 0 ? 0 : 1)
|