version_step3_verify.py 5.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. #!/usr/bin/env python3
  2. """Step 3: 回归验证 dev_abstract / dev_dedup 双版本查询是否对称且各自正确。"""
  3. import sys
  4. from pathlib import Path
  5. sys.path.insert(0, str(Path(__file__).parent.parent.parent))
  6. from knowhub.knowhub_db.pg_capability_store import PostgreSQLCapabilityStore
  7. def q(cur, sql, params=()):
  8. cur.execute(sql, params)
  9. return cur.fetchall()
  10. def qc(cur, sql, params=()):
  11. cur.execute(sql, params)
  12. return cur.fetchone()['c']
  13. def main():
  14. s = PostgreSQLCapabilityStore()
  15. cur = s._get_cursor()
  16. try:
  17. cur.execute("SET statement_timeout = '30s'")
  18. # 1) 挑一个 req,两版各自看到的 strategy
  19. print('\n=== Test 1: REQ_003 两版的 strategy(应分别看到 abstract 和 concrete)===', flush=True)
  20. for v in ('dev_abstract', 'dev_dedup'):
  21. rows = q(cur, """SELECT s.id, s.name, s.version, rs.is_selected
  22. FROM requirement_strategy rs
  23. JOIN strategy s ON s.id=rs.strategy_id
  24. WHERE rs.requirement_id='REQ_003' AND s.version=%s""", (v,))
  25. print(f' [{v}]: {len(rows)} strategies', flush=True)
  26. for r in rows:
  27. print(f' - {r["id"]}: {r["name"][:40]} selected={r["is_selected"]}', flush=True)
  28. # 2) 每版的 strategy → cap 路径是否自洽
  29. print('\n=== Test 2: cap 过滤是否隔离(dev_dedup 的 strat 不能指向 dev_abstract cap)===', flush=True)
  30. for v in ('dev_abstract', 'dev_dedup'):
  31. leak = qc(cur, """SELECT COUNT(*) c FROM strategy_capability sc
  32. JOIN strategy s ON s.id=sc.strategy_id
  33. JOIN capability c ON c.id=sc.capability_id
  34. WHERE s.version=%s AND c.version!=%s""", (v, v))
  35. print(f' [{v}] strat.ver={v} 但 cap.ver 不等于 {v}: {leak} {"❌" if leak else "✓"}', flush=True)
  36. # 3) 每版 strategy_resource 是否自洽
  37. for v in ('dev_abstract', 'dev_dedup'):
  38. leak = qc(cur, """SELECT COUNT(*) c FROM strategy_resource sr
  39. JOIN strategy s ON s.id=sr.strategy_id
  40. JOIN resource r ON r.id=sr.resource_id
  41. WHERE s.version=%s AND r.version!=%s""", (v, v))
  42. print(f' [{v}] strat_resource 跨版: {leak} {"❌" if leak else "✓"}', flush=True)
  43. # 4) req 覆盖度(每 req 在每版都有 strategy?)
  44. print('\n=== Test 3: 每版 strategy 覆盖的 req 数量 ===', flush=True)
  45. for v in ('dev_abstract', 'dev_dedup'):
  46. n = qc(cur, """SELECT COUNT(DISTINCT rs.requirement_id) c
  47. FROM requirement_strategy rs
  48. JOIN strategy s ON s.id=rs.strategy_id
  49. WHERE s.version=%s""", (v,))
  50. print(f' [{v}]: {n}/99 reqs 有 strategy 链接', flush=True)
  51. # 5) 约束验证:knowledge_cap ⊆ req_cap(dev_abstract 的核心约束)
  52. print('\n=== Test 4: knowledge_cap ⊆ req_cap(dev_abstract 约束)===', flush=True)
  53. viol = qc(cur, """SELECT COUNT(*) c FROM knowledge_capability kc
  54. JOIN requirement_knowledge rk ON rk.knowledge_id=kc.knowledge_id
  55. JOIN knowledge k ON k.id=kc.knowledge_id
  56. LEFT JOIN requirement_capability rc
  57. ON rc.requirement_id=rk.requirement_id
  58. AND rc.capability_id=kc.capability_id
  59. WHERE k.version='dev_abstract' AND rc.capability_id IS NULL""")
  60. print(f' 违规: {viol} {"❌" if viol else "✓"}', flush=True)
  61. # 6) dev_dedup 约束:strat_cap ⊆ req_cap(dev_dedup 里 req 是每 cap 研究全集)
  62. print('\n=== Test 5: dev_dedup: strat_cap ⊆ req_cap(每 req 的 strat 用的 cap ⊆ 该 req 的 cap)===', flush=True)
  63. viol = qc(cur, """SELECT COUNT(*) c FROM strategy_capability sc
  64. JOIN strategy s ON s.id=sc.strategy_id
  65. JOIN requirement_strategy rs ON rs.strategy_id=sc.strategy_id
  66. LEFT JOIN requirement_capability rc
  67. ON rc.requirement_id=rs.requirement_id
  68. AND rc.capability_id=sc.capability_id
  69. JOIN capability c ON c.id=sc.capability_id
  70. WHERE s.version='dev_dedup' AND c.version='dev_dedup'
  71. AND rc.capability_id IS NULL""")
  72. print(f' 违规: {viol} {"❌" if viol else "✓"}', flush=True)
  73. # 7) 交叉版本是否完全隔离(不会误查到另一版)
  74. print('\n=== Test 6: 交叉版本隔离(dev_abstract 查询不应看到 dev_dedup 的 strat)===', flush=True)
  75. n = qc(cur, """SELECT COUNT(*) c FROM strategy WHERE version='dev_abstract'""")
  76. print(f' dev_abstract strat: {n} (预期 26) {"✓" if n==26 else "❌"}', flush=True)
  77. n = qc(cur, """SELECT COUNT(*) c FROM strategy WHERE version='dev_dedup'""")
  78. print(f' dev_dedup strat: {n} (预期 99) {"✓" if n==99 else "❌"}', flush=True)
  79. finally:
  80. cur.close(); s.close()
  81. if __name__ == '__main__':
  82. main()