Skip to content

Commit a76f1fc

Browse files
committed
Added standalone healthcheck runner with JSON output support
1 parent 16629c0 commit a76f1fc

1 file changed

Lines changed: 96 additions & 0 deletions

File tree

lf_toolkit/io/healthcheck.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import os
2+
import re
3+
import sys
4+
import time
5+
import unittest
6+
from typing import Any, List, TypedDict
7+
8+
from typing_extensions import NotRequired
9+
10+
11+
class JsonTestResult(TypedDict):
12+
name: str
13+
time: NotRequired[int]
14+
15+
16+
JsonTestResults = List[JsonTestResult]
17+
18+
19+
class HealthcheckJsonTestResult(TypedDict):
20+
tests_passed: bool
21+
successes: JsonTestResults
22+
failures: JsonTestResults
23+
errors: JsonTestResults
24+
25+
26+
class HealthcheckResult(unittest.TextTestResult):
27+
28+
def __init__(self, *args, **kwargs) -> None:
29+
super().__init__(*args, **kwargs)
30+
self.__path_re = re.compile(r"^[\.\/\w]+\.(\w+\.\w+)$")
31+
self.__successes_json: JsonTestResults = []
32+
self.__failures_json: JsonTestResults = []
33+
self.__errors_json: JsonTestResults = []
34+
35+
def _get_name(self, path: str) -> str:
36+
match = self.__path_re.match(path)
37+
return match.group(1) if match else "Unknown"
38+
39+
def startTest(self, test: unittest.TestCase) -> None:
40+
self._start_time = time.time()
41+
super().startTest(test)
42+
43+
def addSuccess(self, test: unittest.TestCase) -> None:
44+
elapsed = time.time() - self._start_time
45+
self.__successes_json.append(
46+
JsonTestResult(name=self._get_name(test.id()), time=round(1e6 * elapsed))
47+
)
48+
super().addSuccess(test)
49+
50+
def addFailure(self, test: unittest.TestCase, err: Any) -> None:
51+
self.__failures_json.append(JsonTestResult(name=self._get_name(test.id())))
52+
super().addFailure(test, err)
53+
54+
def addError(self, test: unittest.TestCase, err: Any) -> None:
55+
self.__errors_json.append(JsonTestResult(name=self._get_name(test.id())))
56+
super().addError(test, err)
57+
58+
def get_successes_json(self) -> JsonTestResults:
59+
return self.__successes_json
60+
61+
def get_failures_json(self) -> JsonTestResults:
62+
return self.__failures_json
63+
64+
def get_errors_json(self) -> JsonTestResults:
65+
return self.__errors_json
66+
67+
68+
class HealthcheckRunner(unittest.TextTestRunner):
69+
70+
def __init__(self, *args, **kwargs) -> None:
71+
super().__init__(resultclass=HealthcheckResult, *args, **kwargs)
72+
73+
def run(self, test) -> HealthcheckJsonTestResult:
74+
result: HealthcheckResult = super().run(test) # type: ignore
75+
return HealthcheckJsonTestResult(
76+
tests_passed=result.wasSuccessful(),
77+
successes=result.get_successes_json(),
78+
failures=result.get_failures_json(),
79+
errors=result.get_errors_json(),
80+
)
81+
82+
83+
def run_healthcheck() -> HealthcheckJsonTestResult:
84+
no_stream = open(os.devnull, "w")
85+
sys.stderr = no_stream
86+
87+
try:
88+
loader = unittest.TestLoader()
89+
suite = loader.discover(start_dir=".", pattern="*test*.py")
90+
runner = HealthcheckRunner(verbosity=0)
91+
result = runner.run(suite)
92+
finally:
93+
sys.stderr = sys.__stderr__
94+
no_stream.close()
95+
96+
return result

0 commit comments

Comments
 (0)