This repository has been archived on 2026-10-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
recoll-modern/tests/integration/test_read_only_corpus.py
T

102 lines
4.5 KiB
Python

"""The service must not be able to mutate its document mount."""
import os
import subprocess
import time
import uuid
from collections.abc import Callable
from pathlib import Path
import httpx
import pytest
def test_document_mount_is_read_only(
compose_exec: Callable[[list[str]], subprocess.CompletedProcess[str]],
) -> None:
result = compose_exec(["sh", "-c", "touch /documents/must-fail"])
assert result.returncode != 0
def test_generated_corpus_indexes_and_searches(
compose_exec: Callable[[list[str]], subprocess.CompletedProcess[str]],
) -> None:
corpus_file = os.environ.get("RECOLL_MODERN_TEST_CORPUS_FILE")
if corpus_file is None:
document_root = os.environ.get("RECOLL_MODERN_DOCUMENTS")
if document_root is None:
pytest.fail("RECOLL_MODERN_DOCUMENTS is required for generated-corpus verification")
relative = Path(f"recoll-modern-test-{uuid.uuid4().hex}") / "probe.txt"
file = Path(document_root) / relative
file.parent.mkdir(parents=True)
file.write_text("Unique zebraplatypus integration corpus.\n")
corpus_file = relative.as_posix()
assert compose_exec(["test", "-f", f"/documents/{corpus_file}"]).returncode == 0
port = os.environ.get("RECOLL_MODERN_PUBLISH_PORT", "8000")
username = os.environ["RECOLL_MODERN_ADMIN_USERNAME"]
password = os.environ["RECOLL_MODERN_ADMIN_PASSWORD"]
prefix = os.environ.get("RECOLL_MODERN_ROOT_PATH", "/").rstrip("/")
with httpx.Client(base_url=f"http://127.0.0.1:{port}{prefix}", timeout=10) as client:
login = client.post("/auth/login", json={"username": username, "password": password})
assert login.status_code == 200, login.text
csrf = login.json()["csrf_token"]
cookie = login.cookies.get("recoll_modern_session")
assert cookie
headers = {"X-CSRF-Token": csrf, "Cookie": f"recoll_modern_session={cookie}"}
created = client.post(
"/collections", json={"name": f"Integration {time.time_ns()}"}, headers=headers
)
assert created.status_code == 201, created.text
collection_id = created.json()["id"]
queued = client.post(
f"/collections/{collection_id}/memberships", headers=headers,
json={"sources": [{"type": "file", "path": corpus_file}]},
)
assert queued.status_code == 202, queued.text
job_id = queued.json()["job"]["id"]
deadline = time.monotonic() + 30
while time.monotonic() < deadline:
job = client.get(f"/jobs/{job_id}", headers=headers).json()["job"]
if job["state"] not in ("queued", "running"):
break
time.sleep(0.1)
assert job["state"] == "completed", job
result = client.get(
"/search", params={"q": "zebraplatypus", "collections": collection_id},
headers=headers,
)
assert result.status_code == 200, result.text
assert result.json()["hits"], result.json()
document_id = result.json()["hits"][0]["document_id"]
second = client.post(
"/collections", json={"name": f"Integration second {time.time_ns()}"},
headers=headers,
)
assert second.status_code == 201, second.text
second_id = second.json()["id"]
second_job = client.post(
f"/collections/{second_id}/memberships", headers=headers,
json={"sources": [{"type": "file", "path": corpus_file}]},
)
assert second_job.status_code == 202, second_job.text
second_job_id = second_job.json()["job"]["id"]
deadline = time.monotonic() + 30
while time.monotonic() < deadline:
job = client.get(f"/jobs/{second_job_id}", headers=headers).json()["job"]
if job["state"] not in ("queued", "running"):
break
time.sleep(0.1)
assert job["state"] == "completed", job
combined = client.get(
"/search", params=[
("q", "zebraplatypus"), ("collections", collection_id),
("collections", second_id), ("fields", "collections"),
], headers=headers,
)
assert combined.status_code == 200, combined.text
assert len(combined.json()["hits"]) == 1
assert set(combined.json()["hits"][0]["collections"]) >= {collection_id, second_id}
extracted = client.get(f"/documents/{document_id}/text", headers=headers)
assert extracted.status_code == 200, extracted.text
assert "zebraplatypus" in extracted.json()["text"]