smoke-package.mjs 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import fs from 'node:fs';
  2. import os from 'node:os';
  3. import path from 'node:path';
  4. import zlib from 'node:zlib';
  5. import { checkPackage } from '../install.js';
  6. import { resultEnvelope } from '../mcp/src/core/result-envelope.mjs';
  7. import { buildImageSetPlan, verifyPlan } from '../mcp/src/features/planning/planner.mjs';
  8. import { scoreImageSet } from '../skills/fmode-image-set-score/scripts/score-image-set.mjs';
  9. function crc32(bytes) {
  10. let crc = 0xffffffff;
  11. for (const byte of bytes) {
  12. crc ^= byte;
  13. for (let bit = 0; bit < 8; bit += 1) crc = (crc >>> 1) ^ (crc & 1 ? 0xedb88320 : 0);
  14. }
  15. return (crc ^ 0xffffffff) >>> 0;
  16. }
  17. function pngChunk(type, data = Buffer.alloc(0)) {
  18. const typeBytes = Buffer.from(type, 'ascii');
  19. const chunk = Buffer.alloc(12 + data.length);
  20. chunk.writeUInt32BE(data.length, 0);
  21. typeBytes.copy(chunk, 4);
  22. data.copy(chunk, 8);
  23. chunk.writeUInt32BE(crc32(Buffer.concat([typeBytes, data])), 8 + data.length);
  24. return chunk;
  25. }
  26. function fakePng(width, height) {
  27. const ihdr = Buffer.alloc(13);
  28. ihdr.writeUInt32BE(width, 0);
  29. ihdr.writeUInt32BE(height, 4);
  30. ihdr[8] = 8;
  31. ihdr[9] = 2;
  32. const raw = Buffer.alloc(height * (width * 3 + 1));
  33. return Buffer.concat([
  34. Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
  35. pngChunk('IHDR', ihdr), pngChunk('IDAT', zlib.deflateSync(raw)), pngChunk('IEND')
  36. ]);
  37. }
  38. const root = path.resolve(import.meta.dirname, '..');
  39. const checked = checkPackage(root);
  40. if (!checked.ok) throw new Error(`Package check failed: ${checked.errors.join(', ')}`);
  41. const envelope = resultEnvelope({ status: 'ok', assistantMessage: 'smoke' });
  42. for (const key of ['status','assistantMessage','summary','data','files','nextActions','warnings','errors']) {
  43. if (!(key in envelope)) throw new Error(`Missing envelope field: ${key}`);
  44. }
  45. const plan = buildImageSetPlan({ request: '规划七张商品套图', productDescription: '测试商品' });
  46. verifyPlan(plan);
  47. if (plan.items.length !== 7 || plan.requiresConfirmation !== true) throw new Error('Planning smoke failed.');
  48. for (const locale of ['zh', 'en']) {
  49. const directory = path.join(root, 'mcp', 'catalog', 'templates', locale);
  50. if (fs.readdirSync(directory).filter(name => name.endsWith('.json')).length !== 25) throw new Error(`${locale} template count mismatch`);
  51. }
  52. const scoreFixture = fs.mkdtempSync(path.join(os.tmpdir(), 'fmode-score-smoke-'));
  53. let score;
  54. try {
  55. fs.writeFileSync(path.join(scoreFixture, 'one.png'), fakePng(1024, 1024));
  56. score = scoreImageSet({ inputs: [scoreFixture], profile: 'generic' });
  57. if (score.automaticScore !== 100 || score.overallScore !== null || score.grade !== null || score.verdict !== 'insufficient_evidence' || score.sourceIntegrity.unchanged !== true) {
  58. throw new Error('Independent score smoke failed.');
  59. }
  60. } finally {
  61. fs.rmSync(scoreFixture, { recursive: true, force: true });
  62. }
  63. console.log(JSON.stringify({ status: 'ok', check: checked, planId: plan.planId, templates: 50, score: { version: score.scoreVersion, automatic: score.automaticScore, overall: score.overallScore, verdict: score.verdict } }, null, 2));