refactor: add CHECK constraints and drop redundant index in v3 tables
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import pytest
|
||||
@@ -173,19 +174,22 @@ def test_insert_unit_verdict_rerun_same_stage_overwrites(store: QuestionGenStore
|
||||
ts="2026-07-15T11:10:00+00:00",
|
||||
**common,
|
||||
)
|
||||
new_ts = "2026-07-15T11:20:00+00:00"
|
||||
store.insert_unit_verdict(
|
||||
verdict="pass",
|
||||
reason="second",
|
||||
metric_value=0.9,
|
||||
model="m2",
|
||||
ts="2026-07-15T11:20:00+00:00",
|
||||
ts=new_ts,
|
||||
**common,
|
||||
)
|
||||
rows = store._conn.execute(
|
||||
"SELECT verdict, reason, metric_value FROM unit_verdict WHERE unit_id='unit-3' AND stage=2"
|
||||
"SELECT verdict, reason, metric_value, ts "
|
||||
"FROM unit_verdict WHERE unit_id='unit-3' AND stage=2"
|
||||
).fetchall()
|
||||
assert len(rows) == 1
|
||||
assert rows[0] == ("pass", "second", 0.9)
|
||||
# ts 随整行覆盖为第二次(新)ts,锁死 DO UPDATE SET ts=excluded.ts 语义。
|
||||
assert rows[0] == ("pass", "second", 0.9, new_ts)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -241,6 +245,7 @@ def test_insert_collapse_metrics_recompute_upserts(store: QuestionGenStore) -> N
|
||||
subtitle_answerability=0.1,
|
||||
ts="2026-07-15T12:00:00+00:00",
|
||||
)
|
||||
new_ts = "2026-07-15T12:30:00+00:00"
|
||||
store.insert_collapse_metrics(
|
||||
pair_id="pair-2",
|
||||
text_only_acc=0.05,
|
||||
@@ -251,13 +256,14 @@ def test_insert_collapse_metrics_recompute_upserts(store: QuestionGenStore) -> N
|
||||
distractor_min_dist=0.9,
|
||||
multiformat_consistency=0.95,
|
||||
subtitle_answerability=0.0,
|
||||
ts="2026-07-15T12:30:00+00:00",
|
||||
ts=new_ts,
|
||||
)
|
||||
rows = store._conn.execute(
|
||||
"SELECT text_only_acc, slot_chi2 FROM collapse_metrics WHERE pair_id='pair-2'"
|
||||
"SELECT text_only_acc, slot_chi2, ts FROM collapse_metrics WHERE pair_id='pair-2'"
|
||||
).fetchall()
|
||||
assert len(rows) == 1
|
||||
assert rows[0] == (0.05, 9.0)
|
||||
# ts 随整行覆盖为第二次(新)ts,锁死 DO UPDATE SET ts=excluded.ts 语义。
|
||||
assert rows[0] == (0.05, 9.0, new_ts)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -346,6 +352,78 @@ def test_upsert_resume_state_same_unit_overwrites(store: QuestionGenStore) -> No
|
||||
assert rows[0] == ("accepted", "cfg-2", 5)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CHECK 约束 fail-loud(非法枚举写入直接报错,符合 §4.2/P5)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_unit_verdict_invalid_stage_raises(store: QuestionGenStore) -> None:
|
||||
"""非法 stage(超出 1-6)触发 CHECK 约束,写入直接 IntegrityError。"""
|
||||
with pytest.raises(sqlite3.IntegrityError):
|
||||
store.insert_unit_verdict(
|
||||
unit_id="unit-bad",
|
||||
pair_id=None,
|
||||
sub_pattern="order",
|
||||
stage=7,
|
||||
verdict="pass",
|
||||
reason="r",
|
||||
metric_value=None,
|
||||
model=None,
|
||||
session_id="sess-1",
|
||||
ts="2026-07-15T11:00:00+00:00",
|
||||
)
|
||||
|
||||
|
||||
def test_unit_verdict_invalid_verdict_raises(store: QuestionGenStore) -> None:
|
||||
"""非法 verdict(不在 pass/fail/abstain)触发 CHECK 约束,写入直接 IntegrityError。"""
|
||||
with pytest.raises(sqlite3.IntegrityError):
|
||||
store.insert_unit_verdict(
|
||||
unit_id="unit-bad2",
|
||||
pair_id=None,
|
||||
sub_pattern="order",
|
||||
stage=1,
|
||||
verdict="maybe",
|
||||
reason="r",
|
||||
metric_value=None,
|
||||
model=None,
|
||||
session_id="sess-1",
|
||||
ts="2026-07-15T11:00:00+00:00",
|
||||
)
|
||||
|
||||
|
||||
def test_facts_invalid_cross_agree_raises(store: QuestionGenStore) -> None:
|
||||
"""非法 cross_agree(非 0/1)触发 CHECK 约束,写入直接 IntegrityError。"""
|
||||
with pytest.raises(sqlite3.IntegrityError):
|
||||
store.insert_fact(
|
||||
fact_id="fact-bad",
|
||||
video_id="v",
|
||||
segment_id="s",
|
||||
subject="a",
|
||||
action="b",
|
||||
object="c",
|
||||
frame_ids="[]",
|
||||
polarity="真",
|
||||
fact_type="state",
|
||||
difficulty_tier=1,
|
||||
verifier_refs="{}",
|
||||
cross_agree=2,
|
||||
negative_at_target="x",
|
||||
session_id="sess",
|
||||
ts="2026-07-15T11:00:00+00:00",
|
||||
)
|
||||
|
||||
|
||||
def test_resume_state_invalid_status_raises(store: QuestionGenStore) -> None:
|
||||
"""非法 status(不在 pending/accepted/rejected)触发 CHECK 约束,写入直接 IntegrityError。"""
|
||||
with pytest.raises(sqlite3.IntegrityError):
|
||||
store.upsert_resume_state(
|
||||
unit_id="unit-bad",
|
||||
status="unknown",
|
||||
config_fingerprint="cfg",
|
||||
seq_offset=0,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# v2 旧表未受影响
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user