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
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import json
|
||||
import shutil
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import pytest
|
||||
from starlette.websockets import WebSocketDisconnect
|
||||
|
||||
from devplacepy.config import DBAPI_DIR
|
||||
from devplacepy.services.manager import service_manager
|
||||
|
||||
|
||||
@@ -74,3 +79,73 @@ def test_ws_non_owner_retries(client, auth):
|
||||
assert info.value.code == 4013
|
||||
finally:
|
||||
service_manager.set_lock_owner(True)
|
||||
|
||||
|
||||
def test_result_route_serves_the_persisted_rows(client, auth):
|
||||
enqueued = client.post(
|
||||
"/dbapi/query/async", json={"sql": "SELECT 1 AS n"}, headers=auth
|
||||
)
|
||||
uid = enqueued.json()["uid"]
|
||||
output_dir = DBAPI_DIR / uid
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
result = {
|
||||
"sql": "SELECT 1 AS n",
|
||||
"row_count": 1,
|
||||
"truncated": False,
|
||||
"suspicious": [],
|
||||
"rows": [{"n": 1}],
|
||||
}
|
||||
(output_dir / "result.json").write_text(json.dumps(result), encoding="utf-8")
|
||||
try:
|
||||
response = client.get(f"/dbapi/query/{uid}/result", headers=auth)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json()["rows"] == [{"n": 1}]
|
||||
finally:
|
||||
shutil.rmtree(output_dir, ignore_errors=True)
|
||||
|
||||
|
||||
def test_result_route_404s_without_a_written_result(client, auth):
|
||||
enqueued = client.post(
|
||||
"/dbapi/query/async", json={"sql": "SELECT 1 AS n"}, headers=auth
|
||||
)
|
||||
uid = enqueued.json()["uid"]
|
||||
response = client.get(f"/dbapi/query/{uid}/result", headers=auth)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_result_route_rejects_a_traversal_uid(client, auth):
|
||||
from devplacepy.database import get_table
|
||||
|
||||
evil_uid = ".."
|
||||
now = datetime.now(timezone.utc).isoformat()
|
||||
get_table("jobs").insert(
|
||||
{
|
||||
"uid": evil_uid,
|
||||
"kind": "dbquery",
|
||||
"status": "done",
|
||||
"owner_kind": "user",
|
||||
"owner_id": "traversal-owner",
|
||||
"preferred_name": "",
|
||||
"payload": "{}",
|
||||
"result": "{}",
|
||||
"error": "",
|
||||
"retry_count": 0,
|
||||
"created_at": now,
|
||||
"started_at": now,
|
||||
"completed_at": now,
|
||||
"updated_at": now,
|
||||
"duration_ms": 0,
|
||||
"last_accessed_at": "",
|
||||
"expires_at": "",
|
||||
"bytes_in": 0,
|
||||
"bytes_out": 0,
|
||||
"item_count": 0,
|
||||
}
|
||||
)
|
||||
escape_target = DBAPI_DIR.parent / "result.json"
|
||||
try:
|
||||
response = client.get(f"/dbapi/query/{evil_uid}/result", headers=auth)
|
||||
assert response.status_code in (400, 404), response.text
|
||||
assert not escape_target.exists()
|
||||
finally:
|
||||
get_table("jobs").delete(uid=evil_uid)
|
||||
|
||||
Reference in New Issue
Block a user