Files
kite/backend/tests/test_topics.py
T
curo1305 21ec9cb4c3 test(03-01): add Wave 0 xfail stubs and shared fixtures for Phase 3
- Add auth_user, admin_user, mock_minio_presigned, mock_minio_stat fixtures to conftest.py
- Create test_quota.py with 4 xfail stubs (STORE-03, STORE-05, STORE-06, SC2 race)
- Append test_migration_0003 to test_alembic.py (full pre-seed + post-migration assertions)
- Append 3 classifier xfail stubs (DOC-03, DOC-05, D-15)
- Append 6 document xfail stubs (D-05, STORE-04, SEC-04, D-16)
- Append 4 topic xfail stubs (DOC-04, D-09, D-17)
- Append test_settings_endpoint_removed stub (D-12)
- All 19 new test IDs collect cleanly with xfail(strict=False)
2026-05-23 13:42:37 +02:00

142 lines
5.0 KiB
Python

"""
Topics API tests — async only (Plan 05 cutover).
Legacy sync tests (using the flat-file storage layer and sync TestClient) were
updated to async in Plan 05 to match the new session-injected API routes.
"""
from __future__ import annotations
import pytest
async def test_list_topics_empty(async_client):
resp = await async_client.get("/api/topics")
assert resp.status_code == 200
assert resp.json()["topics"] == []
async def test_create_topic(async_client):
resp = await async_client.post(
"/api/topics",
json={"name": "Finance", "description": "Financial docs", "color": "#ff0000"},
)
assert resp.status_code == 200
data = resp.json()
assert data["name"] == "Finance"
assert data["color"] == "#ff0000"
assert "id" in data
async def test_create_topic_deduplication(async_client):
await async_client.post("/api/topics", json={"name": "Finance"})
resp = await async_client.post("/api/topics", json={"name": "finance"}) # case-insensitive
assert resp.status_code == 200
topics = (await async_client.get("/api/topics")).json()["topics"]
assert len(topics) == 1
async def test_update_topic(async_client):
create = (await async_client.post("/api/topics", json={"name": "Old Name"})).json()
resp = await async_client.patch(f"/api/topics/{create['id']}", json={"name": "New Name"})
assert resp.status_code == 200
assert resp.json()["name"] == "New Name"
async def test_update_topic_not_found(async_client):
resp = await async_client.patch(
"/api/topics/00000000-0000-0000-0000-000000000000",
json={"name": "X"},
)
assert resp.status_code == 404
async def test_delete_topic(async_client):
create = (await async_client.post("/api/topics", json={"name": "ToDelete"})).json()
resp = await async_client.delete(f"/api/topics/{create['id']}")
assert resp.status_code == 200
assert resp.json()["success"] is True
topics = (await async_client.get("/api/topics")).json()["topics"]
assert not any(t["name"] == "ToDelete" for t in topics)
async def test_delete_topic_cascades_to_documents(async_client, db_session, sample_txt):
# Create a topic
topic = (await async_client.post("/api/topics", json={"name": "Legal"})).json()
# Upload doc (no auto classify)
with open(sample_txt, "rb") as f:
upload = (
await async_client.post(
"/api/documents/upload",
files={"file": ("sample.txt", f, "text/plain")},
data={"auto_classify": "false"},
)
).json()
# Manually set topic via the storage service
from services import storage
await storage.update_document_topics(db_session, upload["id"], ["Legal"])
# Delete topic
await async_client.delete(f"/api/topics/{topic['id']}")
# Verify document no longer has the topic
doc = (await async_client.get(f"/api/documents/{upload['id']}")).json()
assert "Legal" not in doc["topics"]
async def test_delete_topic_not_found(async_client):
resp = await async_client.delete("/api/topics/nonexistent")
assert resp.status_code == 404
# ---------------------------------------------------------------------------
# Wave 0 xfail stubs for Phase 3 topic namespace tests — Plan 03-03
# ---------------------------------------------------------------------------
@pytest.mark.xfail(strict=False, reason="implemented in plan 03-03")
async def test_topic_namespace(async_client, auth_user, db_session):
"""GET /api/topics returns only system topics (user_id=NULL) + auth_user-owned topics.
DOC-04: layered topic namespace — system topics (user_id=NULL) are visible to
all users; per-user topics (user_id=current_user.id) are visible only to that
user. A different user's topics must not appear (CONTEXT.md D-08, D-17).
Test setup: seed one system topic, one auth_user-owned topic, one topic owned
by a different user. GET /api/topics must return exactly the first two.
"""
assert True # scaffold
@pytest.mark.xfail(strict=False, reason="implemented in plan 03-03")
async def test_admin_create_system_topic(async_client, admin_user):
"""POST /api/admin/topics returns 201 and creates a Topic with user_id=NULL.
D-09: only admin can create system topics via POST /api/admin/topics.
The created topic has user_id=NULL and is visible to all users.
"""
assert True # scaffold
@pytest.mark.xfail(strict=False, reason="implemented in plan 03-03")
async def test_regular_user_cannot_create_system_topic(async_client, auth_user):
"""POST /api/admin/topics with auth_user.headers returns 403.
D-09: the admin topics endpoint requires get_current_admin; regular users
receive 403 Forbidden.
"""
assert True # scaffold
@pytest.mark.xfail(strict=False, reason="implemented in plan 03-03")
async def test_topics_require_auth(async_client):
"""Anonymous GET /api/topics (no Authorization header) returns 401 or 403.
D-17: /api/topics/* gains get_current_user in Phase 3 — anonymous access
must be rejected.
"""
assert True # scaffold