1
0

schema.test.js 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { test } from 'node:test'
  2. import assert from 'node:assert/strict'
  3. import { validateReviewReport } from '../../src/review/schema.js'
  4. const issue = (over = {}) => ({
  5. severity: 'high',
  6. category: 'setting',
  7. location: '第3段',
  8. description: '境界矛盾',
  9. evidence: '正文"金丹" vs 角色卡"练气"',
  10. fix_hint: '改回练气',
  11. blocking: false,
  12. ...over,
  13. })
  14. test('合法事实审查报告:通过 + 计数复算', async () => {
  15. const r = validateReviewReport({ chapter: 5, issues: [issue(), issue({ category: 'timeline' })], issues_count: 99 }, { reviewType: 'factCheck' })
  16. assert.equal(r.ok, true)
  17. assert.equal(r.errors.length, 0)
  18. assert.equal(r.report.issues_count, 2, '计数应被复算覆盖伪造的 99')
  19. })
  20. test('critical → 强制 blocking=true(即便 AI 报 false)', async () => {
  21. const r = validateReviewReport({ chapter: 5, issues: [issue({ severity: 'critical', blocking: false })] }, { reviewType: 'factCheck' })
  22. assert.equal(r.report.issues[0].blocking, true)
  23. assert.equal(r.report.has_blocking, true)
  24. })
  25. test('unregistered_thread(D3)→ 强制 blocking=false(即便 AI 报 true)', async () => {
  26. const r = validateReviewReport(
  27. { chapter: 5, issues: [issue({ category: 'unregistered_thread', severity: 'high', blocking: true })] },
  28. { reviewType: 'factCheck' }
  29. )
  30. assert.equal(r.report.issues[0].blocking, false, 'D3 即兴伏笔候选恒非阻断')
  31. assert.equal(r.report.blocking_count, 0)
  32. })
  33. test('category 分域:编辑审 category 出现在事实审查 → 报错', async () => {
  34. const r = validateReviewReport({ chapter: 5, issues: [issue({ category: 'pacing' })] }, { reviewType: 'factCheck' })
  35. assert.equal(r.ok, false)
  36. assert.ok(r.errors.some((e) => e.includes('越界')))
  37. })
  38. test('category 分域:事实审查 category 出现在编辑审 → 报错', async () => {
  39. const r = validateReviewReport({ chapter: 5, issues: [issue({ category: 'setting' })] }, { reviewType: 'editorial' })
  40. assert.equal(r.ok, false)
  41. })
  42. test('编辑审合法 category 通过', async () => {
  43. const r = validateReviewReport({ chapter: 5, issues: [issue({ category: 'structure' })] }, { reviewType: 'editorial' })
  44. assert.equal(r.ok, true)
  45. })
  46. test('缺字段 / 非法 severity → 报错', async () => {
  47. const r = validateReviewReport(
  48. { chapter: 5, issues: [{ severity: 'fatal', category: 'setting', description: '' }] },
  49. { reviewType: 'factCheck' }
  50. )
  51. assert.equal(r.ok, false)
  52. assert.ok(r.errors.some((e) => e.includes('severity')))
  53. assert.ok(r.errors.some((e) => e.includes('evidence') || e.includes('location') || e.includes('fix_hint')))
  54. })
  55. test('issues 非数组 → ok=false 不抛', async () => {
  56. const r = validateReviewReport({ chapter: 5 }, { reviewType: 'factCheck' })
  57. assert.equal(r.ok, false)
  58. assert.equal(r.report, null)
  59. })
  60. test('P1-2:issues 元素为 null/字符串/数组 → 报错不抛', () => {
  61. const r = validateReviewReport(
  62. { chapter: 5, issues: [null, '字符串', [1, 2], issue()] },
  63. { reviewType: 'factCheck' }
  64. )
  65. assert.equal(r.ok, false)
  66. assert.ok(r.errors.some((e) => e.includes('issues[0]')), '应报 issues[0] 非对象')
  67. assert.ok(r.errors.some((e) => e.includes('issues[1]')), '应报 issues[1] 非对象')
  68. assert.ok(r.errors.some((e) => e.includes('issues[2]')), '应报 issues[2] 非对象')
  69. })
  70. test('P1-2:blocking 只认严格布尔 true,字符串 "true"/"false" 不当真', () => {
  71. const r = validateReviewReport(
  72. { chapter: 5, issues: [issue({ severity: 'high', blocking: 'true' })] },
  73. { reviewType: 'factCheck' }
  74. )
  75. assert.equal(r.ok, true)
  76. assert.equal(r.report.issues[0].blocking, false, '字符串 "true" 不应转为阻断')
  77. assert.equal(r.report.blocking_count, 0)
  78. const rc = validateReviewReport(
  79. { chapter: 5, issues: [issue({ severity: 'critical', blocking: 'false' })] },
  80. { reviewType: 'factCheck' }
  81. )
  82. assert.equal(rc.report.issues[0].blocking, true, 'critical 强制阻断覆盖')
  83. })