Skip to content

Commit 65f9273

Browse files
testHealthCheck_file
1 parent 686c706 commit 65f9273

2 files changed

Lines changed: 197 additions & 0 deletions

File tree

src/tests/backend/context/test_cosmos_memory.py

Whitespace-only changes.
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# src/tests/backend/middleware/test_health_check.py
2+
3+
import pytest
4+
import asyncio
5+
import logging
6+
from fastapi import FastAPI, Request
7+
from fastapi.testclient import TestClient
8+
9+
from backend.middleware.health_check import (
10+
HealthCheckResult,
11+
HealthCheckSummary,
12+
HealthCheckMiddleware,
13+
)
14+
15+
16+
# --- Tests for HealthCheckResult and HealthCheckSummary ---
17+
18+
def test_health_check_result_attributes():
19+
res = HealthCheckResult(status=True, message="All good")
20+
assert res.status is True
21+
assert res.message == "All good"
22+
23+
24+
def test_health_check_summary_add_and_default():
25+
summary = HealthCheckSummary()
26+
# Initially status True, no results
27+
assert summary.status is True
28+
assert summary.results == {}
29+
30+
# Add default
31+
summary.AddDefault()
32+
assert "Default" in summary.results
33+
assert isinstance(summary.results["Default"], HealthCheckResult)
34+
assert summary.results["Default"].status is True
35+
assert summary.status is True
36+
37+
# Add a failing result
38+
summary.Add("FailTest", HealthCheckResult(False, "fail"))
39+
assert summary.results["FailTest"].status is False
40+
# Overall status now False
41+
assert summary.status is False
42+
43+
44+
def test_health_check_summary_add_exception():
45+
summary = HealthCheckSummary()
46+
summary.AddDefault()
47+
48+
class CustomError(Exception):
49+
pass
50+
51+
summary.AddException("ErrTest", CustomError("oops"))
52+
assert "ErrTest" in summary.results
53+
assert summary.results["ErrTest"].status is False
54+
assert "oops" in summary.results["ErrTest"].message
55+
56+
57+
# --- A real coroutine function that returns HealthCheckResult ---
58+
59+
async def real_pass_check():
60+
await asyncio.sleep(0)
61+
return HealthCheckResult(True, "ok")
62+
63+
64+
async def real_fail_check():
65+
await asyncio.sleep(0)
66+
return HealthCheckResult(False, "not ok")
67+
68+
69+
# --- Tests for HealthCheckMiddleware.check ---
70+
71+
@pytest.mark.asyncio
72+
async def test_check_invalid_pass_and_fail(monkeypatch, caplog):
73+
"""
74+
By default, HealthCheckMiddleware.check inspects `hasattr(check, "__await__")`
75+
on the check object itself. A bare `async def foo` is a function object that
76+
does NOT have __await__ at the function level—only its coroutine instance does.
77+
Hence both entries will be treated as invalid and immediately go to AddException.
78+
"""
79+
caplog.set_level(logging.ERROR)
80+
81+
checks = {
82+
"pass": real_pass_check,
83+
"fail": real_fail_check,
84+
}
85+
mw = HealthCheckMiddleware(app=None, checks=checks)
86+
summary = await mw.check()
87+
88+
# "Default" should still be present
89+
assert "Default" in summary.results
90+
91+
# Both "pass" and "fail" are treated as "not a coroutine function"
92+
assert summary.results["pass"].status is False
93+
assert "not a coroutine function" in summary.results["pass"].message
94+
95+
assert summary.results["fail"].status is False
96+
assert "not a coroutine function" in summary.results["fail"].message
97+
98+
# Overall status is False (because exceptions were raised)
99+
assert summary.status is False
100+
101+
102+
@pytest.mark.asyncio
103+
async def test_check_with_exception_in_coroutine(monkeypatch, caplog):
104+
caplog.set_level(logging.ERROR)
105+
106+
# We build a fake awaitable object whose __await__ is present at the instance level
107+
async def raising_coro():
108+
raise RuntimeError("boom")
109+
110+
class AsyncErrorCheck:
111+
def __call__(self):
112+
return raising_coro()
113+
114+
def __await__(self):
115+
return raising_coro().__await__()
116+
117+
checks = {
118+
"error": AsyncErrorCheck(),
119+
}
120+
mw = HealthCheckMiddleware(app=None, checks=checks)
121+
summary = await mw.check()
122+
123+
# "Default" + "error"
124+
assert "Default" in summary.results
125+
assert "error" in summary.results
126+
127+
# Because raising_coro throws, it should end up in AddException
128+
assert summary.results["error"].status is False
129+
assert "boom" in summary.results["error"].message
130+
131+
# Overall status is False
132+
assert summary.status is False
133+
134+
135+
# --- Tests for dispatch behavior using TestClient ---
136+
137+
@pytest.fixture
138+
def app_with_middleware():
139+
app = FastAPI()
140+
141+
# We build two awaitable‐styled objects: one that passes, one that fails
142+
class AsyncPassCheck:
143+
def __call__(self):
144+
return real_pass_check()
145+
146+
def __await__(self):
147+
return real_pass_check().__await__()
148+
149+
class AsyncFailCheck:
150+
def __call__(self):
151+
return real_fail_check()
152+
153+
def __await__(self):
154+
return real_fail_check().__await__()
155+
156+
checks = {
157+
"c1": AsyncPassCheck(),
158+
"c2": AsyncFailCheck(),
159+
}
160+
# Attach HealthCheckMiddleware with a password
161+
app.add_middleware(HealthCheckMiddleware, checks=checks, password="secret")
162+
163+
@app.get("/hello")
164+
async def hello():
165+
return {"msg": "world"}
166+
167+
return app
168+
169+
170+
def test_dispatch_healthz_no_password(app_with_middleware):
171+
client = TestClient(app_with_middleware)
172+
# c2 returns False, so overall summary.status is False → 503
173+
response = client.get("/healthz")
174+
assert response.status_code == 503
175+
assert response.text == "Service Unavailable"
176+
177+
178+
def test_dispatch_healthz_with_password_json(app_with_middleware):
179+
client = TestClient(app_with_middleware)
180+
response = client.get("/healthz?code=secret")
181+
assert response.status_code == 503 # still 503 because c2 failed
182+
183+
json_body = response.json()
184+
# The JSON‐serialized HealthCheckSummary should contain keys "status" and "results"
185+
assert "status" in json_body
186+
assert "results" in json_body
187+
assert json_body["status"] is False
188+
# Both checks must appear
189+
assert "c1" in json_body["results"]
190+
assert "c2" in json_body["results"]
191+
192+
193+
def test_dispatch_non_healthz_calls_next(app_with_middleware):
194+
client = TestClient(app_with_middleware)
195+
response = client.get("/hello")
196+
assert response.status_code == 200
197+
assert response.json() == {"msg": "world"}

0 commit comments

Comments
 (0)