Files
Agent-System-Dev/tests/test_component_checks.py
T
weichen adb6cbcf48 Keep fixed checks and lifecycle fixtures isolated in CI (U5)
Why:
Go-to-Python tests leave ignored bytecode in fixed sources, lifecycle clones need a fixed main reference, and boundary gates require ripgrep.

Evidence:
SELF real byproduct and clone regressions failed before repair; host passed 11 component cases, 4 hook cases and 33 CI credential cases.

Impact:
Execute versioned Go checks in disposable clones while preserving editable overlays and strict dirty guards; install the boundary prerequisite and seed only isolated pinned lifecycle refs.

Refs:
R8-R10, R14-R15; dependency-workspace U5; Dev PR 537 run 3036
2026-10-14 02:29:36 +00:00

228 lines
16 KiB
Python

"""Local named checks prepare fixed sources and retain edits with real Go/Git."""
import json
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
SOURCE = Path(__file__).resolve().parents[1]
@unittest.skipUnless(shutil.which("go"), "Go required")
class ComponentCheckTests(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory(prefix="component-check-")
self.addCleanup(self.tmp.cleanup)
self.base = Path(self.tmp.name)
self.root = self.base / "dev"
self.root.mkdir()
self.env = os.environ.copy()
for key in list(self.env):
if key.startswith("GIT_CONFIG_") or key in ("GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE", "GOWORK", "COMPOSITION_SOURCE_MAP", "CI_DEPENDENCY_MIRRORS"):
self.env.pop(key, None)
self.env.update(GIT_CONFIG_NOSYSTEM="1", GIT_CONFIG_GLOBAL="/dev/null", GIT_ALLOW_PROTOCOL="file",
GIT_AUTHOR_NAME="Checks", GIT_AUTHOR_EMAIL="check@example.invalid",
GIT_COMMITTER_NAME="Checks", GIT_COMMITTER_EMAIL="check@example.invalid",
GOTOOLCHAIN="local", GOPROXY="off", GOSUMDB="off")
self.call(self.root, "git", "init", "--initial-branch=main")
shutil.copytree(SOURCE / "scripts", self.root / "scripts")
shutil.copyfile(SOURCE / "Makefile", self.root / "Makefile")
(self.root / ".gitignore").write_text(".workspace/\ngo.work\ngo.work.sum\ntests/integration/multi-instance/.build/\n")
app = self.base / "backend"
self.backend = app
app.mkdir()
self.call(app, "git", "init", "--initial-branch=main")
(app / "go.mod").write_text("module example.com/backend\n\ngo 1.23\n")
(app / "source.go").write_text("package backend\nfunc Value() string {return \"fixed\"}\n")
(app / "source_test.go").write_text('package backend\nimport "testing"\nfunc TestValue(t *testing.T) {if Value() != "fixed" {t.Fatal(Value())}}\n')
(app / "cmd/agent-system").mkdir(parents=True)
(app / "cmd/agent-system/main.go").write_text('package main\nimport "fmt"\nfunc main(){fmt.Print("fixed-binary")}\n')
self.call(app, "git", "add", ".")
self.call(app, "git", "commit", "-m", "Fixed Backend")
sha = self.call(app, "git", "rev-parse", "HEAD").stdout.strip()
manifest = {"schema": 1, "components": [{"id": "Agent-System-Backend", "repo": str(app), "path": ".workspace/Agent-System-Backend", "legacy_path": "modules/backend", "role": "application", "modules": [{"path": "example.com/backend", "directory": "."}], "target": "main"}]}
(self.root / "components.json").write_text(json.dumps(manifest))
(self.root / "components.lock.json").write_text(json.dumps({"schema": 1, "components": {"Agent-System-Backend": {"commit": sha}}}))
self.call(self.root, "git", "add", ".")
self.call(self.root, "git", "commit", "-m", "Fixed Dev")
def call(self, cwd, *args, check=True, env=None):
result = subprocess.run(args, cwd=cwd, env=env or self.env, text=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=90)
if check:
self.assertEqual(result.returncode, 0, result.stdout)
return result
def test_backend_check_prepares_fixed_source_and_replays_owner_edits(self):
result = self.call(self.root, "make", "test-backend")
self.assertIn("GOWORK=off", result.stdout)
child = self.root / ".workspace/Agent-System-Backend"
self.assertEqual((child / "source.go").read_text(), 'package backend\nfunc Value() string {return "fixed"}\n')
self.call(self.root, "python3", "scripts/workspace.py", "--root", str(self.root), "edit", "--name", "Agent-System-Backend", "--branch", "feature/owned")
(child / "source.go").write_text('package backend\nfunc Value() string {return "owner-edit"}\n')
result = self.call(self.root, "make", "test-backend", check=False)
self.assertNotEqual(result.returncode, 0, result.stdout)
self.assertIn("owner-edit", result.stdout)
self.assertEqual(self.call(child, "git", "branch", "--show-current").stdout.strip(), "feature/owned")
self.assertIn('"owner-edit"', (child / "source.go").read_text())
def test_pg_gate_requires_environment_and_forwards_it_to_real_go_tests(self):
result = self.call(self.root, "make", "test-backend-pg", check=False)
self.assertNotEqual(result.returncode, 0, result.stdout)
self.assertFalse((self.root / ".workspace").exists())
self.call(self.root, "python3", "scripts/workspace.py", "--root", str(self.root), "edit", "--name", "Agent-System-Backend", "--branch", "feature/pg-gate")
child = self.root / ".workspace/Agent-System-Backend"
(child / "pg_gate_test.go").write_text('package backend\nimport("os";"testing")\nfunc TestGateEnv(t *testing.T){if os.Getenv("AGENT_PG_TEST_DSN")!="fixture-dsn"||os.Getenv("AGENT_REDIS_TEST_ADDR")!="fixture-redis"{t.Fatal("gate environment missing")}}\n')
self.call(self.root, "make", "test-backend-pg", env=self.env | {"AGENT_PG_TEST_DSN": "fixture-dsn", "AGENT_REDIS_TEST_ADDR": "fixture-redis"})
def test_multi_instance_binary_is_built_inside_dev_root_and_reached_by_suite(self):
suite = self.add_multi_instance_suite()
self.call(self.root, "make", "test-multi-instance", env=self.env | {"AGENT_PG_TEST_DSN": "fixture-dsn"})
self.assertTrue((suite / ".build/agent-system").is_file())
self.assertFalse((self.root / ".workspace/tests").exists())
def add_multi_instance_suite(self):
suite = self.root / "tests/integration/multi-instance"
suite.mkdir(parents=True)
(suite / "go.mod").write_text("module example.com/multi\n\ngo 1.23\n")
(suite / "binary_test.go").write_text('package multi\nimport("os";"os/exec";"testing")\nfunc TestBinary(t *testing.T){b,e:=exec.Command(os.Getenv("AGENT_BIN")).Output();if e!=nil||string(b)!="fixed-binary"{t.Fatalf("%s %v",b,e)}}\n')
return suite
def add_go_byproduct_fixture(self, fail=False):
(self.backend / ".gitignore").write_text("__pycache__/\n.fixture-output\n")
(self.backend / "fixture_module.py").write_text("VALUE = 'imported from Go'\n")
(self.backend / "byproduct_test.go").write_text('''package backend
import("os";"os/exec";"testing")
func TestByproducts(t *testing.T) {
if err := os.WriteFile(".fixture-output", []byte("generated by Go test"), 0600); err != nil {t.Fatal(err)}
cmd := exec.Command("python3", "-c", "import fixture_module")
cmd.Env = append(os.Environ(), "PYTHONDONTWRITEBYTECODE=", "PYTHONPYCACHEPREFIX=")
if out, err := cmd.CombinedOutput(); err != nil {t.Fatalf("%s %v", out, err)}
''' + (' t.Fatal("deliberate fixture failure")\n' if fail else '') + '}\n')
self.call(self.backend, "git", "add", ".")
self.call(self.backend, "git", "commit", "-m", "Add Go and Python byproducts fixture")
lock = json.loads((self.root / "components.lock.json").read_text())
lock["components"]["Agent-System-Backend"]["commit"] = self.call(self.backend, "git", "rev-parse", "HEAD").stdout.strip()
(self.root / "components.lock.json").write_text(json.dumps(lock))
self.call(self.root, "git", "add", "components.lock.json")
self.call(self.root, "git", "commit", "-m", "Pin byproducts fixture")
self.call(self.root, "python3", "scripts/workspace.py", "--root", str(self.root), "prepare")
return self.root / ".workspace/Agent-System-Backend"
def source_snapshot(self, child):
return {"head": self.call(child, "git", "rev-parse", "HEAD").stdout,
"branch": self.call(child, "git", "branch", "--show-current").stdout,
"index": (child / ".git/index").read_bytes(),
"status": self.call(child, "git", "status", "--porcelain", "--untracked-files=all").stdout,
"ignored": self.call(child, "git", "ls-files", "--others", "--ignored", "--exclude-standard").stdout,
"files": {path.relative_to(child).as_posix(): path.read_bytes() for path in child.rglob("*")
if path.is_file() and ".git" not in path.relative_to(child).parts}}
def test_fixed_go_checks_keep_source_clean_across_pg_and_multi_instance(self):
child = self.add_go_byproduct_fixture()
suite = self.add_multi_instance_suite()
before = self.source_snapshot(child)
env = self.env | {"AGENT_PG_TEST_DSN": "fixture-dsn", "AGENT_REDIS_TEST_ADDR": "fixture-redis"}
self.call(self.root, "make", "test-backend-pg", env=env)
generated = self.call(child, "git", "ls-files", "--others", "--ignored", "--exclude-standard").stdout
print("fixed checkout byproducts after PostgreSQL fixture: " + repr(generated), flush=True)
self.call(self.root, "make", "test-multi-instance", env=env)
self.assertEqual(self.source_snapshot(child), before)
self.assertTrue((suite / ".build/agent-system").is_file())
def test_fixed_go_failure_is_reported_without_polluting_source(self):
child = self.add_go_byproduct_fixture(fail=True)
before = self.source_snapshot(child)
result = self.call(self.root, "make", "test-backend", check=False)
self.assertNotEqual(result.returncode, 0, result.stdout)
self.assertIn("deliberate fixture failure", result.stdout)
self.assertIn("local component command failed (1)", result.stdout)
self.assertEqual(self.source_snapshot(child), before)
def test_fixed_go_checks_refuse_and_preserve_owner_changes_and_ignored_files(self):
child = self.add_go_byproduct_fixture()
for kind in ("unstaged", "staged", "ignored"):
with self.subTest(kind=kind):
if kind == "ignored":
(child / ".fixture-output").write_text("owner's unique ignored file")
else:
(child / "source.go").write_text('package backend\nfunc Value() string {return "owner-edit"}\n')
if kind == "staged":
self.call(child, "git", "add", "source.go")
before = self.source_snapshot(child)
result = self.call(self.root, "make", "test-backend", check=False)
self.assertNotEqual(result.returncode, 0, result.stdout)
self.assertIn("dirty or ignored unique files", result.stdout)
self.assertEqual(self.source_snapshot(child), before)
# This fixture owns these files and its index; reset only between cases.
if kind != "ignored":
self.call(child, "git", "restore", "--source=HEAD", "--staged", "--worktree", "source.go")
def test_pg_and_multi_instance_prerequisites_still_refuse_before_preparing_sources(self):
for target, supplied in (("test-backend-pg", {}),
("test-backend-pg", {"AGENT_PG_TEST_DSN": "fixture-dsn"}),
("test-multi-instance", {})):
with self.subTest(target=target, supplied=supplied):
env = {key: value for key, value in self.env.items()
if key not in ("AGENT_PG_TEST_DSN", "AGENT_REDIS_TEST_ADDR")}
result = self.call(self.root, "make", target, check=False, env=env | supplied)
self.assertNotEqual(result.returncode, 0, result.stdout)
self.assertFalse((self.root / ".workspace").exists())
def test_new_pin_gate_validates_fixed_sources_and_rejects_local_replaces(self):
self.call(self.root, "python3", "scripts/workspace.py", "--root", str(self.root), "prepare")
self.call(self.root, "bash", "scripts/check-pins.sh")
child = self.root / ".workspace/Agent-System-Backend"
(child / "go.mod").write_text((child / "go.mod").read_text() + "\nreplace example.com/dependency => ../local\n")
result = self.call(self.root, "bash", "scripts/check-pins.sh", check=False)
self.assertNotEqual(result.returncode, 0, result.stdout)
self.assertIn("local replace", result.stdout)
def test_registered_private_transport_reaches_real_go_process_without_global_change(self):
manifest = json.loads((self.root / "components.json").read_text())
manifest["transport"] = {"go_private": ["private.example.test"], "git_rewrites": [{"from": "https://private.example.test/", "to": self.base.as_uri() + "/"}]}
(self.root / "components.json").write_text(json.dumps(manifest))
global_config = self.base / "global.gitconfig"
global_config.write_text("[user]\n name = existing-user\n")
env = self.env | {"GIT_CONFIG_GLOBAL": str(global_config)}
self.call(self.root, "python3", "scripts/workspace.py", "--root", str(self.root), "edit", "--name", "Agent-System-Backend", "--branch", "feature/transport", env=env)
child = self.root / ".workspace/Agent-System-Backend"
(child / "transport_test.go").write_text('package backend\nimport("os";"testing")\nfunc TestTransport(t *testing.T){if os.Getenv("GOPRIVATE")!="private.example.test"||os.Getenv("GOWORK")!="off"||os.Getenv("GIT_CONFIG_COUNT")==""{t.Fatal("process-scoped transport missing")}}\n')
before = global_config.read_bytes()
self.call(self.root, "make", "test-backend", env=env)
self.assertEqual(global_config.read_bytes(), before)
def test_explicit_overlay_uses_registered_workspace_and_preserves_default_version_mode(self):
self.call(self.root, "python3", "scripts/workspace.py", "--root", str(self.root), "edit", "--name", "Agent-System-Backend", "--branch", "feature/overlay")
child = self.root / ".workspace/Agent-System-Backend"
work = self.root / "go.work"
(child / "overlay_test.go").write_text('package backend\nimport("os";"testing")\nfunc TestOverlay(t *testing.T){if os.Getenv("GOWORK")!="' + str(work) + '"{t.Fatal("registered overlay missing")}}\n')
self.call(self.root, "make", "test-backend", "LOCAL_OVERLAY=1")
default = self.call(self.root, "make", "test-backend", check=False)
self.assertNotEqual(default.returncode, 0, default.stdout)
self.assertIn("registered overlay missing", default.stdout)
self.assertTrue(work.is_file())
def test_make_pin_gate_uses_map_in_gitless_formal_root_and_ignores_external_rdm_identity(self):
self.call(self.root, "python3", "scripts/workspace.py", "--root", str(self.root), "prepare")
formal = self.base / "formal"
formal.mkdir()
shutil.copytree(self.root / "scripts", formal / "scripts")
for name in ("Makefile", "components.json", "components.lock.json"):
shutil.copy2(self.root / name, formal / name)
entry = json.loads((self.root / "components.json").read_text())["components"][0]
locked = json.loads((self.root / "components.lock.json").read_text())["components"][entry["id"]]["commit"]
mapping = {"schema": 1, "root_commit": self.call(self.root, "git", "rev-parse", "HEAD").stdout.strip(),
"components": {entry["id"]: {"path": str(self.root / entry["path"]), "repo": entry["repo"], "commit": locked},
"Agent-System-RDM": {"path": str(self.base / "external-rdm"), "repo": "external registered by composer", "commit": locked}}}
source_map = self.base / "map.json"
source_map.write_text(json.dumps(mapping))
self.call(formal, "make", "test-pins", env=self.env | {"COMPOSITION_SOURCE_MAP": str(source_map)})
self.assertFalse((formal / ".workspace").exists())
if __name__ == "__main__":
unittest.main()