Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 

50 рядки
4.7 KiB

  1. import fs from 'node:fs';
  2. import path from 'node:path';
  3. import crypto from 'node:crypto';
  4. import assert from 'node:assert/strict';
  5. import {report,work} from './scene-editor-closure-live.mjs';
  6. import {sceneUnitPlan} from './scene-editor-evidence-bindings.mjs';
  7. 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/,'')};
  8. const read=name=>JSON.parse(fs.readFileSync(path.join(report,name),'utf8'));
  9. const sha=file=>crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
  10. const write=(name,value)=>fs.writeFileSync(path.join(report,name),JSON.stringify(value,null,2));
  11. const results=read('results.json');
  12. 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||[])])}
  13. const cases=tests(results.suites);
  14. 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');
  15. const runtimeLog=readLog('node-tests-final.log'),apiLog=readLog('scene-api-tests-final.log');
  16. const totals=text=>Object.fromEntries(['tests','pass','fail','skipped'].map(key=>[key,Number(new RegExp(`^# ${key} (\\d+)`,'m').exec(text)?.[1])]));
  17. const runtime=totals(runtimeLog),client=totals(apiLog);
  18. 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)}
  19. const build=readLog('web-build-final.log');assert.match(build,/built in/);assert.match(build,/legacy model iframe verified/);
  20. const apiBuild=readLog('api-build.log');assert.match(apiBuild,/BUILD SUCCESS/);
  21. const apiReports=path.resolve(report,'../../../unreal_tran_api/ut_tran/target/surefire-reports');
  22. const backend={tests:0,failures:0,errors:0,skipped:0,suites:0};
  23. for(const file of fs.readdirSync(apiReports).filter(f=>/^TEST-.*\.xml$/.test(f))){
  24. const head=fs.readFileSync(path.join(apiReports,file),'utf8').match(/<testsuite\s[^>]*>/)?.[0];assert.ok(head);
  25. backend.suites++;for(const key of ['tests','failures','errors','skipped'])backend[key]+=Number(new RegExp(`\\b${key}="(\\d+)"`).exec(head)?.[1]||0);
  26. }
  27. assert.ok(backend.tests>0);assert.equal(backend.failures+backend.errors+backend.skipped,0);
  28. const runId=`scene-editor-${results.stats.startTime}`;
  29. const coverage=sceneUnitPlan.map(([file,title,featureIds])=>{
  30. assert.ok(`${runtimeLog}\n${apiLog}`.includes(`- ${title}\n`)||`${runtimeLog}\n${apiLog}`.includes(`- ${title}\r\n`),`Executed unit proof: ${title}`);
  31. return{kind:'unit',status:'PASS',runId,testTitle:title,featureIds,source:file,note:'实际执行真实方法/纯函数合同,未冒称浏览器操作'};
  32. });
  33. write('validation.json',{time:new Date().toISOString(),runId,overallStatus:'PASS',finalRun:true,
  34. scope:'默认路由的119项功能;浏览器证据与聚焦单元合同分别计数。较大功能扩展未实施。',
  35. e2e:{tests:cases.length,pass:cases.length,fail:0,retries:0,startTime:results.stats.startTime,durationMs:results.stats.duration,complete:true},
  36. priorFullRun:{pass:32,fail:1,reason:'关闭保存失败用例的提示定位同时匹配成功和失败消息,已限定目标错误;另补列表搜索遮罩消失后截图的等待条件。',privateArchived:true,countedAsFinal:false},
  37. secondFullRun:{pass:32,fail:1,reason:'并行加载同源模型时恢复顺序不稳定;已保留文档对象顺序,并以实例编号验证部件隔离和重开。',privateArchived:true,countedAsFinal:false},
  38. frontend:{status:'PASS',command:'pnpm run build',legacyModelPayloadUnchanged:true},
  39. backend:{status:'PASS',command:'mvn -pl ut_tran -am test package',...backend},
  40. runtime:{status:'PASS',...runtime},client:{status:'PASS',...client},coverage,
  41. database:{ddlChanged:false,reason:'未新增或修改表结构;仅注册并清理测试副本、账号和角色。'},
  42. 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))]))});
  43. const captions=read('screenshot-captions.json');
  44. const titles=cases.flatMap(t=>(t.result.attachments||[]).map(a=>({name:a.name,title:t.title})));
  45. for(const file of fs.readdirSync(path.join(report,'screenshots'))){
  46. 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}`;}
  47. }
  48. write('screenshot-captions.json',captions);
  49. console.log(JSON.stringify({e2e:cases.length,backend:backend.tests,node:runtime.tests+client.tests,unitProofs:coverage.length,runId}));