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
228 lines
16 KiB
Python
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()
|