Files
aios/tests/baseline_v1_1/test_package_tools.py
T
owner 6ffd7fe19c feat(governed): port Implementation Baseline 1.1 (57 files)
Apply the approved Implementation Baseline 1.1 architecture revisions onto the
current mainline, verified faithful to the delivered change bundle:

- Add aios_baseline: signed owner-delegation-grant.v1 / operational-epoch.v1 /
  task-principal.v1 auth chain, runtime contract + deployment-truth evaluator,
  declarative operation registry (effect-class + fail-closed dispatch),
  lineage ledger, release/recipe/saga ledger, bounded optimization controller,
  authenticated HTTP transport, resident worker loop and systemd host probe.
- Extend existing Job authority (aios_gitops_job_controller/executor) with
  durable intent/receipt/reconcile and fail-closed admission; harden
  task_plane/store heartbeat CAS and terminal-state fencing; make
  reconciliation_controller fail-closed on incomplete/drift evidence.
- Add isolated-validation profile templates (deploy/baseline-v1.1, systemd
  .example), source change/rollback tooling (tools/baseline_v1_1), the
  AT-01..AT-23 acceptance matrix, M0-M5 runbook, installation/rollback docs and
  the baseline_v1_1 test suite.
- Keep legacy write authorities untouched; isolated-validation profile only.

Staged blobs verified byte-identical to the delivered source-changes bundle.
Cross-platform baseline tests pass; systemd/fcntl-dependent paths require the
declared Linux x86_64 + CPython 3.12 + systemd runtime (site-bound).
2026-09-08 03:51:26 +08:00

142 lines
5.9 KiB
Python

"""Exercise package installation against real temporary Git repositories."""
from pathlib import Path
import importlib.util
import json
import subprocess
import zipfile
import pytest
ROOT = Path(__file__).resolve().parents[2]
SPEC = importlib.util.spec_from_file_location("baseline_bundle", ROOT / "tools/baseline_v1_1/bundle.py")
bundle = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(bundle)
def git(root, *args):
return subprocess.check_output(["git", "-C", str(root), *args], text=True).strip()
@pytest.fixture
def repos(tmp_path):
source = tmp_path / "source"
source.mkdir()
git(source, "init", "--quiet")
(source / "app.py").write_text("print('before')\n")
(source / "retired.txt").write_text("historical\n")
git(source, "add", ".")
git(source, "-c", "user.name=Test", "-c", "user.email=test@localhost", "commit", "--quiet", "-m", "base")
revision = git(source, "rev-parse", "HEAD")
work = tmp_path / "work"
subprocess.run(["git", "clone", "--quiet", str(source), str(work)], check=True)
(work / "app.py").write_text("print('after')\n")
(work / "retired.txt").unlink()
(work / "new").mkdir()
(work / "new/tool.py").write_text("print('new')\n")
artifact = tmp_path / "change.zip"
bundle.create_bundle(work, revision, artifact)
return source, work, revision, artifact
def test_stage_dry_run_install_and_conflict_safe_rollback(repos, tmp_path):
source, work, revision, artifact = repos
target = tmp_path / "staged"
result = bundle.stage_bundle(artifact, source, target, dry_run=True)
assert result["status"] == "DRY_RUN"
assert not target.exists()
assert (source / "app.py").read_text() == "print('before')\n"
result = bundle.stage_bundle(artifact, source, target)
assert result["status"] == "STAGED"
assert (target / "app.py").read_text() == "print('after')\n"
assert not (target / "retired.txt").exists()
assert (target / "new/tool.py").is_file()
assert git(target, "rev-parse", "HEAD") == revision
with pytest.raises(bundle.PackageError, match="exists"):
bundle.stage_bundle(artifact, source, target)
(target / "app.py").write_text("owner edited\n")
with pytest.raises(bundle.PackageError, match="changed"):
bundle.rollback(target)
assert (target / "app.py").read_text() == "owner edited\n"
assert (target / "new/tool.py").exists()
(target / "app.py").write_text("print('after')\n")
assert bundle.rollback(target, dry_run=True)["status"] == "DRY_RUN"
assert bundle.rollback(target)["status"] == "ROLLED_BACK"
assert not (target / "new/tool.py").exists()
assert (target / "retired.txt").read_text() == "historical\n"
assert not git(target, "status", "--porcelain")
def test_dirty_wrong_base_and_unknown_destination_refused(repos, tmp_path):
source, work, revision, artifact = repos
(source / "local.txt").write_text("do not overwrite")
with pytest.raises(bundle.PackageError, match="clean"):
bundle.stage_bundle(artifact, source, tmp_path / "one")
(source / "local.txt").unlink()
(source / "app.py").write_text("another base")
git(source, "add", ".")
git(source, "-c", "user.name=Test", "-c", "user.email=test@localhost", "commit", "--quiet", "-m", "different")
with pytest.raises(bundle.PackageError, match="baseline"):
bundle.stage_bundle(artifact, source, tmp_path / "two")
assert not (tmp_path / "two").exists()
def test_tamper_traversal_extra_entries_and_symlink_refused(repos, tmp_path):
source, work, revision, artifact = repos
with zipfile.ZipFile(artifact) as z:
entries = {name: z.read(name) for name in z.namelist()}
for index, change in enumerate((
{"new/app.py": b"tampered"},
{"../escape": b"bad"},
{"unlisted.py": b"bad"},
)):
bad = tmp_path / f"bad-{index}.zip"
with zipfile.ZipFile(bad, "w") as z:
for name, body in (entries | change).items():
z.writestr(name, body)
with pytest.raises(bundle.PackageError):
bundle.verify_bundle(bad)
(work / "escape.py").symlink_to(source / "app.py")
with pytest.raises(bundle.PackageError, match="symlink"):
bundle.create_bundle(work, revision, tmp_path / "links.zip")
def test_rollback_refuses_added_file_symlink_without_touching_referent(repos, tmp_path):
source, work, revision, artifact = repos
target = tmp_path / "staged"
bundle.stage_bundle(artifact, source, target)
outside = tmp_path / "outside"
outside.write_text("owner data")
(target / "new/tool.py").unlink()
(target / "new/tool.py").symlink_to(outside)
with pytest.raises(bundle.PackageError, match="symlink"):
bundle.rollback(target)
assert outside.read_text() == "owner data"
assert (target / "app.py").read_text() == "print('after')\n"
def test_manifest_semantic_tampering_is_rejected(repos, tmp_path):
source, work, revision, artifact = repos
with zipfile.ZipFile(artifact) as z:
entries = {name: z.read(name) for name in z.namelist()}
document = json.loads(entries["manifest.json"])
document["changes"][0]["before_sha256"] = "0" * 64
entries["manifest.json"] = json.dumps(document).encode()
bad = tmp_path / "bad-manifest.zip"
with zipfile.ZipFile(bad, "w") as z:
for name, body in entries.items():
z.writestr(name, body)
with pytest.raises(bundle.PackageError, match="before|base"):
bundle.verify_bundle(bad)
def test_rollback_preserves_user_staged_changes(repos, tmp_path):
source, work, revision, artifact = repos
target = tmp_path / "staged"
bundle.stage_bundle(artifact, source, target)
git(target, "add", "app.py")
with pytest.raises(bundle.PackageError, match="index changed"):
bundle.rollback(target)
assert (target / "app.py").read_text() == "print('after')\n"
assert git(target, "diff", "--cached", "--name-only") == "app.py"