Files
Agent-System-Dev/tests/test_test_affected.py
T

151 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""按改动选检查(scripts/test_affected.py):临时 Git 仓库 + 本地裸 origin,全部离线。"""
import os
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
SOURCE = Path(__file__).resolve().parents[1]
SCRIPT = SOURCE / "scripts" / "test_affected.py"
class TestAffectedTests(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory(prefix="test-affected-")
self.addCleanup(self.tmp.cleanup)
root = Path(self.tmp.name)
self.repo = root / "repo"
self.env = {k: v for k, v in os.environ.items()
if not k.startswith(("GIT_", "MAKE")) and k not in {"BASE", "TASK_TARGET_BRANCH"}}
self.env.update({"GIT_CONFIG_NOSYSTEM": "1", "GIT_CONFIG_GLOBAL": "/dev/null",
"GIT_TERMINAL_PROMPT": "0", "HOME": str(root / "no-home"),
"GIT_AUTHOR_NAME": "Affected Test", "GIT_AUTHOR_EMAIL": "affected@example.invalid",
"GIT_COMMITTER_NAME": "Affected Test",
"GIT_COMMITTER_EMAIL": "affected@example.invalid"})
self.git(root, "init", "-q", "--bare", "--initial-branch=main", str(root / "origin.git"))
self.git(root, "init", "-q", "--initial-branch=main", str(self.repo))
self.git(self.repo, "remote", "add", "origin", str(root / "origin.git"))
self.write("README.md", "fixture\n")
self.write("modules/ui/notes.txt", "ui\n")
self.commit("Create fixture")
self.git(self.repo, "push", "-q", "origin", "main")
self.git(self.repo, "checkout", "-q", "-b", "feature/work")
def git(self, cwd, *args):
return subprocess.run(["git", *args], cwd=cwd, env=self.env, capture_output=True, text=True,
check=True).stdout.strip()
def write(self, rel, text):
path = self.repo / rel
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding="utf-8")
def commit(self, message, gitlinks=()):
self.git(self.repo, "add", "-A")
sha = self.git(self.repo, "rev-parse", "HEAD") if gitlinks else None
for path in gitlinks:
self.git(self.repo, "update-index", "--add", "--cacheinfo", f"160000,{sha},{path}")
self.git(self.repo, "commit", "-q", "-m", message)
def run_affected(self, *args, base=None):
env = self.env | ({"BASE": base} if base is not None else {})
result = subprocess.run([sys.executable, str(SCRIPT), *args], cwd=self.repo, env=env,
capture_output=True, text=True, timeout=60)
return result.returncode, result.stdout + result.stderr
def summary(self, out):
lines = [line for line in out.splitlines() if line.startswith("[affected] base=")]
self.assertEqual(len(lines), 1, out)
return dict(part.split("=", 1) for part in lines[0].split()[1:])
def test_mapping_dry_run_orders_and_dedupes_targets(self):
self.write("scripts/tool.py", "print('x')\n")
self.write("tests/test_tool.py", "\n")
self.write("modules/core/a.go", "package a\n")
self.write("deps/agent-native/x.go", "package x\n")
self.write("deps/agent-squad/y.go", "package y\n")
self.write("docs/guide.md", "doc\n")
self.write(".task/card.md", "card\n")
self.commit("Branch work", gitlinks=["deps/agent-command"])
self.write("modules/ui/notes.txt", "uncommitted tracked change\n")
code, out = self.run_affected("--dry-run")
self.assertEqual(code, 0, out)
info = self.summary(out)
self.assertEqual(info["base"], "origin/main")
self.assertEqual(info["changed"], "9", out)
self.assertEqual(info["targets"], "test-worktree,test-governance,test-boundaries,test-pins,test-libs,"
"test-core,test-ui,test-agentcmd", out)
self.assertIn("[affected] dry-run: make test-worktree test-governance", out)
def test_gitlink_and_gitmodules_select_pins_and_module_target(self):
self.write(".gitmodules", "[submodule \"modules/backend\"]\n\tpath = modules/backend\n")
self.commit("Bump backend", gitlinks=["modules/backend"])
code, out = self.run_affected("--dry-run")
self.assertEqual(code, 0, out)
self.assertEqual(self.summary(out)["targets"],
"test-worktree,test-boundaries,test-pins,test-backend", out)
def test_lint_boundaries_and_governance_docs_select_targets(self):
self.write("scripts/lint-boundaries.sh", "#!/bin/sh\n")
self.write("AGENT-EXECUTION.md", "x\n")
self.commit("Lint")
code, out = self.run_affected("--dry-run")
self.assertEqual(code, 0, out)
self.assertEqual(self.summary(out)["targets"], "test-worktree,test-governance,test-boundaries", out)
def test_docs_only_change_runs_nothing(self):
self.write("docs/guide.md", "doc\n")
self.write(".task/card.md", "card\n")
self.write("NOTES.md", "notes\n")
self.commit("Docs only")
self.write("README.md", "edited\n")
code, out = self.run_affected()
self.assertEqual(code, 0, out)
info = self.summary(out)
self.assertEqual((info["changed"], info["targets"]), ("4", "none"), out)
self.assertIn("[affected] nothing to test", out)
self.assertNotIn("dry-run", out)
def test_base_env_resolves_bare_branch_to_origin(self):
self.write("scripts/tool.py", "x\n")
self.commit("Tool")
code, out = self.run_affected("--dry-run", base="main")
self.assertEqual(code, 0, out)
self.assertEqual(self.summary(out)["base"], "origin/main")
def test_base_env_local_branch_limits_changes_since_that_base(self):
self.write("modules/core/a.go", "package a\n")
self.commit("Core")
self.git(self.repo, "branch", "local-base")
self.write("docs/later.md", "later\n")
self.commit("Later docs")
code, out = self.run_affected("--dry-run", base="local-base")
self.assertEqual(code, 0, out)
info = self.summary(out)
self.assertEqual((info["base"], info["changed"], info["targets"]), ("local-base", "1", "none"), out)
code, out = self.run_affected("--dry-run", "--base", "origin/main", base="local-base")
self.assertEqual(self.summary(out)["base"], "origin/main", out)
def test_unresolvable_base_is_a_usage_error(self):
code, out = self.run_affected("--dry-run", base="no-such-branch")
self.assertEqual(code, 2, out)
self.assertIn("[affected] ERROR base no-such-branch does not resolve", out)
def test_default_base_is_card_immediate_target(self):
self.git(self.repo, "push", "-q", "origin", "main:feature/init")
self.git(self.repo, "fetch", "-q", "origin")
self.write(".task/child.md", "# child\n- 工作区:/tmp/x;分支:feature/work\n"
"- 分支类型:child;initiative_id:init;root_target:main;immediate_target:feature/init\n")
self.commit("Card")
code, out = self.run_affected("--dry-run")
self.assertEqual(code, 0, out)
self.assertEqual(self.summary(out)["base"], "origin/feature/init", out)
self.git(self.repo, "checkout", "-q", "--detach")
code, out = self.run_affected("--dry-run")
self.assertEqual(self.summary(out)["base"], "origin/main", out)
if __name__ == "__main__":
unittest.main()