138 lines
4.1 KiB
Python
138 lines
4.1 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",
|
|
] |