- shared/cot_logger.py is SSOT; backend/cot_logger.py deleted
- elapsed_ms timing in all REFLECT markers
- Frontend: REASON→REFLECT/EXPLORE in all fetch/post/delete/requestApi
- Dynamic src: route.GET.api.plugins instead of hardcoded api.request_handler
- trace_id generated immediately (no 'no-trace'), X-Trace-ID in both directions
- Global error handlers (window error + unhandledrejection + error.svelte)
- Fixed duplicate logging (shared/logger.py double StreamHandler)
- propagate=False in configure_logger (was in ConfigManager = duplicated startup logs)
- belief_scope: 'Coherence OK' → '{anchor}: completed' + elapsed_ms
- Fixed 28 pre-existing test failures (scheduler sig, DB columns, DRAFT validation, etc)
170 lines
6.4 KiB
Python
170 lines
6.4 KiB
Python
# #region Test.CotLogger [C:3] [TYPE Module] [SEMANTICS test,logging,cot,trace,span,json]
|
|
# @BRIEF Tests for shared/cot_logger.py — trace_id, span, log markers, timing.
|
|
# @RELATION BINDS_TO -> [CotLoggerModule]
|
|
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
|
|
import logging
|
|
import re
|
|
from unittest.mock import patch, MagicMock, call
|
|
import pytest
|
|
|
|
|
|
class TestSeedTraceId:
|
|
"""seed_trace_id — generates UUID and sets ContextVar."""
|
|
|
|
def test_returns_uuid_string(self):
|
|
from ss_tools.shared.cot_logger import seed_trace_id
|
|
tid = seed_trace_id()
|
|
assert isinstance(tid, str)
|
|
assert len(tid) == 32 # UUID4 hex format (no hyphens)
|
|
|
|
def test_sets_trace_id(self):
|
|
from ss_tools.shared.cot_logger import seed_trace_id, get_trace_id
|
|
tid = seed_trace_id()
|
|
assert get_trace_id() == tid
|
|
|
|
|
|
class TestSetTraceId:
|
|
"""set_trace_id — sets explicit trace ID."""
|
|
|
|
def test_sets_provided_id(self):
|
|
from ss_tools.shared.cot_logger import set_trace_id, get_trace_id
|
|
set_trace_id("custom-trace-123")
|
|
assert get_trace_id() == "custom-trace-123"
|
|
|
|
|
|
class TestGetTraceId:
|
|
"""get_trace_id — returns current trace ID."""
|
|
|
|
def test_default_empty(self):
|
|
from ss_tools.shared.cot_logger import get_trace_id
|
|
# After seed, get_trace_id returns non-empty
|
|
# In a fresh test the ContextVar may be empty
|
|
pass # ContextVar has default ""
|
|
|
|
def test_after_seed_returns_value(self):
|
|
from ss_tools.shared.cot_logger import seed_trace_id, get_trace_id
|
|
tid = seed_trace_id()
|
|
assert get_trace_id() == tid
|
|
|
|
|
|
class TestPushPopSpan:
|
|
"""push_span and pop_span — span stack operations."""
|
|
|
|
def test_push_returns_previous(self):
|
|
from ss_tools.shared.cot_logger import push_span, pop_span
|
|
prev1 = push_span("span-1")
|
|
# Initially empty
|
|
prev2 = push_span("span-2")
|
|
assert prev2 == "span-1"
|
|
pop_span(prev2)
|
|
# After pop, current span is back to "span-1"
|
|
|
|
def test_push_pop_restores(self):
|
|
from ss_tools.shared.cot_logger import push_span
|
|
prev = push_span("outer")
|
|
inner_prev = push_span("inner")
|
|
assert inner_prev == "outer"
|
|
# pop restores outer
|
|
from ss_tools.shared.cot_logger import pop_span
|
|
pop_span(inner_prev)
|
|
|
|
|
|
class TestLog:
|
|
"""log — structured JSON logging with markers."""
|
|
|
|
def test_reason_marker_info_level(self):
|
|
from ss_tools.shared.cot_logger import log
|
|
with patch("ss_tools.shared.cot_logger.cot_logger.info") as mock_info:
|
|
log("TestModule", "REASON", "Testing reason")
|
|
mock_info.assert_called_once()
|
|
args, kwargs = mock_info.call_args
|
|
assert "Testing reason" in args[0]
|
|
extra = kwargs.get("extra", {})
|
|
assert extra["marker"] == "REASON"
|
|
assert extra["src"] == "TestModule"
|
|
assert extra["intent"] == "Testing reason"
|
|
|
|
def test_reflect_marker_info_level(self):
|
|
from ss_tools.shared.cot_logger import log
|
|
with patch("ss_tools.shared.cot_logger.cot_logger.info") as mock_info:
|
|
log("Test", "REFLECT", "Reflecting")
|
|
mock_info.assert_called_once()
|
|
|
|
def test_explore_marker_warning_level(self):
|
|
from ss_tools.shared.cot_logger import log
|
|
with patch("ss_tools.shared.cot_logger.cot_logger.warning") as mock_warn:
|
|
log("Test", "EXPLORE", "Exploring alternatives", error="Assumption violated")
|
|
mock_warn.assert_called_once()
|
|
|
|
def test_error_level_explicit(self):
|
|
from ss_tools.shared.cot_logger import log
|
|
with patch("ss_tools.shared.cot_logger.cot_logger.error") as mock_err:
|
|
log("Test", "REASON", "Error test", level="ERROR")
|
|
mock_err.assert_called_once()
|
|
|
|
def test_debug_level_explicit(self):
|
|
from ss_tools.shared.cot_logger import log
|
|
with patch("ss_tools.shared.cot_logger.cot_logger.debug") as mock_debug:
|
|
log("Test", "REASON", "Debug test", level="DEBUG")
|
|
mock_debug.assert_called_once()
|
|
|
|
def test_payload_included(self):
|
|
from ss_tools.shared.cot_logger import log
|
|
with patch("ss_tools.shared.cot_logger.cot_logger.info") as mock_info:
|
|
log("Test", "REASON", "With payload", payload={"key": "value"})
|
|
extra = mock_info.call_args[1]["extra"]
|
|
assert extra["payload"] == {"key": "value"}
|
|
|
|
def test_error_included(self):
|
|
from ss_tools.shared.cot_logger import log
|
|
with patch("ss_tools.shared.cot_logger.cot_logger.warning") as mock_warn:
|
|
log("Test", "EXPLORE", "Error case", error="Something broke")
|
|
extra = mock_warn.call_args[1]["extra"]
|
|
assert extra["error"] == "Something broke"
|
|
|
|
def test_payload_not_included_when_none(self):
|
|
from ss_tools.shared.cot_logger import log
|
|
with patch("ss_tools.shared.cot_logger.cot_logger.info") as mock_info:
|
|
log("Test", "REASON", "No payload")
|
|
extra = mock_info.call_args[1]["extra"]
|
|
assert "payload" not in extra
|
|
|
|
def test_error_not_included_when_none(self):
|
|
from ss_tools.shared.cot_logger import log
|
|
with patch("ss_tools.shared.cot_logger.cot_logger.info") as mock_info:
|
|
log("Test", "REASON", "No error")
|
|
extra = mock_info.call_args[1]["extra"]
|
|
assert "error" not in extra
|
|
|
|
def test_warning_level_inferred_from_explore(self):
|
|
from ss_tools.shared.cot_logger import log
|
|
with patch("ss_tools.shared.cot_logger.cot_logger.warning") as mock_warn:
|
|
log("Test", "EXPLORE", "Auto warning")
|
|
mock_warn.assert_called_once()
|
|
|
|
|
|
class TestTraceIdPropagation:
|
|
"""Trace ID propagation across async contexts."""
|
|
|
|
def test_seed_then_get(self):
|
|
from ss_tools.shared.cot_logger import seed_trace_id, get_trace_id
|
|
tid = seed_trace_id()
|
|
assert get_trace_id() == tid
|
|
|
|
def test_set_then_get(self):
|
|
from ss_tools.shared.cot_logger import set_trace_id, get_trace_id
|
|
set_trace_id("abc-123")
|
|
assert get_trace_id() == "abc-123"
|
|
|
|
def test_set_overrides_seed(self):
|
|
from ss_tools.shared.cot_logger import seed_trace_id, set_trace_id, get_trace_id
|
|
seed_trace_id()
|
|
set_trace_id("override")
|
|
assert get_trace_id() == "override"
|
|
# #endregion Test.CotLogger
|