"""adversarial_verdicts 表:写入 / 续跑查询 / agent_config 作废 / 正确率聚合。""" from app.question_gen.run_store import QuestionGenStore def _store(tmp_path): return QuestionGenStore(str(tmp_path / "q.db")) def _row(**kw): base = { "question_id": "v1_Action Recognition_0001", "round": 0, "stage": "cheat", "question_hash": "h1", "agent_prediction": "B", "agent_correct": False, "verdict": "passed", "pair_id": None, "agent_config": "cfg1", } base.update(kw) return base def test_table_created(tmp_path): store = _store(tmp_path) cols = {r[1] for r in store._conn.execute("PRAGMA table_info(adversarial_verdicts)")} assert {"question_id", "round", "stage", "question_hash", "agent_prediction", "agent_correct", "verdict", "pair_id", "agent_config"} <= cols store.close() def test_record_and_resume_lookup(tmp_path): store = _store(tmp_path) store.record_verdict(**_row(stage="cheat")) done = store.completed_stages("v1_Action Recognition_0001", "h1", "cfg1") assert done == {"cheat"} # 不同 hash 视为未完成 assert store.completed_stages("v1_Action Recognition_0001", "h2", "cfg1") == set() store.close() def test_agent_config_change_invalidates(tmp_path): store = _store(tmp_path) store.record_verdict(**_row(stage="cheat")) store.invalidate_stale_config("v1_Action Recognition_0001", "cfg2") # 旧 config 行必须被真正删除(不能只靠 cfg2 查空——no-op 也满足那个弱断言) assert store.completed_stages("v1_Action Recognition_0001", "h1", "cfg1") == set() cfg1_rows = store._conn.execute( "SELECT COUNT(*) FROM adversarial_verdicts WHERE agent_config='cfg1'" ).fetchone()[0] assert cfg1_rows == 0 assert store.completed_stages("v1_Action Recognition_0001", "h1", "cfg2") == set() store.close() def test_upsert_same_key_overwrites(tmp_path): store = _store(tmp_path) store.record_verdict(**_row(agent_prediction="A")) store.record_verdict(**_row(agent_prediction="C")) rows = store._conn.execute( "SELECT agent_prediction FROM adversarial_verdicts " "WHERE question_id=? AND question_hash=? AND stage=?", ("v1_Action Recognition_0001", "h1", "cheat"), ).fetchall() assert len(rows) == 1 and rows[0][0] == "C" store.close() def test_cheat_accuracy_aggregation(tmp_path): store = _store(tmp_path) store.record_verdict(**_row(question_id="q1", question_hash="a", agent_correct=True)) store.record_verdict(**_row(question_id="q2", question_hash="b", agent_correct=False)) store.record_verdict(**_row(question_id="q3", question_hash="c", agent_correct=True)) assert store.cheat_agent_accuracy(round_no=0) == 2 / 3 store.close() def test_final_passed_question_ids_survives_both_gates(tmp_path): store = _store(tmp_path) # q1 太简单被作弊门剔除;q2 过两门;q3 被翻转门剔除(filtered_no_flip) store.record_verdict(**_row(question_id="q1", question_hash="a", stage="cheat", verdict="filtered_too_easy")) store.record_verdict(**_row(question_id="q2", question_hash="b", stage="cheat", verdict="passed")) store.record_verdict(**_row(question_id="q3", question_hash="c", stage="cheat", verdict="passed")) store.record_verdict(**_row(question_id="q3", question_hash="c", stage="flip_mirror", verdict="filtered_no_flip")) passed = store.final_passed_question_ids( {"q1": "a", "q2": "b", "q3": "c"}, "cfg1" ) assert passed == {"q2"} # 仅 q2:cheat=passed 且无 filtered_no_flip # stale hash 不泄漏(当前 hash 不匹配旧行) assert store.final_passed_question_ids({"q2": "stale"}, "cfg1") == set() # stale config 不泄漏 assert store.final_passed_question_ids({"q2": "b"}, "cfgX") == set() store.close()