Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02b6f-7db1-7222-918b-e813a6bda38d
178 lines
5.7 KiB
Python
178 lines
5.7 KiB
Python
"""Tests for activity-core ops_run client (REIN-A-0002-T01)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
from rein_aharness.ops_run_client import (
|
|
ActivityCoreOpsClient,
|
|
OpsRun,
|
|
OpsRunConfig,
|
|
OpsRunError,
|
|
ops_run_to_taskspec,
|
|
)
|
|
from rein_aharness.taskspec import TaskSpecError
|
|
|
|
|
|
def test_ops_run_from_api() -> None:
|
|
row = OpsRun.from_api(
|
|
{
|
|
"id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
|
|
"activity_definition_id": "11111111-2222-3333-4444-555555555555",
|
|
"idempotency_key": "k",
|
|
"target_repo": "freedom-intelligence",
|
|
"title": "FI daily",
|
|
"description": "d",
|
|
"labels": ["automated", "research-brief"],
|
|
"state": "open",
|
|
"attempt": 0,
|
|
"harness_profile_ref": "harness.agent-dev-local@1.0.0",
|
|
"execution_refs": {
|
|
"correlation_id": "corr-1",
|
|
"goal_refs": ["goal:42@1"],
|
|
},
|
|
}
|
|
)
|
|
assert row.target_repo == "freedom-intelligence"
|
|
assert "research-brief" in row.labels
|
|
assert row.harness_profile_ref == "harness.agent-dev-local@1.0.0"
|
|
assert row.execution_refs == {
|
|
"correlation_id": "corr-1",
|
|
"goal_refs": ["goal:42@1"],
|
|
}
|
|
|
|
|
|
def test_config_from_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("ACTIVITY_CORE_URL", "http://actcore:8010/")
|
|
monkeypatch.setenv("ACTIVITY_CORE_WORKER_TOKEN", "tok")
|
|
monkeypatch.setenv("AGENT_HARNESS_WORKER_ID", "w@host")
|
|
monkeypatch.setenv("AGENT_HARNESS_OPS_LABELS", "automated,research-brief")
|
|
monkeypatch.setenv(
|
|
"AGENT_HARNESS_REPO_MAP",
|
|
json.dumps({"freedom-intelligence": "/tmp/fi"}),
|
|
)
|
|
cfg = OpsRunConfig.from_env()
|
|
assert cfg.base_url == "http://actcore:8010"
|
|
assert cfg.worker_token == "tok"
|
|
assert cfg.worker_id == "w@host"
|
|
assert "research-brief" in cfg.claim_labels
|
|
assert cfg.repo_map["freedom-intelligence"] == "/tmp/fi"
|
|
|
|
|
|
def test_claim_posts_body() -> None:
|
|
cfg = OpsRunConfig(
|
|
base_url="http://example.test",
|
|
worker_token="secret",
|
|
worker_id="worker-1",
|
|
claim_labels=("automated",),
|
|
lease_seconds=120,
|
|
)
|
|
client = ActivityCoreOpsClient(cfg)
|
|
mock_resp = MagicMock()
|
|
mock_resp.raise_for_status = MagicMock()
|
|
mock_resp.json.return_value = {
|
|
"items": [
|
|
{
|
|
"id": "r1",
|
|
"activity_definition_id": "d1",
|
|
"idempotency_key": "k",
|
|
"target_repo": "freedom-intelligence",
|
|
"title": "t",
|
|
"description": "",
|
|
"labels": ["automated", "research-brief"],
|
|
"state": "claimed",
|
|
"claim_owner": "worker-1",
|
|
"attempt": 1,
|
|
}
|
|
]
|
|
}
|
|
with patch("rein_aharness.ops_run_client.httpx.post", return_value=mock_resp) as post:
|
|
claimed = client.claim(limit=1)
|
|
assert len(claimed) == 1
|
|
assert claimed[0].id == "r1"
|
|
assert claimed[0].state == "claimed"
|
|
kwargs = post.call_args.kwargs
|
|
assert kwargs["json"]["worker_id"] == "worker-1"
|
|
assert kwargs["json"]["labels"] == ["automated"]
|
|
assert kwargs["headers"]["X-Worker-Token"] == "secret"
|
|
|
|
|
|
def test_complete_and_fail() -> None:
|
|
cfg = OpsRunConfig(base_url="http://example.test", worker_id="w")
|
|
client = ActivityCoreOpsClient(cfg)
|
|
mock_resp = MagicMock()
|
|
mock_resp.raise_for_status = MagicMock()
|
|
mock_resp.json.return_value = {
|
|
"id": "r1",
|
|
"activity_definition_id": "d",
|
|
"idempotency_key": "k",
|
|
"title": "t",
|
|
"description": "",
|
|
"state": "succeeded",
|
|
"labels": [],
|
|
}
|
|
with patch("rein_aharness.ops_run_client.httpx.post", return_value=mock_resp):
|
|
out = client.complete("r1", result={"path": "x"})
|
|
assert out.state == "succeeded"
|
|
|
|
mock_resp.json.return_value = {**mock_resp.json.return_value, "state": "open"}
|
|
with patch("rein_aharness.ops_run_client.httpx.post", return_value=mock_resp) as post:
|
|
out = client.fail("r1", error="timeout", reopen=True)
|
|
assert post.call_args.kwargs["json"]["reopen"] is True
|
|
|
|
|
|
def test_claim_http_error() -> None:
|
|
cfg = OpsRunConfig(base_url="http://example.test", worker_id="w")
|
|
client = ActivityCoreOpsClient(cfg)
|
|
with patch(
|
|
"rein_aharness.ops_run_client.httpx.post",
|
|
side_effect=httpx.ConnectError("down"),
|
|
):
|
|
with pytest.raises(OpsRunError, match="claim failed"):
|
|
client.claim()
|
|
|
|
|
|
def test_ops_run_to_taskspec(tmp_path: Path) -> None:
|
|
import subprocess
|
|
|
|
repo = tmp_path / "freedom-intelligence"
|
|
repo.mkdir()
|
|
subprocess.run(["git", "init", "-q"], cwd=repo, check=True)
|
|
(repo / "README.md").write_text("x\n")
|
|
subprocess.run(["git", "add", "."], cwd=repo, check=True)
|
|
subprocess.run(
|
|
["git", "-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "i"],
|
|
cwd=repo,
|
|
check=True,
|
|
)
|
|
run = OpsRun(
|
|
id="r1",
|
|
activity_definition_id="d",
|
|
idempotency_key="k",
|
|
target_repo="freedom-intelligence",
|
|
title="FI",
|
|
description="do",
|
|
labels=["automated"],
|
|
)
|
|
cfg = OpsRunConfig(repo_roots=(str(tmp_path),))
|
|
spec = ops_run_to_taskspec(run, cfg)
|
|
assert spec.target_repo == repo.resolve()
|
|
assert spec.hub_task_id == "r1"
|
|
|
|
|
|
def test_ops_run_to_taskspec_missing_repo() -> None:
|
|
run = OpsRun(
|
|
id="r1",
|
|
activity_definition_id="d",
|
|
idempotency_key="k",
|
|
target_repo=None,
|
|
title="t",
|
|
description="",
|
|
)
|
|
with pytest.raises(TaskSpecError):
|
|
ops_run_to_taskspec(run, OpsRunConfig())
|