Files
ss-tools/backend/tests/core/test_cot_logger.py
busya a39a76c87f feat(agent-centric-logging): consolidate CoT infra in shared, close REASON→REFLECT chains
- 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)
2026-07-12 19:30:57 +03:00

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