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