feat: publish CodexPi contest snapshot
Some checks are pending
Qualité / tests (push) Waiting to run
Some checks are pending
Qualité / tests (push) Waiting to run
This commit is contained in:
114
tests/test_master_agent.py
Normal file
114
tests/test_master_agent.py
Normal file
@@ -0,0 +1,114 @@
|
||||
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
|
||||
Reference in New Issue
Block a user