CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m22s
Development Plan M0.1. Every section reference in code, tests, specs/, the example config, the tuning guide, and the CLAUDE.md and README files now cites docs/Architecture.md (P, L, D, O, X, Q) instead of the old spec's A-H, and docs/Architecture-Build-Spec.md is deleted (git keeps it). The plan's old-to-new table goes with it. Mostly mechanical, with a per-site choice where one old section split: A4 became L2 where it meant stop/abandon/the loop and L3 where it meant approvals; A10 became L6 in the context-budget code and L7 elsewhere; B2 became D4 for outbound Gitea and publishing and D5 for review intake; 'A3 step N' became the matching step of L8's boot sequence. Comments describing behaviour the architecture drops (planning always escalating, thresholds tuned at an old build step) now describe the current code and point at the Development Plan. docs/Architecture.md gains one line the rewrite missed: intake outages delay but never fail work (old B2), now in L5, where the poller and Matrix code cite it. Comments and docs only; no behaviour change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
94 lines
3.1 KiB
Python
94 lines
3.1 KiB
Python
"""Live Gitea round trip against the sandbox repo: push a branch, open a PR,
|
|
comment, and prove the L8 reconciler reads external truth correctly.
|
|
|
|
Cleans up after itself (closes the PR, deletes the branch) so repeated runs
|
|
do not silt up the sandbox.
|
|
"""
|
|
|
|
import uuid
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from nisse_dev.config import AgentConfig
|
|
from nisse_dev.git import GitError, GitRepo, authenticated_url
|
|
from nisse_dev.gitea import GiteaClient, pull_request_reconciler
|
|
from nisse_dev.state import Intent
|
|
|
|
pytestmark = pytest.mark.integration
|
|
|
|
|
|
def intent_for(branch: str) -> Intent:
|
|
return Intent(
|
|
id=1,
|
|
kind="open_pr",
|
|
payload={"branch": branch},
|
|
status="pending",
|
|
created_at="t",
|
|
resolved_at=None,
|
|
)
|
|
|
|
|
|
def test_push_open_pr_and_reconcile(
|
|
tmp_path: Path, live_config: AgentConfig, gitea: GiteaClient
|
|
) -> None:
|
|
base_url = live_config.gitea_url
|
|
token = live_config.gitea_token
|
|
repo_path = live_config.gitea_repo # the sandbox
|
|
client = gitea
|
|
|
|
account = client.whoami()
|
|
assert "testing" in account, (
|
|
f"live tests must run as a testing account, not {account}"
|
|
)
|
|
|
|
branch = f"live-test-{uuid.uuid4().hex[:8]}"
|
|
reconcile = pull_request_reconciler(client, repo_path)
|
|
|
|
# Before the PR exists, external truth says the effect has not happened.
|
|
assert reconcile(intent_for(branch)) is False
|
|
|
|
repo = GitRepo(tmp_path / "work")
|
|
repo.init()
|
|
repo.configure_identity(account, f"{account}@example.test")
|
|
(repo.root / f"{branch}.txt").write_text("live test artifact\n")
|
|
assert repo.commit_all("Live test commit") is not None
|
|
|
|
url = authenticated_url(base_url, repo_path, account, token)
|
|
pr = None
|
|
try:
|
|
repo.push(url, branch, token=token)
|
|
pr = client.open_pull_request(
|
|
repo_path,
|
|
head=branch,
|
|
base="main",
|
|
title=f"live test {branch}",
|
|
body="Opened by the nisse-dev live test suite.",
|
|
)
|
|
assert pr.head == branch
|
|
|
|
client.post_comment(repo_path, pr.number, "live test comment")
|
|
bodies = [c.body for c in client.list_comments(repo_path, pr.number)]
|
|
assert "live test comment" in bodies
|
|
|
|
# Now the same reconciler must see the effect and refuse to repeat it.
|
|
assert reconcile(intent_for(branch)) is True
|
|
|
|
# D4 is only real if the server enforces it. The agent's own token
|
|
# must not be able to merge the PR it just opened, and must not be
|
|
# able to push to the protected default branch at all.
|
|
merged = client._http.post(
|
|
f"/repos/{repo_path}/pulls/{pr.number}/merge", json={"Do": "merge"}
|
|
)
|
|
assert merged.status_code >= 400, (
|
|
"the agent merged its own PR: branch protection is not enforcing "
|
|
"the human gate"
|
|
)
|
|
with pytest.raises(GitError):
|
|
repo.push(url, "main", token=token)
|
|
finally:
|
|
if pr is not None:
|
|
client.close_pull_request(repo_path, pr.number)
|
|
repo.delete_remote_branch(url, branch, token=token)
|
|
client.close()
|