unsloth/tests/python/test_rag_reingest.py
Roland Tannous afa2ea0354 Studio: fix backend CI test failures
- test_safetensors_tool_loop: FakeExecuteTool stub now accepts the
  tool_context kwarg the loop passes (production is correct).
- test_desktop_auth: add rag_router to the health-check router stub
  so main.py's router import resolves.
- test_rag_reingest / test_rag_multimodal: load routes/rag.py by file
  path instead of `from routes.rag import`, which runs routes/__init__
  and eagerly imports the datasets router. On the GPU-less repo-cpu
  runner the unsloth bootstrap can leave `datasets` half-initialized,
  making that eager `from datasets import IterableDataset` raise.
2026-06-02 06:41:10 +04:00

81 lines
2.7 KiB
Python

"""Reingest endpoint tests (Backfill UX).
Full end-to-end reingest needs a running studio + a real embedder; that's
covered manually via the curl smoke flow in the plan. Here we cover the
parts that are testable without external models: payload validation and
the (multimodal, late) constraint propagation.
"""
import importlib.util
import sys
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[2]
STUDIO_BACKEND = REPO_ROOT / "studio" / "backend"
if str(STUDIO_BACKEND) not in sys.path:
sys.path.insert(0, str(STUDIO_BACKEND))
def _rag_route():
"""Load ``routes/rag.py`` directly, bypassing the ``routes`` package.
``from routes.rag import X`` first runs ``routes/__init__.py``, which eagerly
imports every router — including the datasets router, whose chain does
``from datasets import IterableDataset`` at import time. On a GPU-less CI
runner the unsloth bootstrap can leave ``datasets`` half-initialized, so that
eager import raises. These tests only need pure helpers from rag.py, so load
the file on its own (it has no intra-``routes`` imports).
"""
mod = sys.modules.get("_rag_route_under_test")
if mod is None:
spec = importlib.util.spec_from_file_location(
"_rag_route_under_test", STUDIO_BACKEND / "routes" / "rag.py"
)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
sys.modules["_rag_route_under_test"] = mod
return mod
def test_reingest_request_accepts_all_optional_fields():
ReingestKBRequest = _rag_route().ReingestKBRequest
empty = ReingestKBRequest()
assert empty.chunking_strategy is None
assert empty.mode is None
assert empty.embedding_model is None
partial = ReingestKBRequest(chunking_strategy = "late")
assert partial.chunking_strategy == "late"
assert partial.mode is None
def test_reingest_request_rejects_unknown_strategy():
from pydantic import ValidationError
ReingestKBRequest = _rag_route().ReingestKBRequest
with pytest.raises(ValidationError):
ReingestKBRequest(chunking_strategy = "telekinetic")
def test_reingest_request_rejects_unknown_mode():
from pydantic import ValidationError
ReingestKBRequest = _rag_route().ReingestKBRequest
with pytest.raises(ValidationError):
ReingestKBRequest(mode = "augmented")
def test_constraint_still_enforced_for_reingest_combos():
"""The combination guard is shared with create — verify it still bites."""
from fastapi import HTTPException
_validate_mode_combo = _rag_route()._validate_mode_combo
with pytest.raises(HTTPException) as excinfo:
_validate_mode_combo("multimodal", "late")
assert excinfo.value.status_code == 400