manual-review-label-guide-smoke.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  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 ROOT = path.resolve(__dirname, '..');
  7. function main() {
  8. const outputDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-manual-review-label-guide-'));
  9. const result = spawnSync(process.execPath, [
  10. 'scripts/manual-review-label-guide.js',
  11. '--output',
  12. outputDir,
  13. '--strict'
  14. ], {
  15. cwd: ROOT,
  16. encoding: 'utf8',
  17. shell: false
  18. });
  19. if (result.stdout) process.stdout.write(result.stdout);
  20. if (result.stderr) process.stderr.write(result.stderr);
  21. assert(result.status === 0, 'manual review label guide should pass');
  22. const summaryPath = path.join(outputDir, 'manual-review-label-guide-summary.json');
  23. const reportPath = path.join(outputDir, 'manual-review-label-guide.md');
  24. const csvPath = path.join(outputDir, 'manual-review-label-guide.csv');
  25. assert(fs.existsSync(summaryPath), 'summary should exist');
  26. assert(fs.existsSync(reportPath), 'report should exist');
  27. assert(fs.existsSync(csvPath), 'csv should exist');
  28. const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
  29. const report = fs.readFileSync(reportPath, 'utf8').replace(/^\uFEFF/, '');
  30. const csv = fs.readFileSync(csvPath, 'utf8').replace(/^\uFEFF/, '');
  31. assert(summary.passed === true, 'summary should pass');
  32. assert(summary.directCustomerProof === false, 'guide must not be direct customer proof');
  33. assert(summary.proofLevel === 'smoke_or_local', 'guide should be smoke/local proof only');
  34. assert(summary.positiveLabelCount >= 2, 'guide should include positive labels');
  35. assert(summary.negativeLabelCount >= 5, 'guide should include negative labels');
  36. assert(summary.negativeLabelsRequireAttribution === true, 'negative labels should require attribution');
  37. assert(summary.attributionTypeCount >= 8, 'guide should include attribution types');
  38. assert(summary.labels.some(item => item.label === '可直接发客户' && item.category === 'positive'), 'positive direct-send label should exist');
  39. assert(summary.labels.some(item => item.label === '跑偏' && item.requiresAttribution), 'negative off-target label should require attribution');
  40. assert(summary.attributionTypes.some(item => item.type === '视频证据误判'), 'video attribution type should exist');
  41. assert(summary.customerEffectAuditRules.some(item => item.includes('customer-effect-summary.json')), 'customer effect audit boundary should be explicit');
  42. assert(report.includes('# 人工复核标签与归因填写指南'), 'report should be Chinese guide');
  43. assert(report.includes('## 人工复核标签'), 'report should include label section');
  44. assert(report.includes('## 负样本归因类型'), 'report should include attribution section');
  45. assert(report.includes('不证明命中率提升'), 'report should not overclaim hit-rate lift');
  46. assert(csv.includes('可直接发客户'), 'csv should include positive label');
  47. assert(csv.includes('视频证据误判'), 'csv should include attribution type');
  48. assert(!/sk-[A-Za-z0-9_-]{10,}/.test(report + csv), 'outputs should not contain model token patterns');
  49. assert(!/r:[A-Za-z0-9]{20,}/.test(report + csv), 'outputs should not contain Parse sessionToken patterns');
  50. assert(!/Authorization/i.test(report + csv), 'outputs should not expose Authorization text');
  51. console.log(JSON.stringify({
  52. ok: true,
  53. outputDir,
  54. labelCount: summary.labelCount,
  55. attributionTypeCount: summary.attributionTypeCount
  56. }, null, 2));
  57. }
  58. function assert(condition, message) {
  59. if (!condition) throw new Error(message);
  60. }
  61. main();