opportunity-ui-publish.test.js 4.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. import test from 'node:test';
  2. import assert from 'node:assert/strict';
  3. import fs from 'node:fs';
  4. import path from 'node:path';
  5. import crypto from 'node:crypto';
  6. import {FILES,publishFiles} from '../scripts/publish-opportunity-ui.mjs';
  7. const sha=x=>crypto.createHash('sha256').update(x).digest('hex');
  8. function fixture(t){
  9. const workspace=fs.realpathSync('work');
  10. const root=fs.mkdtempSync(path.join(workspace,'report-evidence-publish-test-'));
  11. t.after(()=>{
  12. assert.equal(path.dirname(fs.realpathSync(root)),workspace);
  13. assert.ok(path.basename(root).startsWith('report-evidence-publish-test-'));
  14. fs.rmSync(root,{recursive:true});
  15. });
  16. const sourceDir=path.join(root,'bundle'),targetDir=path.join(root,'site'),manifestPath=path.join(root,'manifest.json');
  17. fs.mkdirSync(sourceDir);fs.mkdirSync(targetDir);fs.writeFileSync(path.join(targetDir,'index.html'),'fixed homepage');
  18. const manifest={version:'ksf.opportunity-ui.publish.v1',release:'ksf.opportunity-ui.20261103.v1',homepageSha256:sha('fixed homepage'),files:FILES.map(relative=>{
  19. for(const [dir,text]of [[sourceDir,'new'],[targetDir,'old']]){
  20. const file=path.join(dir,relative);fs.mkdirSync(path.dirname(file),{recursive:true});fs.writeFileSync(file,text+relative);
  21. }
  22. return{path:relative,sha256:sha('new'+relative),priorNormalizedSha256:[sha('old'+relative)],allowMissing:false};
  23. })};
  24. fs.writeFileSync(manifestPath,JSON.stringify(manifest));
  25. return{root,sourceDir,targetDir,manifestPath,manifest};
  26. }
  27. const contents=f=>FILES.map(relative=>fs.readFileSync(path.join(f.targetDir,relative),'utf8'));
  28. test('机会卡修复发布默认只读,六个文件备份后更新,重复执行不写且首页不变',t=>{
  29. const f=fixture(t),before=contents(f),top=fs.readdirSync(f.root);
  30. const plan=publishFiles(f);assert.equal(plan.readOnly,true);assert.equal(plan.changes.length,6);
  31. assert.deepEqual(contents(f),before);assert.deepEqual(fs.readdirSync(f.root),top);
  32. const applied=publishFiles({...f,apply:true});assert.equal(applied.filesystemVerified,true);assert.equal(applied.publicSiteVerified,false);
  33. assert.ok(!applied.backup.startsWith(f.targetDir+path.sep));assert.equal(applied.filesChecked,6);
  34. assert.equal(fs.readFileSync(path.join(f.targetDir,'index.html'),'utf8'),'fixed homepage');
  35. assert.deepEqual(contents(f),FILES.map(relative=>'new'+relative));
  36. assert.equal(publishFiles({...f,apply:true}).backup,null);
  37. });
  38. test('未知站点版本、损坏文件、首页不匹配和越界清单在写前停止',t=>{
  39. for(const kind of ['unknown','source','homepage','manifest']){
  40. const f=fixture(t);
  41. if(kind==='unknown')fs.writeFileSync(path.join(f.targetDir,FILES[0]),'external');
  42. if(kind==='source')fs.appendFileSync(path.join(f.sourceDir,FILES[0]),'corrupt');
  43. if(kind==='homepage')fs.writeFileSync(path.join(f.targetDir,'index.html'),'another homepage');
  44. if(kind==='manifest'){f.manifest.files[0].path='index.html';fs.writeFileSync(f.manifestPath,JSON.stringify(f.manifest));}
  45. const before=contents(f),top=fs.readdirSync(f.root);
  46. assert.throws(()=>publishFiles({...f,apply:true}),{code:{unknown:'UNKNOWN_TARGET_CONTENT',source:'PAYLOAD_HASH_MISMATCH',homepage:'HOMEPAGE_MISMATCH',manifest:'UNEXPECTED_FILE_LIST'}[kind]});
  47. assert.deepEqual(contents(f),before);assert.deepEqual(fs.readdirSync(f.root),top);
  48. }
  49. });
  50. test('写入失败按备份回退六个文件',t=>{
  51. const f=fixture(t),before=contents(f);let count=0;
  52. assert.throws(()=>publishFiles({...f,apply:true},{beforeWrite(){if(++count===3)throw Error('simulated');}}),e=>e.rollbackComplete===true);
  53. assert.deepEqual(contents(f),before);
  54. });
  55. test('并发修改不会被失败回退覆盖',t=>{
  56. const f=fixture(t);
  57. assert.throws(()=>publishFiles({...f,apply:true},{beforeWrite(relative){
  58. if(relative===FILES[1]){fs.writeFileSync(path.join(f.targetDir,FILES[0]),'external version');throw Error('simulated');}
  59. }}),e=>e.rollbackComplete===false);
  60. assert.equal(fs.readFileSync(path.join(f.targetDir,FILES[0]),'utf8'),'external version');
  61. assert.equal(fs.readFileSync(path.join(f.targetDir,FILES[1]),'utf8'),'old'+FILES[1]);
  62. });