Option C oc_diagnostics
Loading...
Searching...
No Matches
test_logging_handler.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# tests/test_logging_handler.py
4
5"""
6Regression Tests — logging_handler
7====================================
8
9Purpose
10-------
11Verify that ``OcDiagnosticsHandler`` correctly bridges Python's ``logging``
12module into ``oc_diagnostics._dbg()``, including level mapping, custom domain
13routing, and the non-raising contract on internal exceptions.
14
15Responsibilities
16----------------
17- Confirm that ``OcDiagnosticsHandler.emit()`` routes a LogRecord without raising.
18- Confirm the default level map: DEBUG→L4, INFO→L2, WARNING/ERROR/CRITICAL→L1.
19- Confirm that the handler's ``domain`` argument is forwarded to ``_dbg()``.
20- Confirm that internal exceptions are swallowed (delegated to ``handleError``).
21
22Diagnostics
23-----------
24Domain: TEST
25Levels:
26 L2 — lifecycle
27 L3 — semantic details
28 L4 — deep tracing
29
30Contracts
31---------
32- Must not permanently modify oc_diagnostics global state.
33- Must not start servers or require network access.
34- Must run deterministically across repeated invocations.
35"""
36
37import logging
38import os
39import sys
40import unittest.mock as mock
41
42# ---------------------------------------------------------------------------
43# Ensure oc_diagnostics is importable when run from the project root or the
44# tests/ subdirectory.
45# ---------------------------------------------------------------------------
46_REPO_ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), ".."))
47if _REPO_ROOT not in sys.path:
48 sys.path.insert(0, _REPO_ROOT)
49
51from oc_diagnostics.logging_handler import OcDiagnosticsHandler
52
53
54def _make_record(level: int, msg: str = "test message") -> logging.LogRecord:
55 """Build a minimal LogRecord for testing."""
56 return logging.LogRecord(
57 name="test.logger",
58 level=level,
59 pathname="test_file.py",
60 lineno=1,
61 msg=msg,
62 args=(),
63 exc_info=None,
64 )
65
66
67# ============================================================
68# BASIC EMIT
69# ============================================================
70
72 """OcDiagnosticsHandler.emit() routes an INFO LogRecord through _dbg() without raising."""
73 handler = OcDiagnosticsHandler(domain="GENERAL")
74 record = _make_record(logging.INFO, "test handler message")
75 old_silent = _ud.SILENT_MODE
76 _ud.SILENT_MODE = True
77 try:
78 handler.emit(record) # must not raise
79 finally:
80 _ud.SILENT_MODE = old_silent
81
82
83# ============================================================
84# LEVEL MAPPING
85# ============================================================
86
88 """Default level map routes Python levels to oc_diagnostics levels correctly.
89
90 DEBUG→L4, INFO→L2, WARNING→L1, ERROR→L1, CRITICAL→L1.
91 """
92 handler = OcDiagnosticsHandler(domain="_LEVEL_MAP_TEST_")
93 expected = [
94 (logging.DEBUG, 4),
95 (logging.INFO, 2),
96 (logging.WARNING, 1),
97 (logging.ERROR, 1),
98 (logging.CRITICAL, 1),
99 ]
100
101 for py_level, expected_diag_level in expected:
102 with mock.patch("oc_diagnostics.logging_handler._dbg") as mock_dbg:
103 record = _make_record(py_level)
104 handler.emit(record)
105 assert mock_dbg.called, (
106 f"_dbg was not called for Python level {py_level}"
107 )
108 _, kwargs = mock_dbg.call_args
109 actual_level = kwargs.get("level")
110 assert actual_level == expected_diag_level, (
111 f"Python level {py_level}: expected oc_diagnostics L{expected_diag_level}, "
112 f"got L{actual_level}"
113 )
114
115
116# ============================================================
117# CUSTOM DOMAIN
118# ============================================================
119
121 """Handler's domain argument is forwarded as the first positional arg to _dbg()."""
122 handler = OcDiagnosticsHandler(domain="MY_CUSTOM_DOMAIN")
123 record = _make_record(logging.INFO, "domain check")
124
125 with mock.patch("oc_diagnostics.logging_handler._dbg") as mock_dbg:
126 handler.emit(record)
127 assert mock_dbg.called, "_dbg was not called"
128 args, _ = mock_dbg.call_args
129 assert args[0] == "MY_CUSTOM_DOMAIN", (
130 f"Expected domain 'MY_CUSTOM_DOMAIN', got {args[0]!r}"
131 )
132
133
134# ============================================================
135# NON-RAISING CONTRACT
136# ============================================================
137
139 """OcDiagnosticsHandler.emit() must not propagate exceptions from _dbg()."""
140 handler = OcDiagnosticsHandler(domain="GENERAL")
141 record = _make_record(logging.INFO, "exception test")
142
143 # Patch handleError to suppress the default stderr output during the test
144 with mock.patch.object(handler, "handleError"):
145 with mock.patch(
146 "oc_diagnostics.logging_handler._dbg",
147 side_effect=RuntimeError("deliberate internal failure"),
148 ):
149 handler.emit(record) # must not raise — exception handled internally
logging.LogRecord _make_record(int level, str msg="test message")