rein-openweights/tests/test_runner.py
tegwick 4ee612140b Bootstrap rein-openweights: minimal agentic loop over OpenRouter (REIN-OW-WP-0001)
Implements T01-T04:
- tools.py: green-commit-only-equivalent tool surface (read/write/edit/
  glob/grep + git status/diff/log/add+commit), path-traversal guarded.
- openrouter_client.py: own minimal chat-completions client — llm-connect's
  OpenRouterAdapter takes a single prompt string and never surfaces
  tool_calls, so it can't drive a multi-turn tool-calling loop without a
  breaking change to its frozen Core ABC. llm-connect stays an optional
  dependency (pyproject.toml), not load-bearing.
- loop.py: plan -> tool call -> observe -> repeat, budget- and
  turn-bounded, tool errors reported back to the model instead of
  crashing the loop.
- credentials.py: own OpenBao AppRole/ambient-token acquisition, per
  glas-harness ADR-002 (Option B) — glas-harness does not broker this.
- runner.py/hub.py: commit-verified success criterion + State Hub
  progress/token reporting, mirroring rein-aharness's model.

26 tests, all mocked at the httpx/subprocess boundary — no real
OpenRouter or OpenBao calls made. T05 (Forgejo repo creation) stays
open, deferred to the operator.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 13:37:09 +02:00

75 lines
2.6 KiB
Python

import json
import subprocess
from unittest.mock import patch
from rein_openweights.loop import LoopResult
from rein_openweights.runner import run_task
def _init_repo(tmp_path):
subprocess.run(["git", "init", "-q"], cwd=tmp_path, check=True)
subprocess.run(["git", "config", "user.email", "t@example.com"], cwd=tmp_path, check=True)
subprocess.run(["git", "config", "user.name", "t"], cwd=tmp_path, check=True)
subprocess.run(["git", "commit", "-q", "--allow-empty", "-m", "init"], cwd=tmp_path, check=True)
return tmp_path
def _task_file(tmp_path, repo):
task = tmp_path / "task.json"
task.write_text(
json.dumps({"title": "t", "description": "d", "target_repo": str(repo)})
)
return str(task)
def test_run_task_ok_when_loop_produces_a_commit(tmp_path):
repo = _init_repo(tmp_path)
task_file = _task_file(tmp_path, repo)
def fake_run_loop(client, repo_root, title, description, *, max_turns, budget):
subprocess.run(["git", "-C", str(repo_root), "commit", "-q", "--allow-empty", "-m", "task"])
budget.consume(500)
return LoopResult(turns=2, finished=True)
with (
patch("rein_openweights.runner.resolve_openrouter_api_key", return_value="sk-test"),
patch("rein_openweights.runner.run_loop", side_effect=fake_run_loop),
patch("rein_openweights.runner.hub.post_progress_event", return_value=True),
patch("rein_openweights.runner.hub.post_token_event", return_value=True),
):
result = run_task(task_file)
assert result.ok is True
assert result.committed is True
assert result.tokens_spent == 500
def test_run_task_fails_when_no_commit_produced(tmp_path):
repo = _init_repo(tmp_path)
task_file = _task_file(tmp_path, repo)
with (
patch("rein_openweights.runner.resolve_openrouter_api_key", return_value="sk-test"),
patch(
"rein_openweights.runner.run_loop",
return_value=LoopResult(turns=1, finished=True),
),
patch("rein_openweights.runner.hub.post_progress_event", return_value=True),
patch("rein_openweights.runner.hub.post_token_event", return_value=True),
):
result = run_task(task_file)
assert result.ok is False
assert result.committed is False
assert result.reason == "no commit produced"
def test_run_task_fails_fast_without_credential(tmp_path):
repo = _init_repo(tmp_path)
task_file = _task_file(tmp_path, repo)
with patch("rein_openweights.runner.resolve_openrouter_api_key", return_value=None):
result = run_task(task_file, report_to_hub=False)
assert result.ok is False
assert "credential" in result.reason