151 lines
4.8 KiB
Python
151 lines
4.8 KiB
Python
import json
|
|
|
|
import pytest
|
|
from app import api as api_module # noqa: F401
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def reset_globals(tmp_path):
|
|
from app import api
|
|
api.set_data_dir(tmp_path)
|
|
api.reset_last_action()
|
|
yield
|
|
api.reset_last_action()
|
|
|
|
|
|
def test_import():
|
|
from app import api # noqa: F401
|
|
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
from app.api import app
|
|
return TestClient(app)
|
|
|
|
|
|
@pytest.fixture
|
|
def queue_with_items():
|
|
"""Write 3 test emails to the queue file."""
|
|
from app import api as api_module
|
|
items = [
|
|
{"id": f"id{i}", "subject": f"Subject {i}", "body": f"Body {i}",
|
|
"from": "test@example.com", "date": "2026-03-01", "source": "imap:test"}
|
|
for i in range(3)
|
|
]
|
|
queue_path = api_module._DATA_DIR / "email_label_queue.jsonl"
|
|
queue_path.write_text("\n".join(json.dumps(x) for x in items) + "\n")
|
|
return items
|
|
|
|
|
|
def test_queue_returns_items(client, queue_with_items):
|
|
r = client.get("/api/queue?limit=2")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert len(data["items"]) == 2
|
|
assert data["total"] == 3
|
|
|
|
|
|
def test_queue_empty_when_no_file(client):
|
|
r = client.get("/api/queue")
|
|
assert r.status_code == 200
|
|
assert r.json() == {"items": [], "total": 0}
|
|
|
|
|
|
def test_label_appends_to_score(client, queue_with_items):
|
|
from app import api as api_module
|
|
r = client.post("/api/label", json={"id": "id0", "label": "interview_scheduled"})
|
|
assert r.status_code == 200
|
|
records = api_module._read_jsonl(api_module._score_file())
|
|
assert len(records) == 1
|
|
assert records[0]["id"] == "id0"
|
|
assert records[0]["label"] == "interview_scheduled"
|
|
assert "labeled_at" in records[0]
|
|
|
|
def test_label_removes_from_queue(client, queue_with_items):
|
|
from app import api as api_module
|
|
client.post("/api/label", json={"id": "id0", "label": "rejected"})
|
|
queue = api_module._read_jsonl(api_module._queue_file())
|
|
assert not any(x["id"] == "id0" for x in queue)
|
|
|
|
def test_label_unknown_id_returns_404(client, queue_with_items):
|
|
r = client.post("/api/label", json={"id": "unknown", "label": "neutral"})
|
|
assert r.status_code == 404
|
|
|
|
def test_skip_moves_to_back(client, queue_with_items):
|
|
from app import api as api_module
|
|
r = client.post("/api/skip", json={"id": "id0"})
|
|
assert r.status_code == 200
|
|
queue = api_module._read_jsonl(api_module._queue_file())
|
|
assert queue[-1]["id"] == "id0"
|
|
assert queue[0]["id"] == "id1"
|
|
|
|
def test_skip_unknown_id_returns_404(client, queue_with_items):
|
|
r = client.post("/api/skip", json={"id": "nope"})
|
|
assert r.status_code == 404
|
|
|
|
|
|
# --- Part A: POST /api/discard ---
|
|
|
|
def test_discard_writes_to_discarded_file(client, queue_with_items):
|
|
from app import api as api_module
|
|
r = client.post("/api/discard", json={"id": "id1"})
|
|
assert r.status_code == 200
|
|
discarded = api_module._read_jsonl(api_module._discarded_file())
|
|
assert len(discarded) == 1
|
|
assert discarded[0]["id"] == "id1"
|
|
assert discarded[0]["label"] == "__discarded__"
|
|
|
|
def test_discard_removes_from_queue(client, queue_with_items):
|
|
from app import api as api_module
|
|
client.post("/api/discard", json={"id": "id1"})
|
|
queue = api_module._read_jsonl(api_module._queue_file())
|
|
assert not any(x["id"] == "id1" for x in queue)
|
|
|
|
|
|
# --- Part B: DELETE /api/label/undo ---
|
|
|
|
def test_undo_label_removes_from_score(client, queue_with_items):
|
|
from app import api as api_module
|
|
client.post("/api/label", json={"id": "id0", "label": "neutral"})
|
|
r = client.delete("/api/label/undo")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert data["undone"]["type"] == "label"
|
|
score = api_module._read_jsonl(api_module._score_file())
|
|
assert score == []
|
|
|
|
def test_undo_discard_removes_from_discarded(client, queue_with_items):
|
|
from app import api as api_module
|
|
client.post("/api/discard", json={"id": "id0"})
|
|
r = client.delete("/api/label/undo")
|
|
assert r.status_code == 200
|
|
discarded = api_module._read_jsonl(api_module._discarded_file())
|
|
assert discarded == []
|
|
|
|
def test_undo_skip_restores_to_front(client, queue_with_items):
|
|
from app import api as api_module
|
|
client.post("/api/skip", json={"id": "id0"})
|
|
r = client.delete("/api/label/undo")
|
|
assert r.status_code == 200
|
|
queue = api_module._read_jsonl(api_module._queue_file())
|
|
assert queue[0]["id"] == "id0"
|
|
|
|
def test_undo_with_no_action_returns_404(client):
|
|
r = client.delete("/api/label/undo")
|
|
assert r.status_code == 404
|
|
|
|
|
|
# --- Part C: GET /api/config/labels ---
|
|
|
|
def test_config_labels_returns_metadata(client):
|
|
r = client.get("/api/config/labels")
|
|
assert r.status_code == 200
|
|
labels = r.json()
|
|
assert len(labels) == 10
|
|
assert labels[0]["key"] == "1"
|
|
assert "emoji" in labels[0]
|
|
assert "color" in labels[0]
|
|
assert "name" in labels[0]
|