Isolate cloud model access before enabling product RAG workflows
Some checks failed
verify / verify (push) Has been cancelled
Some checks failed
verify / verify (push) Has been cancelled
The API and ingestion tools now use a fixed internal model gateway while governed profiles, embedding cache assignments, traceable citations, and stable API errors establish the boundaries required by later workflows. Constraint: The current Alibaba Cloud workspace rejects all three live model calls with authentication failures Rejected: Give the API or seed tools the Bailian key and direct egress | combines database access, cloud credentials, and public network access Rejected: Mix offline and Bailian vectors in one demo namespace | makes profile activation and retrieval ambiguous Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep Bailian credentials and egress exclusive to model-gateway and create a new immutable profile hash for any embedding identity change Tested: make verify; 121 backend tests; 14 frontend tests; fresh and populated Alembic upgrade-downgrade-upgrade; two idempotent offline seeds; Docker health and HTTP retrieval; isolated provider smoke Not-tested: Successful live Bailian responses because the supplied workspace credential currently fails authentication
This commit is contained in:
65
backend/tests/unit/test_application_contract.py
Normal file
65
backend/tests/unit/test_application_contract.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.core.problems import ApiProblem
|
||||
from app.main import create_app
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_application_factory_generates_openapi_without_runtime_secrets() -> None:
|
||||
app = create_app()
|
||||
schema = app.openapi()
|
||||
|
||||
assert schema["openapi"].startswith("3.")
|
||||
assert "/api/v1/health/live" in schema["paths"]
|
||||
assert "/api/v1/meta" in schema["paths"]
|
||||
assert "/health/live" not in schema["paths"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_trace_id_accepts_only_uuid_and_is_returned() -> None:
|
||||
app = create_app()
|
||||
transport = httpx.ASGITransport(app=app)
|
||||
supplied = str(uuid.uuid4())
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
accepted = await client.get("/api/v1/health/live", headers={"x-request-id": supplied})
|
||||
replaced = await client.get(
|
||||
"/api/v1/health/live",
|
||||
headers={"x-request-id": "secret-or-unbounded-client-value"},
|
||||
)
|
||||
|
||||
assert accepted.headers["x-request-id"] == supplied
|
||||
assert uuid.UUID(replaced.headers["x-request-id"])
|
||||
assert replaced.headers["x-request-id"] != "secret-or-unbounded-client-value"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_formal_api_problem_is_sanitized_and_traceable() -> None:
|
||||
app = create_app()
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/api/v1/problem-test")
|
||||
async def fail() -> None:
|
||||
raise ApiProblem(
|
||||
status=409,
|
||||
code="VERSION_CONFLICT",
|
||||
title="Version conflict",
|
||||
detail="The resource changed; reload and retry.",
|
||||
)
|
||||
|
||||
app.include_router(router)
|
||||
transport = httpx.ASGITransport(app=app)
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
response = await client.get("/api/v1/problem-test")
|
||||
|
||||
payload = response.json()
|
||||
assert response.status_code == 409
|
||||
assert response.headers["content-type"].startswith("application/problem+json")
|
||||
assert payload["code"] == "VERSION_CONFLICT"
|
||||
assert uuid.UUID(payload["trace_id"])
|
||||
assert payload["field_errors"] == []
|
||||
Reference in New Issue
Block a user