Spaces:
Running
Running
| """ | |
| Tests for GET /saved page. | |
| Covers: empty state, paper listing, cookie, remove action, source logging. | |
| """ | |
| import asyncio | |
| import pytest | |
| from fastapi.testclient import TestClient | |
| def client(tmp_path, monkeypatch): | |
| import app.config as cfg | |
| import app.db as db_mod | |
| db_path = str(tmp_path / "test.db") | |
| monkeypatch.setattr(cfg, "DB_PATH", db_path) | |
| monkeypatch.setattr(db_mod, "DB_PATH", db_path) | |
| import app.user_state as us | |
| us._cache.clear() | |
| from app.qdrant_svc import _client | |
| _client.cache_clear() | |
| from app.main import app | |
| asyncio.get_event_loop().run_until_complete(db_mod.init_db()) | |
| with TestClient(app, raise_server_exceptions=True) as c: | |
| yield c | |
| # ββ Basic page behaviour ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def test_saved_page_returns_200(client): | |
| resp = client.get("/saved") | |
| assert resp.status_code == 200 | |
| def test_saved_page_sets_cookie(client): | |
| resp = client.get("/saved") | |
| assert "arxiv_user_id" in resp.cookies | |
| def test_saved_page_empty_for_new_user(client): | |
| """New user has no saves β shows the empty-state message.""" | |
| resp = client.get("/saved") | |
| assert resp.status_code == 200 | |
| assert "No saved papers" in resp.text | |
| def test_saved_page_shows_paper_after_save(client, monkeypatch): | |
| """After saving a paper, it appears on the saved page.""" | |
| import app.arxiv_svc as arxiv | |
| async def fake_batch(ids): | |
| return { | |
| "1706.03762": { | |
| "arxiv_id": "1706.03762", | |
| "title": "Attention Is All You Need", | |
| "abstract": "The original transformer paper.", | |
| "authors": '["Vaswani"]', | |
| "category": "cs.CL", | |
| "published": "2017-06-12", | |
| "year": 2017, | |
| } | |
| } | |
| monkeypatch.setattr(arxiv, "fetch_metadata_batch", fake_batch) | |
| client.get("/") | |
| client.post("/api/papers/1706.03762/save", data={"source": "search"}) | |
| resp = client.get("/saved") | |
| assert resp.status_code == 200 | |
| assert "Attention Is All You Need" in resp.text | |
| def test_saved_page_shows_correct_count(client, monkeypatch): | |
| """The count badge reflects the number of saved papers.""" | |
| import app.arxiv_svc as arxiv | |
| papers = { | |
| "1706.03762": { | |
| "arxiv_id": "1706.03762", "title": "Attention Is All You Need", "abstract": "...", | |
| "authors": '[]', "category": "cs.CL", "published": "2017-06-12", "year": 2017, | |
| }, | |
| "1512.03385": { | |
| "arxiv_id": "1512.03385", "title": "Deep Residual Learning for Image Recognition", | |
| "abstract": "...", "authors": '[]', "category": "cs.CV", | |
| "published": "2015-12-10", "year": 2015, | |
| }, | |
| } | |
| async def fake_batch(ids): | |
| return {k: v for k, v in papers.items() if k in ids} | |
| monkeypatch.setattr(arxiv, "fetch_metadata_batch", fake_batch) | |
| client.get("/") | |
| client.post("/api/papers/1706.03762/save", data={"source": "search"}) | |
| client.post("/api/papers/1512.03385/save", data={"source": "search"}) | |
| resp = client.get("/saved") | |
| assert resp.status_code == 200 | |
| assert "2 saved" in resp.text | |
| # ββ Remove from saved βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def test_remove_paper_updates_state(client): | |
| """Dismissing a saved paper removes it from positive_list and adds to negatives.""" | |
| import app.user_state as us | |
| client.get("/") | |
| user_id = client.cookies.get("arxiv_user_id") | |
| client.post("/api/papers/1706.03762/save", data={"source": "search"}) | |
| state = us.get_user_state(user_id) | |
| assert "1706.03762" in state.positive_list | |
| client.post("/api/papers/1706.03762/not-interested", data={"source": "saved"}) | |
| state = us.get_user_state(user_id) | |
| assert "1706.03762" not in state.positive_list | |
| assert "1706.03762" in state.negative_list | |
| def test_remove_returns_empty_response(client): | |
| """not-interested returns empty HTML (HTMX removes card).""" | |
| client.get("/") | |
| client.post("/api/papers/1706.03762/save", data={"source": "search"}) | |
| resp = client.post("/api/papers/1706.03762/not-interested", data={"source": "saved"}) | |
| assert resp.status_code == 200 | |
| assert resp.text.strip() == "" | |
| # ββ Source logging ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def test_save_source_is_logged(client): | |
| """Source field on the save action is persisted to the DB.""" | |
| import app.db as db_mod | |
| client.get("/") | |
| user_id = client.cookies.get("arxiv_user_id") | |
| client.post("/api/papers/1706.03762/save", data={"source": "search", "position": "2"}) | |
| rows = asyncio.get_event_loop().run_until_complete( | |
| db_mod.get_user_interactions(user_id, event_types=["save"]) | |
| ) | |
| assert len(rows) == 1 | |
| assert rows[0]["paper_id"] == "1706.03762" | |
| def test_dismiss_source_saved_is_logged(client): | |
| """Dismiss from saved page logs source='saved'.""" | |
| import app.db as db_mod | |
| client.get("/") | |
| user_id = client.cookies.get("arxiv_user_id") | |
| client.post("/api/papers/1706.03762/save", data={"source": "search"}) | |
| client.post("/api/papers/1706.03762/not-interested", data={"source": "saved"}) | |
| rows = asyncio.get_event_loop().run_until_complete( | |
| db_mod.get_user_interactions(user_id, event_types=["not_interested"]) | |
| ) | |
| assert len(rows) == 1 | |
| assert rows[0]["paper_id"] == "1706.03762" | |