| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- #!/usr/bin/env python3
- """Step 3: 回归验证 dev_abstract / dev_dedup 双版本查询是否对称且各自正确。"""
- import sys
- from pathlib import Path
- sys.path.insert(0, str(Path(__file__).parent.parent.parent))
- from knowhub.knowhub_db.pg_capability_store import PostgreSQLCapabilityStore
- def q(cur, sql, params=()):
- cur.execute(sql, params)
- return cur.fetchall()
- def qc(cur, sql, params=()):
- cur.execute(sql, params)
- return cur.fetchone()['c']
- def main():
- s = PostgreSQLCapabilityStore()
- cur = s._get_cursor()
- try:
- cur.execute("SET statement_timeout = '30s'")
- # 1) 挑一个 req,两版各自看到的 strategy
- print('\n=== Test 1: REQ_003 两版的 strategy(应分别看到 abstract 和 concrete)===', flush=True)
- for v in ('dev_abstract', 'dev_dedup'):
- rows = q(cur, """SELECT s.id, s.name, s.version, rs.is_selected
- FROM requirement_strategy rs
- JOIN strategy s ON s.id=rs.strategy_id
- WHERE rs.requirement_id='REQ_003' AND s.version=%s""", (v,))
- print(f' [{v}]: {len(rows)} strategies', flush=True)
- for r in rows:
- print(f' - {r["id"]}: {r["name"][:40]} selected={r["is_selected"]}', flush=True)
- # 2) 每版的 strategy → cap 路径是否自洽
- print('\n=== Test 2: cap 过滤是否隔离(dev_dedup 的 strat 不能指向 dev_abstract cap)===', flush=True)
- for v in ('dev_abstract', 'dev_dedup'):
- leak = qc(cur, """SELECT COUNT(*) c FROM strategy_capability sc
- JOIN strategy s ON s.id=sc.strategy_id
- JOIN capability c ON c.id=sc.capability_id
- WHERE s.version=%s AND c.version!=%s""", (v, v))
- print(f' [{v}] strat.ver={v} 但 cap.ver 不等于 {v}: {leak} {"❌" if leak else "✓"}', flush=True)
- # 3) 每版 strategy_resource 是否自洽
- for v in ('dev_abstract', 'dev_dedup'):
- leak = qc(cur, """SELECT COUNT(*) c FROM strategy_resource sr
- JOIN strategy s ON s.id=sr.strategy_id
- JOIN resource r ON r.id=sr.resource_id
- WHERE s.version=%s AND r.version!=%s""", (v, v))
- print(f' [{v}] strat_resource 跨版: {leak} {"❌" if leak else "✓"}', flush=True)
- # 4) req 覆盖度(每 req 在每版都有 strategy?)
- print('\n=== Test 3: 每版 strategy 覆盖的 req 数量 ===', flush=True)
- for v in ('dev_abstract', 'dev_dedup'):
- n = qc(cur, """SELECT COUNT(DISTINCT rs.requirement_id) c
- FROM requirement_strategy rs
- JOIN strategy s ON s.id=rs.strategy_id
- WHERE s.version=%s""", (v,))
- print(f' [{v}]: {n}/99 reqs 有 strategy 链接', flush=True)
- # 5) 约束验证:knowledge_cap ⊆ req_cap(dev_abstract 的核心约束)
- print('\n=== Test 4: knowledge_cap ⊆ req_cap(dev_abstract 约束)===', flush=True)
- viol = qc(cur, """SELECT COUNT(*) c FROM knowledge_capability kc
- JOIN requirement_knowledge rk ON rk.knowledge_id=kc.knowledge_id
- JOIN knowledge k ON k.id=kc.knowledge_id
- LEFT JOIN requirement_capability rc
- ON rc.requirement_id=rk.requirement_id
- AND rc.capability_id=kc.capability_id
- WHERE k.version='dev_abstract' AND rc.capability_id IS NULL""")
- print(f' 违规: {viol} {"❌" if viol else "✓"}', flush=True)
- # 6) dev_dedup 约束:strat_cap ⊆ req_cap(dev_dedup 里 req 是每 cap 研究全集)
- print('\n=== Test 5: dev_dedup: strat_cap ⊆ req_cap(每 req 的 strat 用的 cap ⊆ 该 req 的 cap)===', flush=True)
- viol = qc(cur, """SELECT COUNT(*) c FROM strategy_capability sc
- JOIN strategy s ON s.id=sc.strategy_id
- JOIN requirement_strategy rs ON rs.strategy_id=sc.strategy_id
- LEFT JOIN requirement_capability rc
- ON rc.requirement_id=rs.requirement_id
- AND rc.capability_id=sc.capability_id
- JOIN capability c ON c.id=sc.capability_id
- WHERE s.version='dev_dedup' AND c.version='dev_dedup'
- AND rc.capability_id IS NULL""")
- print(f' 违规: {viol} {"❌" if viol else "✓"}', flush=True)
- # 7) 交叉版本是否完全隔离(不会误查到另一版)
- print('\n=== Test 6: 交叉版本隔离(dev_abstract 查询不应看到 dev_dedup 的 strat)===', flush=True)
- n = qc(cur, """SELECT COUNT(*) c FROM strategy WHERE version='dev_abstract'""")
- print(f' dev_abstract strat: {n} (预期 26) {"✓" if n==26 else "❌"}', flush=True)
- n = qc(cur, """SELECT COUNT(*) c FROM strategy WHERE version='dev_dedup'""")
- print(f' dev_dedup strat: {n} (预期 99) {"✓" if n==99 else "❌"}', flush=True)
- finally:
- cur.close(); s.close()
- if __name__ == '__main__':
- main()
|