Files
AI/ontology_platform/tests/integration/test_phase6_maintenance_api.py

112 lines
3.6 KiB
Python
Raw Normal View History

2026-05-19 20:31:52 +09:00
"""Phase 6 maintenance API tests."""
from __future__ import annotations
import importlib
from collections.abc import Generator
from contextlib import asynccontextmanager
from datetime import datetime
from fastapi.testclient import TestClient
from ont_platform.api import db_deps
from ont_platform.storage.candidate_repository import CandidateRepository
from ont_platform.storage.models import Base, SourceDocument
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 _phase6_client(monkeypatch) -> TestClient:
monkeypatch.setenv("PHASE", "6")
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()
if db.get(SourceDocument, "doc_api_phase6") is None:
_seed(db)
db.commit()
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 _seed(db: Session) -> None:
db.add(
SourceDocument(
id="doc_api_phase6",
project_id="proj_api_phase6",
source_url="https://example.test/api-phase6",
document_type="html",
title="API Phase 6 Source",
text="Acme appears in a source.",
content_hash="hash_api_phase6",
fingerprint="fp_api_phase6",
retrieved_at=datetime.utcnow(),
extracted_by="trafilatura",
)
)
CandidateRepository(db).save_lightweight_result(
project_id="proj_api_phase6",
document_id="doc_api_phase6",
result={
"entities": [
{
"id": "E_api_phase6",
"label": "Acme",
"type": "org",
"confidence": 0.4,
"evidence_ids": [],
}
],
"relations": [],
"evidence_spans": [],
},
)
def test_phase6_api_runs_loop_and_reviews_proposal(monkeypatch) -> None:
with _phase6_client(monkeypatch) as client:
run_response = client.post(
"/api/v1/maintenance/runs",
json={
"project_id": "proj_api_phase6",
"requested_by": "ops",
"actor_role": "admin",
},
)
proposal_response = client.get(
"/api/v1/maintenance/proposals",
params={"project_id": "proj_api_phase6", "status": "pending_review"},
)
proposal_id = proposal_response.json()["proposals"][0]["id"]
review_response = client.post(
f"/api/v1/maintenance/proposals/{proposal_id}/review",
json={"reviewed_by": "admin", "actor_role": "admin", "approve": True},
)
assert run_response.status_code == 200, run_response.text
assert run_response.json()["run"]["summary"]["direct_mutations"] == 0
assert proposal_response.status_code == 200
assert proposal_response.json()["proposals"]
assert review_response.status_code == 200, review_response.text
assert review_response.json()["proposal"]["status"] == "approved"