Option C oc_diagnostics
Loading...
Searching...
No Matches
test_instrumentation_middleware.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# tests/test_instrumentation_middleware.py
4
5"""
6Regression Tests — instrumentation_middleware
7==============================================
8
9Purpose
10-------
11Verify the public API of ``instrumentation_middleware``: the no-op contract
12when instrumentation is disabled, list/tuple container preservation, single-
13component instrumentation, and the double-wrapping prevention guard.
14
15Responsibilities
16----------------
17- Confirm ``apply_instrumentation()`` returns the component unchanged when
18 ``INSTRUMENTATION_ENABLED=False``.
19- Confirm list inputs are instrumented element-by-element with the list type
20 preserved.
21- Confirm tuple inputs are instrumented element-by-element with the tuple type
22 preserved.
23- Confirm a single component has its string ID rewritten.
24- Confirm a component whose ID is already a dict is not double-wrapped.
25
26Diagnostics
27-----------
28Domain: TEST
29Levels:
30 L2 — lifecycle
31 L3 — semantic details
32 L4 — deep tracing
33
34Contracts
35---------
36- All tests are skipped automatically when Dash is not installed.
37- Must not permanently modify oc_diagnostics global state.
38- Must restore all patched globals in finally blocks.
39- Must not start servers or require network access.
40- Must run deterministically across repeated invocations.
41"""
42
43import os
44import sys
45
46# ---------------------------------------------------------------------------
47# Ensure oc_diagnostics is importable when run from the project root or the
48# tests/ subdirectory.
49# ---------------------------------------------------------------------------
50_REPO_ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), ".."))
51if _REPO_ROOT not in sys.path:
52 sys.path.insert(0, _REPO_ROOT)
53
54import pytest
55
56# Skip the entire module when Dash is not installed
57dash_html = pytest.importorskip("dash.html", reason="Dash not installed")
58html = dash_html
59
61from oc_diagnostics.instrumentation_middleware import apply_instrumentation
62
63
64# ============================================================
65# NO-OP WHEN DISABLED
66# ============================================================
67
69 """apply_instrumentation() returns the component unchanged when disabled."""
70 old_enabled = _ud.INSTRUMENTATION_ENABLED
71 _ud.INSTRUMENTATION_ENABLED = False
72 try:
73 comp = html.Div("X", id="test-noop")
74 result = apply_instrumentation(comp)
75 assert result is comp, "Component must be returned unchanged when disabled"
76 assert result.id == "test-noop", (
77 f"ID must not change when disabled, got: {result.id!r}"
78 )
79 finally:
80 _ud.INSTRUMENTATION_ENABLED = old_enabled
81
82
83# ============================================================
84# LIST HANDLING
85# ============================================================
86
88 """apply_instrumentation() instruments each element of a list and preserves the type."""
89 old_enabled = _ud.INSTRUMENTATION_ENABLED
90 old_level = _ud.DEBUG_LEVELS.get("INSTRUMENTATION", 0)
91 _ud.INSTRUMENTATION_ENABLED = True
92 _ud.DEBUG_LEVELS["INSTRUMENTATION"] = 9
93 try:
94 comps = [html.Button("A", id="btn-a"), html.Div("B", id="div-b")]
95 result = apply_instrumentation(comps)
96 assert isinstance(result, list), f"Expected list, got {type(result)}"
97 assert result[0].id == {"event": "btn-a"}, (
98 f"Expected instrumented ID for list[0], got: {result[0].id!r}"
99 )
100 assert result[1].id == {"event": "div-b"}, (
101 f"Expected instrumented ID for list[1], got: {result[1].id!r}"
102 )
103 finally:
104 _ud.INSTRUMENTATION_ENABLED = old_enabled
105 _ud.DEBUG_LEVELS["INSTRUMENTATION"] = old_level
106
107
108# ============================================================
109# TUPLE HANDLING
110# ============================================================
111
113 """apply_instrumentation() instruments each element of a tuple and preserves the type."""
114 old_enabled = _ud.INSTRUMENTATION_ENABLED
115 old_level = _ud.DEBUG_LEVELS.get("INSTRUMENTATION", 0)
116 _ud.INSTRUMENTATION_ENABLED = True
117 _ud.DEBUG_LEVELS["INSTRUMENTATION"] = 9
118 try:
119 comps = (html.Button("X", id="btn-x"),)
120 result = apply_instrumentation(comps)
121 assert isinstance(result, tuple), f"Expected tuple, got {type(result)}"
122 assert result[0].id == {"event": "btn-x"}, (
123 f"Expected instrumented ID for tuple[0], got: {result[0].id!r}"
124 )
125 finally:
126 _ud.INSTRUMENTATION_ENABLED = old_enabled
127 _ud.DEBUG_LEVELS["INSTRUMENTATION"] = old_level
128
129
130# ============================================================
131# SINGLE COMPONENT
132# ============================================================
133
135 """apply_instrumentation() rewrites the ID of a single component."""
136 old_enabled = _ud.INSTRUMENTATION_ENABLED
137 old_level = _ud.DEBUG_LEVELS.get("INSTRUMENTATION", 0)
138 _ud.INSTRUMENTATION_ENABLED = True
139 _ud.DEBUG_LEVELS["INSTRUMENTATION"] = 9
140 try:
141 comp = html.Button("Solo", id="solo-btn")
142 result = apply_instrumentation(comp)
143 assert result.id == {"event": "solo-btn"}, (
144 f"Expected instrumented ID, got: {result.id!r}"
145 )
146 finally:
147 _ud.INSTRUMENTATION_ENABLED = old_enabled
148 _ud.DEBUG_LEVELS["INSTRUMENTATION"] = old_level
149
150
151# ============================================================
152# NO DOUBLE-WRAPPING
153# ============================================================
154
156 """apply_instrumentation() does not re-wrap a component whose ID is already a dict."""
157 old_enabled = _ud.INSTRUMENTATION_ENABLED
158 old_level = _ud.DEBUG_LEVELS.get("INSTRUMENTATION", 0)
159 _ud.INSTRUMENTATION_ENABLED = True
160 _ud.DEBUG_LEVELS["INSTRUMENTATION"] = 9
161 try:
162 already_wrapped_id = {"event": "already-wrapped"}
163 comp = html.Button("Wrapped", id=already_wrapped_id)
164 result = apply_instrumentation(comp)
165 assert result.id == already_wrapped_id, (
166 f"Dict ID must not be double-wrapped, got: {result.id!r}"
167 )
168 finally:
169 _ud.INSTRUMENTATION_ENABLED = old_enabled
170 _ud.DEBUG_LEVELS["INSTRUMENTATION"] = old_level