Files
agentic-mobile-control/apps/device-host-agent/tests/test_history.py
T

135 lines
4.0 KiB
Python

from __future__ import annotations
import json
from datetime import UTC, datetime
from host_agent.history import ConsoleHistoryStore
def test_history_store_records_assignment_and_heartbeat_newest_first(tmp_path) -> None:
store = ConsoleHistoryStore(tmp_path / "history.sqlite3")
store.record_assignment(
task_id="task-a",
attempt=1,
status="done",
failure_reason=None,
device_id="device-a",
)
store.record_heartbeat(device_count=2)
entries = store.list_recent()
assert len(entries) == 2
assert entries[0] == {
"kind": "heartbeat",
"occurred_at": entries[0]["occurred_at"],
"summary": "heartbeat: 2 devices",
"detail": {"device_count": 2},
}
assert entries[1] == {
"kind": "assignment",
"occurred_at": entries[1]["occurred_at"],
"summary": "task-a attempt 1 on device-a: done",
"detail": {
"task_id": "task-a",
"attempt": 1,
"status": "done",
"failure_reason": None,
"device_id": "device-a",
},
}
def test_history_store_prunes_oldest_entries_beyond_limit(tmp_path) -> None:
store = ConsoleHistoryStore(tmp_path / "history.sqlite3", limit=3)
for index in range(5):
store.record_heartbeat(device_count=index)
entries = store.list_recent()
assert len(entries) == 3
assert [entry["detail"]["device_count"] for entry in entries] == [4, 3, 2]
def test_history_store_returns_empty_list_when_no_entries(tmp_path) -> None:
store = ConsoleHistoryStore(tmp_path / "history.sqlite3")
assert store.list_recent() == []
def test_history_store_uses_injected_now_for_occurred_at(tmp_path) -> None:
fixed_now = datetime(2026, 1, 1, 12, 0, 0, tzinfo=UTC)
store = ConsoleHistoryStore(tmp_path / "history.sqlite3", now=lambda: fixed_now)
store.record_heartbeat(device_count=1)
entries = store.list_recent()
assert entries[0]["occurred_at"] == fixed_now.isoformat()
def test_history_store_records_task_submission_with_device(tmp_path) -> None:
store = ConsoleHistoryStore(tmp_path / "history.sqlite3")
store.record_task_submission(task_id="task-cloud-1", device_id="device-cloud-a")
entries = store.list_recent()
assert len(entries) == 1
assert entries[0]["kind"] == "task_submission"
assert entries[0]["summary"] == "task submitted: task-cloud-1 on device-cloud-a"
assert entries[0]["detail"] == {
"task_id": "task-cloud-1",
"device_id": "device-cloud-a",
}
def test_history_store_records_task_submission_without_device(tmp_path) -> None:
store = ConsoleHistoryStore(tmp_path / "history.sqlite3")
store.record_task_submission(task_id="task-cloud-2", device_id=None)
entries = store.list_recent()
assert entries[0]["summary"] == ("task submitted: task-cloud-2 (automatic device)")
assert entries[0]["detail"] == {"task_id": "task-cloud-2"}
def test_history_store_task_submission_redacts_goal_and_secrets(tmp_path) -> None:
store = ConsoleHistoryStore(tmp_path / "history.sqlite3")
secret_goal = (
"rotate secret-XYZ-abcdef-very-secret cookie=session-abc; lease=lease-stale"
)
store.record_task_submission(task_id="task-cloud-3", device_id="device-cloud-a")
store.record_heartbeat(device_count=1)
entries = store.list_recent()
rendered = "\n".join(
repr(entry["summary"]) + " " + json.dumps(entry["detail"]) for entry in entries
)
assert secret_goal not in rendered
assert "session-abc" not in rendered
assert "lease-stale" not in rendered
def test_history_store_task_submissions_prune_beyond_limit(tmp_path) -> None:
store = ConsoleHistoryStore(tmp_path / "history.sqlite3", limit=3)
for index in range(5):
store.record_task_submission(
task_id=f"task-{index}", device_id=f"device-{index}"
)
entries = store.list_recent()
assert len(entries) == 3
assert [entry["detail"]["task_id"] for entry in entries] == [
"task-4",
"task-3",
"task-2",
]