File size: 4,352 Bytes
dfb775d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | """Coach governance endpoints: boardroom convene + dojo settle."""
from __future__ import annotations
from fastapi.testclient import TestClient
from mindxtrain.governance import panel as P
from mindxtrain.operator.app import app
client = TestClient(app)
def test_boardroom_presets():
r = client.get("/coach/api/boardroom/presets")
assert r.status_code == 200
presets = r.json()
assert "classic_triad" in presets
assert presets["classic_triad"] == ["advocate", "critic", "analyst"]
def test_convene_with_explicit_votes():
r = client.post("/coach/api/boardroom/convene", json={
"motion": "promote actor x",
"members": [{"id": "a", "role": "advocate"}, {"id": "b", "role": "critic"},
{"id": "c", "role": "analyst"}],
"votes": {"a": "approve", "b": "approve", "c": "reject"},
})
assert r.status_code == 200, r.text
d = r.json()["decision"]
assert d["outcome"] == "approved"
assert d["approvals"] == 2 and d["rejections"] == 1
def test_convene_tie_is_disputed():
r = client.post("/coach/api/boardroom/convene", json={
"motion": "x",
"members": [{"id": "a"}, {"id": "b"}],
"votes": {"a": "approve", "b": "reject"},
})
assert r.status_code == 200
assert r.json()["decision"]["disputed"] is True
def test_convene_with_models(monkeypatch):
def _chat(model, messages, **k):
return "VERDICT: APPROVE" if "advocate" in messages[0]["content"].lower() else "VERDICT: REJECT"
monkeypatch.setattr(P, "chat_once", _chat)
r = client.post("/coach/api/boardroom/convene", json={
"motion": "promote actor x",
"members": [{"id": "a", "role": "advocate", "model": "m"},
{"id": "b", "role": "critic", "model": "m"}],
"use_models": True,
})
assert r.status_code == 200, r.text
body = r.json()
assert len(body["deliberations"]) == 2
assert body["decision"]["approvals"] == 1 and body["decision"]["rejections"] == 1
def test_convene_requires_votes_or_models():
r = client.post("/coach/api/boardroom/convene", json={
"motion": "x", "members": [{"id": "a"}],
})
assert r.status_code == 422
def test_convene_rejects_bad_role():
r = client.post("/coach/api/boardroom/convene", json={
"motion": "x", "members": [{"id": "a", "role": "wizard"}],
"votes": {"a": "approve"},
})
assert r.status_code == 422
def test_dojo_settle_with_votes():
r = client.post("/coach/api/dojo/settle", json={
"motion": "promote actor x", "size": 3,
"votes": {"judge-0": "approve", "judge-1": "approve", "judge-2": "reject"},
})
assert r.status_code == 200, r.text
v = r.json()
assert v["settled"] is True
assert v["winner"] == "approve"
def test_dojo_settle_rounds_size_to_prime():
# size 4 → 5 judges; need all five votes.
r = client.post("/coach/api/dojo/settle", json={
"motion": "x", "size": 4,
"votes": {f"judge-{i}": ("approve" if i < 3 else "reject") for i in range(5)},
})
assert r.status_code == 200, r.text
v = r.json()
assert len(v["judges"]) == 5
assert v["winner"] == "approve"
def test_dojo_settle_missing_vote_422():
r = client.post("/coach/api/dojo/settle", json={
"motion": "x", "size": 3, "votes": {"judge-0": "approve"},
})
assert r.status_code == 422
def test_models_endpoint_shape():
# Returns 200 with the backend base_url + a (possibly empty) model list,
# whether or not a chat backend is reachable.
r = client.get("/coach/api/models")
assert r.status_code == 200
body = r.json()
assert "base_url" in body
assert isinstance(body["models"], list)
def test_coach_index_has_boardroom_card():
r = client.get("/coach/")
assert r.status_code == 200
assert 'id="step-boardroom"' in r.text
assert 'id="br-convene"' in r.text
assert 'id="br-settle"' in r.text
def test_coach_js_wires_boardroom():
r = client.get("/coach/static/coach.js")
assert r.status_code == 200
assert "wireBoardroom" in r.text
assert "/coach/api/boardroom/convene" in r.text
assert "/coach/api/dojo/settle" in r.text
# Model dropdown is populated from the live backend.
assert "loadBoardroomModels" in r.text
assert "/coach/api/models" in r.text
|