Option C Tools
Loading...
Searching...
No Matches
test_log_handle_encapsulation.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# tests/tests_linter/test_log_handle_encapsulation.py
4
5"""
6Purpose
7-------
8OI-513 regression coverage — ``LOG_HANDLE`` must not exist as a
9module-level global on :mod:`oct.linter.oct_lint`. All log state lives
10on :class:`LinterContext` so that concurrent linter invocations (MCP,
11parallel ``--jobs``, health dashboard) never share mutable state.
12
13Responsibilities
14----------------
15- Verify ``oct.linter.oct_lint`` exposes no ``LOG_HANDLE`` attribute.
16- Verify ``log()`` writes through ``ctx.log_handle`` only.
17- Verify two concurrent contexts never cross-pollute handles.
18
19Diagnostics
20-----------
21Domain: LINTER-TESTS
22Levels:
23 L2 — test lifecycle
24 L3 — assertion details
25 L4 — deep tracing
26
27Contracts
28---------
29- ``oct.linter.oct_lint`` exposes no ``LOG_HANDLE`` attribute.
30- :func:`log` writes through ``ctx.log_handle`` only; silent when None.
31- Two contexts in flight never cross-pollute handles.
32"""
33
34from __future__ import annotations
35
36import io
37import threading
38from pathlib import Path
39
40from oct.linter import oct_lint
41from oct.linter.oct_lint import LinterContext, log
42
43
44def _make_ctx(tmp_path: Path, handle) -> LinterContext:
45 return LinterContext(
46 project_root=tmp_path,
47 project_name="demo",
48 diagnostics_dir=tmp_path / "oc_diagnostics",
49 tests_dir=tmp_path / "tests",
50 docs_dir=tmp_path / "docs",
51 log_handle=handle,
52 log_lock=threading.Lock(),
53 )
54
55
57 """OI-513: the deprecated module global is gone."""
58 assert not hasattr(oct_lint, "LOG_HANDLE")
59
60
62 """OI-513: ``log()`` routes through ``ctx.log_handle``."""
63 buf = io.StringIO()
64 ctx = _make_ctx(tmp_path, buf)
65 log("hello", ctx)
66 assert buf.getvalue() == "hello\n"
67
68
70 """OI-513: ``log()`` is a no-op when ctx is None or handle is None."""
71 # No ctx at all — must not raise.
72 log("dropped", None)
73 # ctx with no handle — still silent.
74 ctx = _make_ctx(tmp_path, None)
75 log("also dropped", ctx) # no exception, nothing written.
76
77
79 """OI-513: parallel runs do not share a global handle."""
80 buf_a = io.StringIO()
81 buf_b = io.StringIO()
82 ctx_a = _make_ctx(tmp_path, buf_a)
83 ctx_b = _make_ctx(tmp_path, buf_b)
84 log("A1", ctx_a)
85 log("B1", ctx_b)
86 log("A2", ctx_a)
87 assert buf_a.getvalue() == "A1\nA2\n"
88 assert buf_b.getvalue() == "B1\n"