Files
devplacepy/tests/unit/test_devrant_token_auth.py
T
retoorandClaude Sonnet 5 57087536e5 Attribute Devii AI spend to its invoking action, fix quiz question-at-a-time review, DB API/isslop result routes, workspace docs, and drop redundant docstrings
- Route Devii-driven AI gateway cost to the action/tool that triggered
  it instead of a blanket "internal" bucket, so per-feature AI spend
  is attributable.
- Fix the quiz attempt review to show one previously-answered question
  at a time instead of all of them at once, and stop a quiz endpoint
  linked from the quiz flow from responding with raw JSON.
- Add DB API async query result route and AI Usage Analyzer annotated
  source/media routes, with traversal-safe uid/path handling and
  matching tests.
- Add Code Farm action audit logging (plant/harvest/buy-plot/upgrade/
  fertilize) and related admin workspace/services/trash/gateway route
  and doc touch-ups.
- Drop redundant docstrings from access_tokens.py per the no-comments
  convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VL9Xn57W5UR3HZbbuuzxdK
2026-09-03 08:47:57 +02:00

186 lines
6.8 KiB
Python

# retoor <retoor@molodetz.nl>
import secrets
from datetime import datetime, timezone
from devplacepy.database import get_table
from devplacepy.utils import generate_uid, _user_from_devrant_token, _resolve_user
from devplacepy.services.devrant.tokens import resolve_user_by_key
# ── mock helpers ─────────────────────────────────────────────────────────────
class _MockHeaders:
def __init__(self, data: dict):
self._data = data
def get(self, key: str, default=None):
return self._data.get(key, default)
class _MockCookies:
def __init__(self, data: dict):
self._data = data
def get(self, key: str):
return self._data.get(key)
class _MockRequest:
def __init__(self, headers=None, cookies=None):
self.headers = _MockHeaders(headers or {})
self.cookies = _MockCookies(cookies or {})
# ── seed helpers ─────────────────────────────────────────────────────────────
def _seed_user(username, email, is_active=True, **extra):
uid = generate_uid()
get_table("users").insert(
{
"uid": uid,
"username": username,
"terms_version": "1",
"email": email,
"is_active": is_active,
"created_at": datetime.now(timezone.utc).isoformat(),
**extra,
}
)
return uid
def _seed_token(user_uid, key, expire_time):
get_table("devrant_tokens").insert(
{
"uid": generate_uid(),
"key": key,
"user_uid": user_uid,
"user_id": 1,
"expire_time": expire_time,
"created_at": datetime.now(timezone.utc).isoformat(),
"deleted_at": None,
"deleted_by": None,
}
)
# ── resolve_user_by_key tests ────────────────────────────────────────────────
def test_resolve_user_by_key_valid(local_db):
"""Create a devrant token for a user, then resolve it by key - the right user is returned."""
uid = _seed_user("dr_valid", "drvalid@test.dev")
token_key = secrets.token_hex(20)
expire_time = int(datetime.now(timezone.utc).timestamp()) + 86400
_seed_token(uid, token_key, expire_time)
user = resolve_user_by_key(token_key)
assert user is not None
assert user["uid"] == uid
assert user["username"] == "dr_valid"
def test_resolve_user_by_key_not_found(local_db):
"""Look up a random key that has no token - verify None."""
random_key = secrets.token_hex(20)
assert resolve_user_by_key(random_key) is None
def test_resolve_user_by_key_expired(local_db):
"""Create an expired token - resolve returns None."""
uid = _seed_user("dr_expired", "drexpired@test.dev")
token_key = secrets.token_hex(20)
expire_time = int(datetime.now(timezone.utc).timestamp()) - 86400
_seed_token(uid, token_key, expire_time)
assert resolve_user_by_key(token_key) is None
def test_resolve_user_by_key_inactive_user(local_db):
"""Create a token for an inactive user - resolve returns None."""
uid = _seed_user("dr_inactive", "drinactive@test.dev", is_active=False)
token_key = secrets.token_hex(20)
expire_time = int(datetime.now(timezone.utc).timestamp()) + 86400
_seed_token(uid, token_key, expire_time)
assert resolve_user_by_key(token_key) is None
# ── _user_from_devrant_token hex-format guard tests ──────────────────────────
def test_user_from_devrant_token_hex_format(local_db):
"""_user_from_devrant_token only tries DB for 40-char hex strings;
non-hex or wrong-length keys return None without a DB hit."""
# empty / falsy
assert _user_from_devrant_token("") is None
assert _user_from_devrant_token(None) is None
# wrong length
assert _user_from_devrant_token("abc123") is None
assert _user_from_devrant_token("a" * 39) is None
assert _user_from_devrant_token("a" * 41) is None
# right length but non-hex
assert _user_from_devrant_token("z" * 40) is None
assert _user_from_devrant_token(("g" * 39) + "x") is None
# valid 40-char hex that simply has no matching token in the DB
assert _user_from_devrant_token("a" * 40) is None
# ── _resolve_user bridge tests ───────────────────────────────────────────────
def test_resolve_user_falls_back_to_devrant_token(local_db):
"""_resolve_user finds a user from X-API-KEY header containing a DevRant token key."""
uid = _seed_user("dr_fallback", "drfallback@test.dev")
token_key = secrets.token_hex(20)
expire_time = int(datetime.now(timezone.utc).timestamp()) + 86400
_seed_token(uid, token_key, expire_time)
request = _MockRequest(headers={"X-API-KEY": token_key})
user = _resolve_user(request)
assert user is not None
assert user["uid"] == uid
assert user["username"] == "dr_fallback"
def test_resolve_user_falls_back_to_devrant_token_bearer(local_db):
"""_resolve_user finds a user from Authorization: Bearer header containing a DevRant token key."""
uid = _seed_user("dr_bearer", "drbearer@test.dev")
token_key = secrets.token_hex(20)
expire_time = int(datetime.now(timezone.utc).timestamp()) + 86400
_seed_token(uid, token_key, expire_time)
request = _MockRequest(headers={"Authorization": f"Bearer {token_key}"})
user = _resolve_user(request)
assert user is not None
assert user["uid"] == uid
assert user["username"] == "dr_bearer"
def test_resolve_user_prefers_api_key_over_devrant(local_db):
"""If a key matches both an api_key and a devrant token, the api_key user wins."""
shared_key = secrets.token_hex(20)
# User A - owns the api_key
uid_a = _seed_user("api_key_owner", "apikey@test.dev", api_key=shared_key)
# User B - owns the devrant token with the same key
uid_b = _seed_user("devrant_owner", "drowner@test.dev")
expire_time = int(datetime.now(timezone.utc).timestamp()) + 86400
_seed_token(uid_b, shared_key, expire_time)
# ── X-API-KEY path ──
request = _MockRequest(headers={"X-API-KEY": shared_key})
user = _resolve_user(request)
assert user is not None
assert user["uid"] == uid_a
assert user["username"] == "api_key_owner"
# ── Bearer path ──
request2 = _MockRequest(headers={"Authorization": f"Bearer {shared_key}"})
user2 = _resolve_user(request2)
assert user2 is not None
assert user2["uid"] == uid_a
assert user2["username"] == "api_key_owner"