File size: 4,491 Bytes
7c6ffa6 3bcdb36 7c6ffa6 | 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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | from __future__ import annotations
def test_document_upload_extracts_and_chunks_text_file(client) -> None:
response = client.post(
"/documents/upload",
data={
"title": "Upload smoke test",
"subject": "Biology",
"chapter": "Photosynthesis",
},
files={
"file": (
"photosynthesis.txt",
b"Photosynthesis uses light to make glucose and oxygen.",
"text/plain",
),
},
)
assert response.status_code == 201
payload = response.json()
assert payload["status"] == "ready"
assert payload["chunk_count"] >= 1
# extracted_text was removed from API response (security hardening)
assert "extracted_text" not in payload
def test_document_upload_returns_failed_document_when_indexing_fails(client, monkeypatch) -> None:
from app.routes import documents
def fail_indexing(*_args, **_kwargs) -> None:
raise RuntimeError("embedding backend unavailable")
monkeypatch.setattr(documents, "replace_document_chunks", fail_indexing)
response = client.post(
"/documents/upload",
data={"title": "Indexing failure"},
files={
"file": (
"notes.txt",
b"This text extracts but indexing fails.",
"text/plain",
),
},
)
assert response.status_code == 201
payload = response.json()
assert payload["status"] == "failed"
assert payload["chunk_count"] == 0
# extraction_error is stored server-side but must NOT be in the API response
assert "extraction_error" not in payload
def test_document_upload_strips_lone_surrogates_before_database_write(client, monkeypatch) -> None:
from app.routes import documents
monkeypatch.setattr(
documents,
"extract_text_from_file",
lambda *_args, **_kwargs: "Clean biology text \ud835 with invalid surrogate.",
)
response = client.post(
"/documents/upload",
data={"title": "Unicode repair", "subject": "Biology"},
files={
"file": (
"unicode.txt",
b"placeholder",
"text/plain",
),
},
)
assert response.status_code == 201
payload = response.json()
assert payload["status"] == "ready"
# extracted_text was removed from API response (security hardening);
# verify it is NOT leaked to the client and the upload still succeeds
assert "extracted_text" not in payload
def test_upload_rejects_unsupported_mime_type(client) -> None:
response = client.post(
"/documents/upload",
data={"title": "Bad type"},
files={"file": ("notes.exe", b"MZ\x90\x00", "application/x-msdownload")},
)
assert response.status_code == 415
def test_upload_rejects_images_before_creating_a_failed_document(client) -> None:
response = client.post(
"/documents/upload",
data={"title": "Scanned notes"},
files={"file": ("notes.png", b"\x89PNG\r\n\x1a\n", "image/png")},
)
assert response.status_code == 415
detail = response.json()["detail"]
assert "Image OCR" in detail
assert client.get("/documents").json() == []
def test_upload_rejects_file_over_the_size_limit(client) -> None:
# 20 MB + 1 byte of allowed-type content must be rejected as 413, not stored.
oversized = b"a" * (20 * 1024 * 1024 + 1)
response = client.post(
"/documents/upload",
data={"title": "Too big"},
files={"file": ("huge.txt", oversized, "text/plain")},
)
assert response.status_code == 413
def test_upload_accepts_a_malayalam_filename_and_sanitises_it(client) -> None:
response = client.post(
"/documents/upload",
data={"title": "Malayalam name"},
files={
"file": (
"ശബ്ദം physics notes.txt",
b"Sound travels as a longitudinal wave.",
"text/plain",
),
},
)
assert response.status_code == 201
assert response.json()["status"] == "ready"
def test_upload_requires_authentication(auth_client) -> None:
# No Authorization header -> the upload endpoint must refuse.
response = auth_client.post(
"/documents/upload",
data={"title": "Anon"},
files={"file": ("x.txt", b"hello", "text/plain")},
)
assert response.status_code in (401, 403)
|