File size: 3,002 Bytes
a753e74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
"""Tests for the system/config/papers API routes."""

import json

import pytest
from fastapi.testclient import TestClient

from researchlink.api.app import app

client = TestClient(app)


@pytest.fixture
def papers_dir(tmp_path, monkeypatch):
    """Point the API at a temp papers/ root with one module."""
    mod = tmp_path / "2024-x"
    mod.mkdir()
    (mod / "metadata.json").write_text(json.dumps({
        "slug": "2024-x", "conflicts": [],
        "fields": {"title": {"value": "Paper X", "status": "verified"},
                   "year": {"value": 2024}, "authors": {"value": ["A"]}},
    }), encoding="utf-8")
    (mod / "summary.md").write_text("# Summary\n", encoding="utf-8")
    monkeypatch.setenv("RESEARCHLINK_OUTPUT_DIR", str(tmp_path))
    from researchlink import config
    config.get_settings.__globals__["_settings"] = None
    yield tmp_path
    config.get_settings.__globals__["_settings"] = None


def test_health():
    r = client.get("/api/health")
    assert r.status_code == 200 and r.json()["backend"] == "ok"


def test_config_has_no_keys():
    body = client.get("/api/config").text
    assert "api_key" not in body and "sk-" not in body
    data = json.loads(body)
    assert data["mode"] in ("offline", "anthropic", "openai", "openrouter", "ollama", "custom")


def test_llm_status_safe():
    d = client.get("/api/llm/status").json()
    assert "tasks" in d and "api_key" not in d


def test_papers_list(papers_dir):
    d = client.get("/api/papers").json()
    slugs = [p["slug"] for p in d["papers"]]
    assert "2024-x" in slugs
    p = next(p for p in d["papers"] if p["slug"] == "2024-x")
    assert p["title"] == "Paper X"
    assert p["artifacts"]["summary.md"] == "generated"       # exists
    assert p["artifacts"]["review.md"] == "not-generated"    # missing


def test_paper_detail(papers_dir):
    d = client.get("/api/papers/2024-x").json()
    assert d["metadata"]["slug"] == "2024-x"
    assert d["artifacts"]["summary.md"]["content"].startswith("# Summary")
    assert d["artifacts"]["summary.md"]["status"] == "generated"


def test_paper_detail_missing_artifact_placeholder(papers_dir):
    d = client.get("/api/papers/2024-x").json()
    review = d["artifacts"]["review.md"]
    assert review["status"] == "not-generated"
    assert "not-generated" in review["content"]           # clean placeholder, never null
    assert review["exists"] is False


def test_raw_artifact_and_traversal(papers_dir):
    assert client.get("/api/papers/2024-x/raw/summary.md").text.startswith("# Summary")
    assert client.get("/api/papers/2024-x/raw/nope.md").status_code == 404
    assert client.get("/api/papers/2024-x/raw/..%2f..%2fetc").status_code in (400, 404)


def test_paper_detail_404(papers_dir):
    assert client.get("/api/papers/does-not-exist").status_code == 404


def test_paper_traversal_blocked(papers_dir):
    # encoded traversal should not escape the papers root
    assert client.get("/api/papers/..%2f..%2fetc").status_code in (400, 404)