from __future__ import annotations import subprocess import sys import tempfile import unittest from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] SCRIPTS_DIR = REPO_ROOT / "skills" / "ack" / "scripts" VALIDATOR = SCRIPTS_DIR / "validate_regression.py" SELECTOR = SCRIPTS_DIR / "select_regression.py" EXAMPLE = REPO_ROOT / "skills" / "ack" / "examples" / "regression.example.yaml" sys.path.insert(0, str(SCRIPTS_DIR)) import select_regression # noqa: E402 import validate_regression # noqa: E402 def empty_catalog() -> dict: return { "version": 1, "updatedAt": "2026-08-25T10:00:00+08:00", "project": {"name": "demo"}, "cases": [], } def sample_case(**overrides: object) -> dict: case = { "id": "REG-login-001", "title": "登录后进入工作台", "status": "active", "source": {"taskId": "BUG-001", "kind": "bug"}, "suite": "smoke", "surface": "browser", "setup": "使用测试账号 A,未登录", "steps": ["打开 /login", "输入账号 A 并提交"], "expected": [ {"kind": "visible-text", "value": "工作台"}, {"kind": "url", "value": "/dashboard"}, ], } case.update(overrides) return case class AckRegressionValidationTests(unittest.TestCase): def test_example_and_template_are_valid(self) -> None: for no_site_packages in (False, True): command = [sys.executable] if no_site_packages: command.append("-S") result = subprocess.run( [*command, str(VALIDATOR), str(EXAMPLE)], cwd=REPO_ROOT, text=True, capture_output=True, check=False, ) with self.subTest(no_site_packages=no_site_packages): self.assertEqual(result.returncode, 0, result.stderr) self.assertIn("回归目录校验通过", result.stdout) template = REPO_ROOT / "skills" / "ack" / "templates" / "regression.template.yaml" rendered = template.read_text(encoding="utf-8").replace( "", "demo" ).replace("", "2026-08-25T10:00:00+08:00") with tempfile.TemporaryDirectory() as temp_dir: path = Path(temp_dir) / "regression.yaml" path.write_text(rendered, encoding="utf-8") result = subprocess.run( [sys.executable, str(VALIDATOR), str(path)], cwd=REPO_ROOT, text=True, capture_output=True, check=False, ) self.assertEqual(result.returncode, 0, result.stderr) def test_rejects_duplicate_ids_and_unknown_fields(self) -> None: catalog = empty_catalog() catalog["cases"] = [sample_case(), sample_case()] errors = validate_regression.validate_builtin(catalog) self.assertTrue(any("不能重复" in item for item in errors)) catalog["cases"] = [sample_case(extra="nope")] errors = validate_regression.validate_builtin(catalog) self.assertTrue(any("未知字段" in item for item in errors)) def test_selects_smoke_by_default_and_full_includes_all_active(self) -> None: catalog = empty_catalog() catalog["cases"] = [ sample_case(), sample_case( id="REG-notes-create-001", suite="full", surface="api", source={"taskId": "FEAT-012", "kind": "feature"}, ), sample_case(id="REG-old-001", status="retired"), ] smoke = select_regression.select_cases( catalog, suite="smoke", case_ids=[], limit=50 ) full = select_regression.select_cases( catalog, suite="full", case_ids=[], limit=50 ) self.assertEqual([case["id"] for case in smoke], ["REG-login-001"]) self.assertEqual( [case["id"] for case in full], ["REG-login-001", "REG-notes-create-001"], ) def test_selector_cli_and_task_link(self) -> None: result = subprocess.run( [sys.executable, str(SELECTOR), str(EXAMPLE), "--format", "ids"], cwd=REPO_ROOT, text=True, capture_output=True, check=False, ) self.assertEqual(result.returncode, 0, result.stderr) self.assertEqual(result.stdout.strip(), "REG-login-001") tasks = { "project": { "name": "notes-web", "regressionFile": ".pouch/ack/regression.yaml", }, "regressionRuns": [], "tasks": [ { "id": "BUG-001", "regressionRefs": ["REG-login-001"], } ], } catalog = validate_regression.load_yaml(EXAMPLE, "回归目录") self.assertEqual(validate_regression.validate_tasks_link(catalog, tasks), []) tasks["project"]["regressionFile"] = ".pouch/ack/other.yaml" errors = validate_regression.validate_tasks_link(catalog, tasks) self.assertTrue(any("必须固定为 .pouch/ack/regression.yaml" in item for item in errors)) if __name__ == "__main__": unittest.main()