Merge remote-tracking branch 'origin/master' into riya/backend-unit-tests-core-utils

This commit is contained in:
Riya
2026-02-07 17:11:21 +00:00
12 changed files with 1890 additions and 4 deletions

View File

@@ -4,7 +4,9 @@ from fastapi import FastAPI, HTTPException
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from app.core.error_handling import REQUEST_ID_HEADER, install_error_handling
from starlette.requests import Request
from app.core.error_handling import REQUEST_ID_HEADER, _error_payload, _get_request_id, install_error_handling
def test_request_validation_error_includes_request_id():
@@ -80,3 +82,38 @@ def test_response_validation_error_returns_500_with_request_id():
assert body["detail"] == "Internal Server Error"
assert isinstance(body.get("request_id"), str) and body["request_id"]
assert resp.headers.get(REQUEST_ID_HEADER) == body["request_id"]
def test_client_provided_request_id_is_preserved():
app = FastAPI()
install_error_handling(app)
@app.get("/needs-int")
def needs_int(limit: int) -> dict[str, int]:
return {"limit": limit}
client = TestClient(app)
resp = client.get("/needs-int?limit=abc", headers={REQUEST_ID_HEADER: " req-123 "})
assert resp.status_code == 422
body = resp.json()
assert body["request_id"] == "req-123"
assert resp.headers.get(REQUEST_ID_HEADER) == "req-123"
def test_get_request_id_returns_none_for_missing_or_invalid_state() -> None:
# Empty state
req = Request({"type": "http", "headers": [], "state": {}})
assert _get_request_id(req) is None
# Non-string request_id
req = Request({"type": "http", "headers": [], "state": {"request_id": 123}})
assert _get_request_id(req) is None
# Empty string request_id
req = Request({"type": "http", "headers": [], "state": {"request_id": ""}})
assert _get_request_id(req) is None
def test_error_payload_omits_request_id_when_none() -> None:
assert _error_payload(detail="x", request_id=None) == {"detail": "x"}

View File

@@ -12,6 +12,21 @@ def test_matches_agent_mention_matches_first_name():
assert matches_agent_mention(agent, {"cooper"}) is False
def test_matches_agent_mention_no_mentions_is_false():
agent = Agent(name="Alice")
assert matches_agent_mention(agent, set()) is False
def test_matches_agent_mention_empty_agent_name_is_false():
agent = Agent(name=" ")
assert matches_agent_mention(agent, {"alice"}) is False
def test_matches_agent_mention_matches_full_normalized_name():
agent = Agent(name="Alice Cooper")
assert matches_agent_mention(agent, {"alice cooper"}) is True
def test_matches_agent_mention_supports_reserved_lead_shortcut():
lead = Agent(name="Riya", is_board_lead=True)
other = Agent(name="Lead", is_board_lead=False)

View File

@@ -0,0 +1,85 @@
from __future__ import annotations
import pytest
from app.core.error_handling import REQUEST_ID_HEADER, RequestIdMiddleware
@pytest.mark.asyncio
async def test_request_id_middleware_passes_through_non_http_scope() -> None:
called = False
async def app(scope, receive, send): # type: ignore[no-untyped-def]
nonlocal called
called = True
middleware = RequestIdMiddleware(app)
scope = {"type": "websocket", "headers": []}
await middleware(scope, lambda: None, lambda message: None) # type: ignore[arg-type]
assert called is True
@pytest.mark.asyncio
async def test_request_id_middleware_ignores_blank_client_header_and_generates_one() -> None:
captured_request_id: str | None = None
response_headers: list[tuple[bytes, bytes]] = []
async def app(scope, receive, send): # type: ignore[no-untyped-def]
nonlocal captured_request_id
captured_request_id = scope.get("state", {}).get("request_id")
await send({"type": "http.response.start", "status": 200, "headers": []})
await send({"type": "http.response.body", "body": b"ok"})
async def send(message): # type: ignore[no-untyped-def]
if message["type"] == "http.response.start":
response_headers.extend(list(message.get("headers") or []))
middleware = RequestIdMiddleware(app)
scope = {
"type": "http",
"headers": [(REQUEST_ID_HEADER.lower().encode("latin-1"), b" ")],
}
await middleware(scope, lambda: None, send)
assert isinstance(captured_request_id, str) and captured_request_id
# Header should reflect the generated id, not the blank one.
values = [v for k, v in response_headers if k.lower() == REQUEST_ID_HEADER.lower().encode("latin-1")]
assert values == [captured_request_id.encode("latin-1")]
@pytest.mark.asyncio
async def test_request_id_middleware_does_not_duplicate_existing_header() -> None:
sent_start = False
start_headers: list[tuple[bytes, bytes]] | None = None
async def app(scope, receive, send): # type: ignore[no-untyped-def]
# Simulate an app that already sets the request id header.
await send(
{
"type": "http.response.start",
"status": 200,
"headers": [(REQUEST_ID_HEADER.lower().encode("latin-1"), b"already")],
}
)
await send({"type": "http.response.body", "body": b"ok"})
async def send(message): # type: ignore[no-untyped-def]
nonlocal sent_start, start_headers
if message["type"] == "http.response.start":
sent_start = True
start_headers = list(message.get("headers") or [])
middleware = RequestIdMiddleware(app)
scope = {"type": "http", "headers": []}
await middleware(scope, lambda: None, send)
assert sent_start is True
assert start_headers is not None
# Ensure the middleware did not append a second copy.
values = [v for k, v in start_headers if k.lower() == REQUEST_ID_HEADER.lower().encode("latin-1")]
assert values == [b"already"]