mjs-mime-preflight.test.mjs 2.8 KB

12345678910111213141516171819202122232425262728293031323334
  1. import fs from 'node:fs/promises';
  2. import path from 'node:path';
  3. import os from 'node:os';
  4. import test from 'node:test';
  5. import assert from 'node:assert/strict';
  6. import {spawnSync} from 'node:child_process';
  7. import {preflightMjsMime} from '../scripts/preflight-mime-mjs.mjs';
  8. async function fixture(t,{mapping='types { application/javascript js mjs; }',type='application/javascript; charset=utf-8',body='export const ready=true;'}={}){
  9. const root=await fs.mkdtemp(path.join(os.tmpdir(),'ksf-mjs-preflight-'));t.after(()=>fs.rm(root,{recursive:true,force:true}));
  10. const targetDir=await fs.realpath(root),mimeFile=path.join(root,'mime.types');await fs.writeFile(mimeFile,mapping);await fs.writeFile(path.join(root,'module.mjs'),'export const ready=true;');
  11. return{origin:'https://example.test',targetDir,mimeFile,files:[{path:'module.mjs'}],fetchImpl:async()=>new Response(body,{headers:{'content-type':type}})};
  12. }
  13. test('有效配置与真实模块响应均通过,预检保持只读',async t=>{
  14. const options=await fixture(t),before=await fs.readdir(options.targetDir),r=await preflightMjsMime(options);
  15. assert.equal(r.ok,true);assert.equal(r.readOnly,true);assert.equal(r.publicModulesChecked,1);assert.deepEqual(await fs.readdir(options.targetDir),before);
  16. });
  17. test('旧配置缺少mjs映射时,在请求前停止,注释不能冒充映射',async t=>{
  18. const options=await fixture(t,{mapping:'types { application/javascript js; }\n# application/javascript mjs;'});let requests=0;
  19. const r=await preflightMjsMime({...options,fetchImpl:async()=>{requests++;throw Error('should not fetch');}});
  20. assert.equal(r.ok,false);assert.equal(r.errorCode,'MJS_JAVASCRIPT_MAPPING_MISSING');assert.equal(requests,0);assert.ok(r.remedy.includes('nginx -t'));
  21. const cli=spawnSync(process.execPath,['scripts/preflight-mime-mjs.mjs','--target-dir',options.targetDir,'--mime-file',options.mimeFile],{encoding:'utf8'});
  22. assert.equal(cli.status,1);assert.equal(JSON.parse(cli.stdout).ok,false);
  23. });
  24. test('配置正确但响应为octet-stream时拒绝,复现白屏原因',async t=>{
  25. const r=await preflightMjsMime(await fixture(t,{type:'application/octet-stream'}));assert.equal(r.ok,false);assert.equal(r.errorCode,'PUBLIC_MJS_NOT_LOADABLE');
  26. });
  27. test('SPA返回HTML或内容不符不能因200和JavaScript响应头而通过',async t=>{
  28. const r=await preflightMjsMime(await fixture(t,{body:'<!doctype html><title>fallback</title>'}));assert.equal(r.ok,false);assert.equal(r.modules[0].bytesMatchTarget,false);
  29. });
  30. test('首次发布尚不存在的模块明确留待发布后验证',async t=>{
  31. const options=await fixture(t);await fs.unlink(path.join(options.targetDir,'module.mjs'));
  32. const r=await preflightMjsMime(options);assert.equal(r.ok,true);assert.equal(r.publicModulesChecked,0);assert.equal(r.publicVerificationDeferred,true);assert.equal(r.modules[0].reason,'not-yet-deployed');
  33. });