"""Phase 3 crawl job API tests.""" from __future__ import annotations import importlib from collections.abc import Generator from contextlib import asynccontextmanager from fastapi.testclient import TestClient from ont_platform.api import db_deps from ont_platform.storage.models import Base from sqlalchemy import create_engine from sqlalchemy.orm import Session, sessionmaker from sqlalchemy.pool import StaticPool main_module = importlib.import_module("ont_platform.api.main") @asynccontextmanager async def _noop_lifespan(app): yield def _phase3_client(monkeypatch) -> TestClient: monkeypatch.setenv("PHASE", "3") engine = create_engine( "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool, ) Base.metadata.create_all(engine) session_factory = sessionmaker(bind=engine) def override_get_db() -> Generator[Session, None, None]: db = session_factory() try: yield db finally: db.close() app = main_module.create_app() app.router.lifespan_context = _noop_lifespan app.dependency_overrides[db_deps.get_db] = override_get_db return TestClient(app, raise_server_exceptions=True, backend="asyncio") def test_phase3_crawl_job_accepts_inline_html_and_persists_progress(monkeypatch) -> None: html = """ Ontology Job

Ontology Job

Alice works at Acme in Berlin.

""" with _phase3_client(monkeypatch) as client: response = client.post( "/api/v1/crawl/jobs", json={ "project_id": "proj_crawl", "url": "https://example.test/job", "html": html, "profile": "dynamic_page", }, ) body = response.json() job_id = body["job"]["id"] status = client.get(f"/api/v1/crawl/jobs/{job_id}") assert response.status_code == 200, response.text assert body["job"]["status"] == "completed" assert body["job"]["document_id"].startswith("doc_") assert body["job"]["entity_count"] >= 1 assert body["job"]["metadata"]["progress"]["profile"] == "dynamic_page" assert body["job"]["metadata"]["progress"]["pages_completed"] == 1 assert status.status_code == 200 assert status.json()["job"]["id"] == job_id