chore: snapshot in-progress question-gen work before preflight fixes

This commit is contained in:
2026-07-16 04:12:21 -04:00
parent 11a5545f57
commit a4c429b247
39 changed files with 738 additions and 283 deletions
+31 -48
View File
@@ -9,7 +9,6 @@ import pytest
from app.harness.store import (
_parse_version,
_write_meta,
advance_version,
extract_run_db,
init_seed,
@@ -21,7 +20,6 @@ from app.harness.store import (
read_seed,
)
# ---------------------------------------------------------------------------
# _parse_version
# ---------------------------------------------------------------------------
@@ -52,7 +50,7 @@ class TestParseVersion:
class TestListVersions:
"""list_versions 按数字排序,v10 排在 v2 后。"""
def test_list_versions_numeric_sort(self, tmp_path: "Path") -> None:
def test_list_versions_numeric_sort(self, tmp_path: Path) -> None:
"""v10 必须排在 v2 后面(非字典序)。"""
store = tmp_path / "store"
resource = store / "skills"
@@ -62,11 +60,11 @@ class TestListVersions:
result = list_versions(store, "skills")
assert result == ["v1", "v2", "v3", "v10", "v20"]
def test_list_versions_empty(self, tmp_path: "Path") -> None:
def test_list_versions_empty(self, tmp_path: Path) -> None:
store = tmp_path / "store"
assert list_versions(store, "skills") == []
def test_list_versions_ignores_non_version_dirs(self, tmp_path: "Path") -> None:
def test_list_versions_ignores_non_version_dirs(self, tmp_path: Path) -> None:
"""非 v\\d+ 格式的目录被忽略。"""
store = tmp_path / "store"
resource = store / "skills"
@@ -85,11 +83,11 @@ class TestListVersions:
class TestNextVersion:
"""next_version 返回下一个可用版本号。"""
def test_next_version_empty(self, tmp_path: "Path") -> None:
def test_next_version_empty(self, tmp_path: Path) -> None:
store = tmp_path / "store"
assert next_version(store, "skills") == "v1"
def test_next_version_after_existing(self, tmp_path: "Path") -> None:
def test_next_version_after_existing(self, tmp_path: Path) -> None:
store = tmp_path / "store"
resource = store / "skills"
resource.mkdir(parents=True)
@@ -97,7 +95,7 @@ class TestNextVersion:
(resource / "v2").mkdir()
assert next_version(store, "skills") == "v3"
def test_next_version_with_gap(self, tmp_path: "Path") -> None:
def test_next_version_with_gap(self, tmp_path: Path) -> None:
"""v1 和 v10 之间有 gapnext 应为 v11。"""
store = tmp_path / "store"
resource = store / "skills"
@@ -115,7 +113,7 @@ class TestNextVersion:
class TestAdvanceVersion:
"""advance_version copytree + _write_meta。"""
def test_advance_version(self, tmp_path: "Path") -> None:
def test_advance_version(self, tmp_path: Path) -> None:
store = tmp_path / "store"
resource = store / "skills"
resource.mkdir(parents=True)
@@ -149,7 +147,7 @@ class TestAdvanceVersion:
class TestInitStore:
"""init_store 初始化 Store 目录结构。"""
def test_init_store(self, tmp_path: "Path") -> None:
def test_init_store(self, tmp_path: Path) -> None:
videos = tmp_path / "videos_src"
videos.mkdir()
(videos / "v001").mkdir()
@@ -172,13 +170,11 @@ class TestInitStore:
assert (store / "skills" / "v1" / "search.md").read_text() == "skill"
assert (store / "prompts" / "v1" / "system.md").read_text() == "prompt"
skills_meta = json.loads(
(store / "skills" / "v1" / "meta.json").read_text()
)
skills_meta = json.loads((store / "skills" / "v1" / "meta.json").read_text())
assert skills_meta["version"] == "v1"
assert skills_meta["source"] == "manual"
def test_init_store_exists_raises(self, tmp_path: "Path") -> None:
def test_init_store_exists_raises(self, tmp_path: Path) -> None:
store = tmp_path / "store"
store.mkdir()
with pytest.raises(FileExistsError, match="Store 已存在"):
@@ -205,13 +201,9 @@ def _make_seed_fixtures(tmp_path):
baseline_db = tmp_path / "base.db"
conn = sqlite3.connect(baseline_db)
conn.execute(
"CREATE TABLE _runs (run_id TEXT PRIMARY KEY, status TEXT)"
)
conn.execute("CREATE TABLE _runs (run_id TEXT PRIMARY KEY, status TEXT)")
conn.execute("INSERT INTO _runs VALUES ('r1', 'done')")
conn.execute(
"CREATE TABLE predictions (run_id TEXT, question_id TEXT, answer TEXT)"
)
conn.execute("CREATE TABLE predictions (run_id TEXT, question_id TEXT, answer TEXT)")
conn.execute("INSERT INTO predictions VALUES ('r1', 'q1', 'A')")
conn.commit()
conn.close()
@@ -222,7 +214,7 @@ def _make_seed_fixtures(tmp_path):
class TestInitSeed:
"""init_seed 创建种子目录。"""
def test_init_seed(self, tmp_path: "Path") -> None:
def test_init_seed(self, tmp_path: Path) -> None:
store, skills_dir, prompts_dir, baseline_db = _make_seed_fixtures(tmp_path)
seed_dir = init_seed(
store,
@@ -245,25 +237,23 @@ class TestInitSeed:
assert meta["description"] == "初始种子"
assert "created_at" in meta
def test_init_seed_exists_raises(self, tmp_path: "Path") -> None:
def test_init_seed_exists_raises(self, tmp_path: Path) -> None:
store, skills_dir, prompts_dir, baseline_db = _make_seed_fixtures(tmp_path)
init_seed(store, "dup", skills_dir, prompts_dir, baseline_db, "r1", None, "first")
with pytest.raises(FileExistsError, match="种子已存在"):
init_seed(
store, "dup", skills_dir, prompts_dir, baseline_db, "r1", None, "second"
)
init_seed(store, "dup", skills_dir, prompts_dir, baseline_db, "r1", None, "second")
class TestListSeeds:
"""list_seeds 列出所有种子。"""
def test_list_seeds(self, tmp_path: "Path") -> None:
def test_list_seeds(self, tmp_path: Path) -> None:
store, skills_dir, prompts_dir, baseline_db = _make_seed_fixtures(tmp_path)
init_seed(store, "beta", skills_dir, prompts_dir, baseline_db, "r1", None, "b")
init_seed(store, "alpha", skills_dir, prompts_dir, baseline_db, "r1", None, "a")
assert list_seeds(store) == ["alpha", "beta"]
def test_list_seeds_empty(self, tmp_path: "Path") -> None:
def test_list_seeds_empty(self, tmp_path: Path) -> None:
store = tmp_path / "store"
assert list_seeds(store) == []
@@ -271,14 +261,14 @@ class TestListSeeds:
class TestReadSeed:
"""read_seed 读取 seed.json。"""
def test_read_seed(self, tmp_path: "Path") -> None:
def test_read_seed(self, tmp_path: Path) -> None:
store, skills_dir, prompts_dir, baseline_db = _make_seed_fixtures(tmp_path)
init_seed(store, "s1", skills_dir, prompts_dir, baseline_db, "r1", None, "desc")
meta = read_seed(store, "s1")
assert meta["baseline_run_id"] == "r1"
assert meta["description"] == "desc"
def test_read_seed_not_found(self, tmp_path: "Path") -> None:
def test_read_seed_not_found(self, tmp_path: Path) -> None:
store = tmp_path / "store"
store.mkdir()
with pytest.raises(FileNotFoundError, match="种子不存在"):
@@ -296,22 +286,17 @@ class TestExtractRunDb:
def _make_src_db(self, path):
"""创建带 _runs + predictions 表的源 db。"""
conn = sqlite3.connect(path)
conn.execute(
"CREATE TABLE _runs (run_id TEXT PRIMARY KEY, status TEXT)"
)
conn.execute("CREATE TABLE _runs (run_id TEXT PRIMARY KEY, status TEXT)")
conn.execute("INSERT INTO _runs VALUES ('r1', 'done')")
conn.execute("INSERT INTO _runs VALUES ('r2', 'done')")
conn.execute(
"CREATE TABLE predictions "
"(run_id TEXT, question_id TEXT, answer TEXT)"
)
conn.execute("CREATE TABLE predictions (run_id TEXT, question_id TEXT, answer TEXT)")
conn.execute("INSERT INTO predictions VALUES ('r1', 'q1', 'A')")
conn.execute("INSERT INTO predictions VALUES ('r1', 'q2', 'B')")
conn.execute("INSERT INTO predictions VALUES ('r2', 'q1', 'C')")
conn.commit()
conn.close()
def test_extract_run_db_preserves_pk(self, tmp_path: "Path") -> None:
def test_extract_run_db_preserves_pk(self, tmp_path: Path) -> None:
"""原始 CREATE 保留主键约束。"""
src = tmp_path / "src.db"
dst = tmp_path / "dst.db"
@@ -334,7 +319,7 @@ class TestExtractRunDb:
assert len(preds) == 2
conn.close()
def test_extract_run_db_missing_table(self, tmp_path: "Path") -> None:
def test_extract_run_db_missing_table(self, tmp_path: Path) -> None:
"""源 db 无目标表时报错。"""
src = tmp_path / "src.db"
dst = tmp_path / "dst.db"
@@ -345,7 +330,7 @@ class TestExtractRunDb:
with pytest.raises(RuntimeError, match="源 db 无表"):
extract_run_db(src, dst, "r1")
def test_extract_run_db_no_rows(self, tmp_path: "Path") -> None:
def test_extract_run_db_no_rows(self, tmp_path: Path) -> None:
"""目标 run_id 不存在时报错。"""
src = tmp_path / "src.db"
dst = tmp_path / "dst.db"
@@ -382,9 +367,7 @@ def _make_promote_fixtures(tmp_path):
prompts_version TEXT
)
""")
conn.execute(
"INSERT INTO _runs VALUES ('eval_001', 'v2', 'v2')"
)
conn.execute("INSERT INTO _runs VALUES ('eval_001', 'v2', 'v2')")
conn.execute("""
CREATE TABLE predictions (
run_id TEXT, question_id TEXT, answer TEXT
@@ -400,7 +383,7 @@ def _make_promote_fixtures(tmp_path):
class TestPromoteToSeed:
"""promote_to_seed 固化 workspace 版本为种子。"""
def test_promote_to_seed_success(self, tmp_path: "Path") -> None:
def test_promote_to_seed_success(self, tmp_path: Path) -> None:
ws, store = _make_promote_fixtures(tmp_path)
seed_dir = promote_to_seed(ws, store, "v2", "eval_001", "evolved-seed", "good")
assert seed_dir == store / "seeds" / "evolved-seed"
@@ -411,13 +394,13 @@ class TestPromoteToSeed:
assert meta["baseline_run_id"] == "eval_001"
assert meta["parent"] == "ws:v2"
def test_promote_to_seed_version_mismatch(self, tmp_path: "Path") -> None:
def test_promote_to_seed_version_mismatch(self, tmp_path: Path) -> None:
"""eval run 的 skills_version 与 --version 不符时报错。"""
ws, store = _make_promote_fixtures(tmp_path)
with pytest.raises(ValueError, match="版本.*不符"):
promote_to_seed(ws, store, "v3", "eval_001", "bad", "mismatch")
def test_promote_to_seed_null_version(self, tmp_path: "Path") -> None:
def test_promote_to_seed_null_version(self, tmp_path: Path) -> None:
"""eval run 的版本为 NULL 时报错。"""
ws = tmp_path / "ws2"
ws.mkdir()
@@ -438,20 +421,20 @@ class TestPromoteToSeed:
with pytest.raises(ValueError, match="NULL"):
promote_to_seed(ws, store, "v1", "eval_null", "bad", "null ver")
def test_promote_to_seed_run_not_found(self, tmp_path: "Path") -> None:
def test_promote_to_seed_run_not_found(self, tmp_path: Path) -> None:
"""eval run 不存在时报错。"""
ws, store = _make_promote_fixtures(tmp_path)
with pytest.raises(ValueError, match="eval run 不存在"):
promote_to_seed(ws, store, "v1", "nonexistent", "bad", "no run")
def test_promote_cleanup_tmp_db(self, tmp_path: "Path") -> None:
def test_promote_cleanup_tmp_db(self, tmp_path: Path) -> None:
"""finally 清理临时 db 文件。"""
ws, store = _make_promote_fixtures(tmp_path)
promote_to_seed(ws, store, "v2", "eval_001", "clean-test", "cleanup")
# 临时 db 应已清理
assert not (ws / "_promote_tmp.db").exists()
def test_promote_cleanup_tmp_db_on_error(self, tmp_path: "Path") -> None:
def test_promote_cleanup_tmp_db_on_error(self, tmp_path: Path) -> None:
"""即使 init_seed 失败(同名种子),临时 db 也应被清理。"""
ws, store = _make_promote_fixtures(tmp_path)
promote_to_seed(ws, store, "v2", "eval_001", "first", "first time")