88 lines
3.8 KiB
Python
88 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import socket
|
|
import sys
|
|
import threading
|
|
import uuid
|
|
from pathlib import Path
|
|
|
|
from codexpi.database import Database, utcnow
|
|
from codexpi.host_session_worker import main as host_worker_main
|
|
|
|
|
|
def fake_host_channel(path: Path, token: str, captured: dict):
|
|
ready = threading.Event()
|
|
|
|
def serve():
|
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as server:
|
|
server.bind(str(path))
|
|
server.listen(1)
|
|
ready.set()
|
|
connection, _ = server.accept()
|
|
with connection:
|
|
request = json.loads(connection.recv(100_000).split(b"\n", 1)[0])
|
|
captured.update(request)
|
|
assert request["token"] == token
|
|
response = {"ok": True, "exit_code": 0, "answer": "Mesures hôte vérifiées et rapport enregistré."}
|
|
connection.sendall(json.dumps(response).encode() + b"\n")
|
|
|
|
thread = threading.Thread(target=serve, daemon=True)
|
|
thread.start()
|
|
ready.wait(2)
|
|
return thread
|
|
|
|
|
|
def test_host_worker_keeps_the_normal_conversation_and_queue(tmp_path: Path, monkeypatch):
|
|
database = Database(tmp_path / "codexpi.db")
|
|
database.initialize()
|
|
project_root = tmp_path / "pi-doctor"
|
|
project_root.mkdir()
|
|
stamp = utcnow()
|
|
database.execute(
|
|
"INSERT INTO projects(id,name,path,owner_id,full_access,execution_group,created_at) VALUES(?,?,?,?,?,?,?)",
|
|
("doctor", "Pi Doctor", str(project_root), "owner", 1, "__global_agent__", stamp),
|
|
)
|
|
database.execute(
|
|
"INSERT INTO conversations(id,project_id,user_id,title,status,created_at,updated_at) VALUES(?,?,?,?,?,?,?)",
|
|
("conversation", "doctor", "owner", "Audit hôte", "running", stamp, stamp),
|
|
)
|
|
queue_id = str(uuid.uuid4())
|
|
message_id = str(uuid.uuid4())
|
|
database.execute(
|
|
"INSERT INTO messages(id,conversation_id,role,content,metadata,created_at) VALUES(?,?,?,?,?,?)",
|
|
(message_id, "conversation", "user", "Mesurer le CPU réel du Pi en lecture seule.", json.dumps({"queue_id": queue_id}), stamp),
|
|
)
|
|
database.execute(
|
|
"INSERT INTO prompt_queue(id,conversation_id,requested_by,message_id,content,status,created_at,started_at) VALUES(?,?,?,?,?,'running',?,?)",
|
|
(queue_id, "conversation", "owner", message_id, "Mesurer le CPU réel du Pi en lecture seule.", stamp, stamp),
|
|
)
|
|
token = "fixture-token-which-is-longer-than-thirty-two-characters"
|
|
token_file = tmp_path / "agent.token"
|
|
token_file.write_text(token, encoding="utf-8")
|
|
os.chmod(token_file, 0o600)
|
|
socket_path = tmp_path / "agent.sock"
|
|
captured: dict = {}
|
|
thread = fake_host_channel(socket_path, token, captured)
|
|
events = tmp_path / "events.jsonl"
|
|
monkeypatch.setattr(sys, "argv", [
|
|
"host-worker", "--database", str(database.path), "--events", str(events),
|
|
"--conversation", "conversation", "--cwd", str(project_root),
|
|
"--socket", str(socket_path), "--token-file", str(token_file),
|
|
"--queue", queue_id, "--privileged",
|
|
])
|
|
|
|
assert host_worker_main() == 0
|
|
thread.join(2)
|
|
assert captured["privileged"] is True
|
|
assert "véritable hôte Raspberry Pi" in captured["prompt"]
|
|
assert str(project_root) in captured["prompt"]
|
|
assert database.one("SELECT status FROM prompt_queue WHERE id=?", (queue_id,))["status"] == "done"
|
|
conversation = database.one("SELECT status,worker_pid FROM conversations WHERE id='conversation'")
|
|
assert conversation == {"status": "completed", "worker_pid": None}
|
|
answer = database.one("SELECT content,metadata FROM messages WHERE conversation_id='conversation' AND role='assistant'")
|
|
assert answer["content"] == "Mesures hôte vérifiées et rapport enregistré."
|
|
assert json.loads(answer["metadata"])["host_agent"] is True
|
|
|