Coverage for src / ocarina / opinionated / plugins / reports / results_to_json.py: 0.00%

35 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-06-04 15:56 +0200

1"""Generate JSON test reports. 

2 

3Serializes test results into structured, machine-readable JSON files. 

4Designed for integration with CI/CD pipelines, monitoring tools, 

5or automated reporting systems. 

6 

7Output format: 

8 { 

9 "Campaign": { 

10 "Suite": { 

11 "test_case": [ 

12 {"status": "success" | "fail", "error"?: "..."}, 

13 counter, 

14 metadata 

15 ] 

16 } 

17 } 

18 } 

19 

20Example: 

21 >>> results_dir = Path(tempfile.mkdtemp(prefix=".json_results_", dir=Path.cwd())) 

22 >>> generate_json_results(results, results_dir=results_dir, logger=logger) 

23 # Creates: results_dir/abcd1234.json 

24 

25""" 

26 

27import json 

28import uuid 

29from typing import TYPE_CHECKING, Any 

30 

31from ocarina.railway.result import is_fail, is_ok 

32 

33if TYPE_CHECKING: 

34 from pathlib import Path 

35 

36 from ocarina.custom_types.oc_test_layers import TestCycleResults 

37 from ocarina.ports.ilogger import ILogger 

38 

39 

40def _result_to_serializable(v: Any) -> dict[str, str]: # noqa: ANN401 

41 if is_ok(v): 

42 return {"status": "success"} 

43 if is_fail(v): 

44 return {"status": "fail", "error": str(v.error)} 

45 

46 msg = f"Expected Ok or Fail instances, but got: {type(v)}" 

47 raise TypeError(msg) 

48 

49 

50def generate_json_results( 

51 *, 

52 results: TestCycleResults, 

53 output_dir: Path, 

54 logger: ILogger, 

55) -> None: 

56 """Write test results to a JSON file in results_dir. 

57 

58 Args: 

59 results: Hierarchical test results structure. 

60 output_dir: Directory where the JSON file will be written. 

61 logger: Logger for progress and error reporting. 

62 

63 Raises: 

64 RuntimeError: If a unique filename cannot be generated. 

65 

66 """ 

67 output_dir.mkdir(parents=True, exist_ok=True) 

68 

69 max_stem_length = 8 

70 max_attempts = 500 

71 for _ in range(max_attempts): 

72 filename = f"{uuid.uuid4().hex[:max_stem_length]}.json" 

73 file_path = output_dir / filename 

74 

75 if not file_path.exists(): 

76 break 

77 else: 

78 msg = f"Can't generate unique JSON filename in: {output_dir}." 

79 raise RuntimeError(msg) 

80 

81 payload = { 

82 campaign_name: { 

83 suite_name: tests for suite_name, tests in suites.items() if tests 

84 } 

85 for campaign_name, suites in results.items() 

86 if any(tests for tests in suites.values()) 

87 } 

88 

89 if not payload: 

90 logger.warning("No test results to write.") 

91 return 

92 

93 try: 

94 with file_path.open("w", encoding="utf-8") as f: 

95 json.dump(payload, f, indent=2, default=_result_to_serializable) 

96 except Exception as exc: 

97 msg = f"Can't write JSON file: {file_path}" 

98 logger.exception(msg, exc=exc) 

99 return 

100 

101 msg = f"Plugin execution done. Output: {file_path}" 

102 logger.info(msg)