Kairo-Jinkendo/backend/tests/test_ap17_operational_api.py
Lars 5dc372c043
Some checks failed
Deploy Development / deploy (push) Failing after 42s
Test Suite / pytest-backend (push) Failing after 1s
Test Suite / k6 /api/health Baseline (push) Has been skipped
Test Suite / playwright-smoke (push) Has been skipped
Test Suite / lint-backend (push) Successful in 3s
Test Suite / compose-smoke (push) Has been skipped
AP1.7: Operational API und Actor Service Tokens fuer Vibe-Coder.
Agenten authentifizieren per X-Actor-Token; /api/operational/ Fassade mit context, next-action, status, evidence; Token-Verwaltung unter /api/actors/{id}/service-tokens.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 12:21:16 +02:00

125 lines
3.6 KiB
Python

"""AP1.7 — Operational API and Actor Service Tokens."""
from __future__ import annotations
import hashlib
from auth import AUTH_HEADER
from services import actors as actor_service
from services.actor_service_tokens import (
TOKEN_PREFIX,
_hash_token,
create_service_token,
validate_service_token,
)
from tests.factories import provision_user_in_tenant
from tests.test_initiatives_actions import _auth, _create_initiative, _login
def test_token_hash_roundtrip():
raw = f"{TOKEN_PREFIX}abc123_secretpart"
assert _hash_token(raw) == hashlib.sha256(raw.encode()).hexdigest()
def test_validate_service_token_rejects_garbage():
assert validate_service_token("not-a-token") is None
assert validate_service_token("") is None
def test_operational_api_with_service_token(client):
admin = provision_user_in_tenant(tenant_role="admin")
token = _login(client, admin)
agent = actor_service.create_actor(
tenant_id=admin["tenant_id"],
actor_type="agent",
name="Cursor Agent",
)
created = create_service_token(
tenant_id=admin["tenant_id"],
actor_id=agent["id"],
label="Test Agent",
created_by_user_id=admin["id"],
)
actor_token = created["token"]
me = client.get(
"/api/operational/me",
headers={"X-Actor-Token": actor_token},
)
assert me.status_code == 200
body = me.json()
assert body["ok"] is True
assert body["actor_id"] == agent["id"]
assert body["data"]["auth_source"] == "service_token"
initiative = _create_initiative(client, token, title="Agent Test Initiative")
initiative_id = initiative.json()["id"]
ctx = client.get(
f"/api/operational/initiatives/{initiative_id}/context",
headers={"X-Actor-Token": actor_token},
)
assert ctx.status_code == 200
assert ctx.json()["data"]["title"] == "Agent Test Initiative"
next_action = client.get(
f"/api/operational/next-action?initiative_id={initiative_id}&limit=3",
headers={"X-Actor-Token": actor_token},
)
assert next_action.status_code == 200
assert isinstance(next_action.json()["data"], list)
def test_operational_token_missing_capability(client):
admin = provision_user_in_tenant(tenant_role="admin")
agent = actor_service.create_actor(
tenant_id=admin["tenant_id"],
actor_type="agent",
name="Limited Agent",
)
created = create_service_token(
tenant_id=admin["tenant_id"],
actor_id=agent["id"],
label="Read-only",
created_by_user_id=admin["id"],
capabilities=["kairo.action.read"],
)
res = client.post(
"/api/operational/evidence",
json={
"initiative_id": "00000000-0000-0000-0000-000000000001",
"title": "Should fail",
},
headers={"X-Actor-Token": created["token"]},
)
assert res.status_code == 403
def test_create_service_token_via_api(client):
admin = provision_user_in_tenant(tenant_role="admin")
token = _login(client, admin)
agent = actor_service.create_actor(
tenant_id=admin["tenant_id"],
actor_type="agent",
name="API Token Agent",
)
res = client.post(
f"/api/actors/{agent['id']}/service-tokens",
json={"label": "Cursor"},
headers=_auth(token),
)
assert res.status_code == 201
body = res.json()
assert body["token"].startswith(TOKEN_PREFIX)
assert body["actor_id"] == agent["id"]
listed = client.get(
f"/api/actors/{agent['id']}/service-tokens",
headers=_auth(token),
)
assert listed.status_code == 200
assert len(listed.json()) >= 1