Files
codexpi/tests/test_master_agent.py
CodexPi 995b17a6e2
Some checks are pending
Qualité / tests (push) Waiting to run
feat: publish CodexPi contest snapshot
2026-11-14 20:23:59 +01:00

115 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
import json
import os
import socket
import threading
import uuid
from pathlib import Path
from codexpi.database import utcnow
def fake_host_channel(path: Path, token: str):
ready = threading.Event()
def serve():
if path.exists():
path.unlink()
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])
assert request["token"] == token
connection.sendall(json.dumps({"ok": True, "exit_code": 0, "answer": "Action hôte de test terminée."}).encode() + b"\n")
thread = threading.Thread(target=serve, daemon=True)
thread.start()
ready.wait(2)
return thread
def test_master_agent_channel_is_separate_confirmed_and_audited(logged_in):
client, app, _, _, headers = logged_in
base = app.state.settings.data_dir / "host-agent"
base.mkdir()
token_file, socket_path = base / "agent.token", base / "agent.sock"
token = "fixture-token-which-is-longer-than-thirty-two-characters"
token_file.write_text(token, encoding="utf-8")
os.chmod(token_file, 0o600)
thread = fake_host_channel(socket_path, token)
created = client.post(
"/api/master-agent/sessions", headers=headers,
json={"title": "Maintenance de test", "privileged": True},
)
assert created.status_code == 201
session_id = created.json()["id"]
refused = client.post(
f"/api/master-agent/sessions/{session_id}/messages", headers=headers,
json={"prompt": "Effectuer une action réversible de test.", "acknowledge_privileged": False},
)
assert refused.status_code == 409
accepted = client.post(
f"/api/master-agent/sessions/{session_id}/messages", headers=headers,
json={"prompt": "Effectuer une action réversible de test.", "acknowledge_privileged": True},
)
assert accepted.status_code == 200, accepted.text
assert accepted.json()["answer"] == "Action hôte de test terminée."
thread.join(2)
history = client.get("/api/master-agent").json()["sessions"]
assert [message["role"] for message in history[0]["messages"]] == ["user", "assistant"]
def test_project_user_cannot_access_master_agent(logged_in):
client, app, _, _, headers = logged_in
app.state.auth.create_user("member", "a-different-long-test-password", is_admin=False)
client.post("/api/auth/logout", headers=headers)
login = client.post("/api/auth/login", json={"username": "member", "password": "a-different-long-test-password"}).json()
member_headers = {"X-CSRF-Token": login["csrf_token"]}
assert client.get("/api/master-agent").status_code == 403
assert client.post("/api/master-agent/sessions", headers=member_headers, json={"title": "No", "privileged": False}).status_code == 403
def test_additional_admin_is_not_the_host_owner(logged_in):
client, app, _, _, headers = logged_in
app.state.auth.create_user("other-admin", "another-long-administrator-password", is_admin=True)
client.post("/api/auth/logout", headers=headers)
login = client.post("/api/auth/login", json={"username": "other-admin", "password": "another-long-administrator-password"}).json()
admin_headers = {"X-CSRF-Token": login["csrf_token"]}
assert client.get("/api/master-agent").status_code == 403
assert client.post("/api/master-agent/sessions", headers=admin_headers, json={"title": "No", "privileged": False}).status_code == 403
def test_additional_admin_cannot_queue_the_privileged_pi_doctor(logged_in):
client, app, projects, _, headers = logged_in
owner_id = str(app.state.db.one("SELECT id FROM users LIMIT 1")["id"])
project_id = str(uuid.uuid4())
conversation_id = str(uuid.uuid4())
root = projects / "pi-doctor"
root.mkdir()
stamp = utcnow()
app.state.db.execute(
"INSERT INTO projects(id,name,path,owner_id,full_access,execution_group,created_at) VALUES(?,?,?,?,?,?,?)",
(project_id, "Pi Doctor", str(root), owner_id, 1, "__global_agent__", stamp),
)
app.state.db.execute(
"INSERT INTO conversations(id,project_id,user_id,title,status,created_at,updated_at) VALUES(?,?,?,?,?,?,?)",
(conversation_id, project_id, owner_id, "Host", "idle", stamp, stamp),
)
app.state.auth.create_user("other-host-admin", "another-long-administrator-password", is_admin=True)
client.post("/api/auth/logout", headers=headers)
login = client.post(
"/api/auth/login",
json={"username": "other-host-admin", "password": "another-long-administrator-password"},
).json()
response = client.post(
f"/api/conversations/{conversation_id}/queue",
headers={"X-CSRF-Token": login["csrf_token"]},
json={"content": "Inspecter l’hôte.", "attachment_ids": []},
)
assert response.status_code == 403