|
- import fs from 'node:fs';
- import path from 'node:path';
- import crypto from 'node:crypto';
- import assert from 'node:assert/strict';
- import {report,work} from './scene-editor-closure-live.mjs';
- import {sceneUnitPlan} from './scene-editor-evidence-bindings.mjs';
- const readLog=name=>{const b=fs.readFileSync(path.join(work,name));return b.subarray(0,2).equals(Buffer.from([255,254]))?b.toString('utf16le').slice(1):b.toString('utf8').replace(/^\uFEFF/,'')};
- const read=name=>JSON.parse(fs.readFileSync(path.join(report,name),'utf8'));
- const sha=file=>crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
- const write=(name,value)=>fs.writeFileSync(path.join(report,name),JSON.stringify(value,null,2));
- const results=read('results.json');
- function tests(suites){return suites.flatMap(s=>[...(s.specs||[]).flatMap(p=>p.tests.map(t=>({title:p.title,status:t.status,result:t.results.at(-1)}))),...tests(s.suites||[])])}
- const cases=tests(results.suites);
- assert.ok(cases.length>0);assert.equal(results.errors.length,0);assert.ok(cases.every(t=>t.status==='expected'&&t.result.status==='passed'&&t.result.retry===0),'One full no-retry browser run must pass');
- const runtimeLog=readLog('node-tests-final.log'),apiLog=readLog('scene-api-tests-final.log');
- const totals=text=>Object.fromEntries(['tests','pass','fail','skipped'].map(key=>[key,Number(new RegExp(`^# ${key} (\\d+)`,'m').exec(text)?.[1])]));
- const runtime=totals(runtimeLog),client=totals(apiLog);
- for(const group of [runtime,client]){assert.ok(group.tests>0);assert.equal(group.pass,group.tests);assert.equal(group.fail,0);assert.equal(group.skipped,0)}
- const build=readLog('web-build-final.log');assert.match(build,/built in/);assert.match(build,/legacy model iframe verified/);
- const apiBuild=readLog('api-build.log');assert.match(apiBuild,/BUILD SUCCESS/);
- const apiReports=path.resolve(report,'../../../unreal_tran_api/ut_tran/target/surefire-reports');
- const backend={tests:0,failures:0,errors:0,skipped:0,suites:0};
- for(const file of fs.readdirSync(apiReports).filter(f=>/^TEST-.*\.xml$/.test(f))){
- const head=fs.readFileSync(path.join(apiReports,file),'utf8').match(/<testsuite\s[^>]*>/)?.[0];assert.ok(head);
- backend.suites++;for(const key of ['tests','failures','errors','skipped'])backend[key]+=Number(new RegExp(`\\b${key}="(\\d+)"`).exec(head)?.[1]||0);
- }
- assert.ok(backend.tests>0);assert.equal(backend.failures+backend.errors+backend.skipped,0);
- const runId=`scene-editor-${results.stats.startTime}`;
- const coverage=sceneUnitPlan.map(([file,title,featureIds])=>{
- assert.ok(`${runtimeLog}\n${apiLog}`.includes(`- ${title}\n`)||`${runtimeLog}\n${apiLog}`.includes(`- ${title}\r\n`),`Executed unit proof: ${title}`);
- return{kind:'unit',status:'PASS',runId,testTitle:title,featureIds,source:file,note:'实际执行真实方法/纯函数合同,未冒称浏览器操作'};
- });
- write('validation.json',{time:new Date().toISOString(),runId,overallStatus:'PASS',finalRun:true,
- scope:'默认路由的119项功能;浏览器证据与聚焦单元合同分别计数。较大功能扩展未实施。',
- e2e:{tests:cases.length,pass:cases.length,fail:0,retries:0,startTime:results.stats.startTime,durationMs:results.stats.duration,complete:true},
- priorFullRun:{pass:32,fail:1,reason:'关闭保存失败用例的提示定位同时匹配成功和失败消息,已限定目标错误;另补列表搜索遮罩消失后截图的等待条件。',privateArchived:true,countedAsFinal:false},
- secondFullRun:{pass:32,fail:1,reason:'并行加载同源模型时恢复顺序不稳定;已保留文档对象顺序,并以实例编号验证部件隔离和重开。',privateArchived:true,countedAsFinal:false},
- frontend:{status:'PASS',command:'pnpm run build',legacyModelPayloadUnchanged:true},
- backend:{status:'PASS',command:'mvn -pl ut_tran -am test package',...backend},
- runtime:{status:'PASS',...runtime},client:{status:'PASS',...client},coverage,
- database:{ddlChanged:false,reason:'未新增或修改表结构;仅注册并清理测试副本、账号和角色。'},
- logSha256:Object.fromEntries(['web-build-final.log','api-build.log','node-tests-final.log','scene-api-tests-final.log'].map(file=>[file,sha(path.join(work,file))]))});
- const captions=read('screenshot-captions.json');
- const titles=cases.flatMap(t=>(t.result.attachments||[]).map(a=>({name:a.name,title:t.title})));
- for(const file of fs.readdirSync(path.join(report,'screenshots'))){
- if(!captions[file]){const base=file.replace(/\.[^.]+$/,'');const found=titles.find(a=>a.name===base||a.name===file);assert.ok(found,`Current screenshot attachment ${file}`);captions[file]=`${found.title}:${base}`;}
- }
- write('screenshot-captions.json',captions);
- console.log(JSON.stringify({e2e:cases.length,backend:backend.tests,node:runtime.tests+client.tests,unitProofs:coverage.length,runId}));
|