POST /v1/lead-scoring/score now evaluates in the request, applies an overall AI deadline, and stores the completed run only after scoring. Idempotency is kept by (app_id, account_id, request_id) with a body fingerprint. Removes the webhook outbox, scheduler/recovery loop, app lead_scoring permission, credential audit events, seed_demo and generate_secret scripts, and the tests that covered them. Trims tests to behavior of the new flow.
60 lines
2.2 KiB
Python
60 lines
2.2 KiB
Python
import json
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
from ai_platform.infrastructure.openai_compatible_scoring import OpenAICompatibleScoringGateway
|
|
from ai_platform.lead_scoring.application.ai_provider import AIProviderFailure
|
|
|
|
|
|
def _completion(content: str) -> httpx.Response:
|
|
return httpx.Response(200, json={"choices": [{"message": {"content": content}}]})
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_compatible_adapter_accepts_json_fence_without_request_vkey():
|
|
requests: list[httpx.Request] = []
|
|
|
|
def handler(request: httpx.Request) -> httpx.Response:
|
|
requests.append(request)
|
|
assert "authorization" not in request.headers
|
|
assert json.loads(request.content)["model"] == "gemini-3.5-flash-lite"
|
|
return _completion('```json\n{"items":[]}\n```')
|
|
|
|
http = httpx.AsyncClient(transport=httpx.MockTransport(handler))
|
|
provider = OpenAICompatibleScoringGateway("http://127.0.0.1:8317", None, http)
|
|
try:
|
|
evidence = await provider.extract_evidence(
|
|
model="gemini-3.5-flash-lite", snapshot={}, vkey="", timeout=7
|
|
)
|
|
finally:
|
|
await http.aclose()
|
|
assert evidence == []
|
|
assert len(requests) == 1
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("response", "error_code"),
|
|
[
|
|
(
|
|
_completion('{"score":80,"score":1,"confidence":0.8,"reasoning":"a"}'),
|
|
"openai_invalid_response",
|
|
),
|
|
(_completion('{"score":"80","confidence":0.8,"reasoning":"a"}'), "openai_invalid_score"),
|
|
(httpx.Response(429, text="sensitive upstream error"), "openai_http_error"),
|
|
],
|
|
)
|
|
@pytest.mark.asyncio
|
|
async def test_compatible_adapter_rejects_unsafe_outputs(response: httpx.Response, error_code: str):
|
|
http = httpx.AsyncClient(transport=httpx.MockTransport(lambda _: response))
|
|
provider = OpenAICompatibleScoringGateway("https://proxy.example", "test-key", http)
|
|
try:
|
|
with pytest.raises(AIProviderFailure) as error:
|
|
await provider.score(
|
|
model="ignored", system_prompt="system", user_prompt="user", vkey="", timeout=7
|
|
)
|
|
finally:
|
|
await http.aclose()
|
|
assert error.value.error_code == error_code
|
|
assert "sensitive upstream error" not in str(error.value)
|