overnight-manual-review-smoke.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. #!/usr/bin/env node
  2. const fs = require('fs');
  3. const os = require('os');
  4. const path = require('path');
  5. const { spawnSync } = require('child_process');
  6. const { buildMetrics, readCsv } = require('./review-metrics');
  7. const root = path.resolve(__dirname, '..');
  8. function main() {
  9. const output = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-overnight-review-'));
  10. const result = spawnSync(process.execPath, [
  11. path.join(root, 'scripts', 'overnight-quality-campaign.js')
  12. ], {
  13. cwd: root,
  14. encoding: 'utf8',
  15. env: {
  16. ...process.env,
  17. TIHAO_OVERNIGHT_LIVE: 'false',
  18. TIHAO_OVERNIGHT_OUTPUT: output,
  19. TIHAO_OVERNIGHT_FIXTURES: 'dha-mom-baby',
  20. TIHAO_OVERNIGHT_VARIANTS: 'baseline-live,result-first',
  21. TIHAO_OVERNIGHT_MAX_RUNS: '2',
  22. TIHAO_OVERNIGHT_FAIL_ON_GATES: 'false'
  23. }
  24. });
  25. if (result.status !== 0) {
  26. process.stderr.write(result.stderr || result.stdout || '');
  27. throw new Error('overnight quality smoke run failed');
  28. }
  29. const reviewCsv = path.join(output, 'manual-review-sample.csv');
  30. const aggregateReport = path.join(output, 'aggregate-report.md');
  31. assert(fs.existsSync(reviewCsv), 'manual-review-sample.csv should be written');
  32. assert(fs.existsSync(aggregateReport), 'aggregate-report.md should be written');
  33. const rows = readCsv(reviewCsv);
  34. for (const column of ['人工复核标签', '客户选择', '归因类型', '反馈原因']) {
  35. assert(rows.header.includes(column), `manual review sample should include ${column}`);
  36. }
  37. const metrics = buildMetrics({ inputPath: reviewCsv, rows });
  38. assert(metrics.headerOk, 'manual review sample should keep software table prefix header');
  39. assert(metrics.duplicateKeyCount === 0, 'manual review sample should have zero duplicate keys');
  40. assert(metrics.rankContinuous, 'manual review sample ranks should be continuous per brief');
  41. const report = fs.readFileSync(aggregateReport, 'utf8');
  42. assert(report.includes('归因类型'), 'aggregate report should instruct failure attribution');
  43. assert(report.includes('客户选择'), 'aggregate report should instruct customer decision labeling');
  44. assert(report.includes('参考账号不像'), 'aggregate report should list reference mismatch label');
  45. console.log(JSON.stringify({
  46. ok: true,
  47. output,
  48. reviewCsv,
  49. rows: metrics.total,
  50. duplicateKeyCount: metrics.duplicateKeyCount,
  51. rankContinuous: metrics.rankContinuous
  52. }, null, 2));
  53. }
  54. function assert(condition, message) {
  55. if (!condition) throw new Error(message);
  56. }
  57. main();