Files
meshcore-stats/tests/unit/test_log.py
Jorijn Schrijvershof ca13e31aae test: stabilize suite and broaden integration coverage (#32)
* tests: cache integration/report fixtures to speed up tests

* fix: speed up yearly aggregation and refresh timings report

* chore: remove the report

* fix: unrecognized named-value: 'runner'. Located at position 1 within expression: runner.temp

* fix: ruff linting error

* test: strengthen assertions and stabilize tests

* test(integration): expand rendered chart metrics
2026-01-08 21:20:34 +01:00

182 lines
6.3 KiB
Python

"""Tests for logging utilities."""
from datetime import datetime
from unittest.mock import MagicMock, patch
import pytest
from meshmon import log
class TestTimestamp:
"""Test the _ts() timestamp function."""
def test_returns_string(self):
"""_ts() should return a string."""
result = log._ts()
assert isinstance(result, str)
def test_format_is_correct(self):
"""_ts() should return timestamp in expected format."""
result = log._ts()
# Format: YYYY-MM-DD HH:MM:SS
try:
datetime.strptime(result, "%Y-%m-%d %H:%M:%S")
except ValueError:
pytest.fail(f"Timestamp '{result}' doesn't match expected format")
@patch("meshmon.log.datetime")
def test_uses_current_time(self, mock_datetime):
"""_ts() should use current time."""
mock_now = MagicMock()
mock_now.strftime.return_value = "2024-01-15 10:30:45"
mock_datetime.now.return_value = mock_now
result = log._ts()
mock_datetime.now.assert_called_once()
mock_now.strftime.assert_called_once_with("%Y-%m-%d %H:%M:%S")
assert result == "2024-01-15 10:30:45"
@pytest.fixture
def fixed_ts(monkeypatch):
"""Freeze log timestamp for deterministic output assertions."""
timestamp = "2024-01-15 10:30:45"
monkeypatch.setattr(log, "_ts", lambda: timestamp)
return timestamp
class TestInfoLog:
"""Test the info() function."""
def test_prints_to_stdout(self, capsys, fixed_ts):
"""info() should print to stdout."""
log.info("test message")
captured = capsys.readouterr()
assert captured.out == f"[{fixed_ts}] test message\n"
assert captured.err == ""
def test_includes_timestamp(self, capsys, fixed_ts):
"""info() output should include timestamp."""
log.info("test")
captured = capsys.readouterr()
# Should have format: [YYYY-MM-DD HH:MM:SS] message
assert captured.out.startswith(f"[{fixed_ts}]")
assert captured.out.endswith("test\n")
def test_message_appears_after_timestamp(self, capsys, fixed_ts):
"""Message should appear after the timestamp."""
log.info("unique_test_message")
captured = capsys.readouterr()
assert captured.out == f"[{fixed_ts}] unique_test_message\n"
# Message should be after the closing bracket
bracket_pos = captured.out.index("]")
message_pos = captured.out.index("unique_test_message")
assert message_pos > bracket_pos
class TestDebugLog:
"""Test the debug() function."""
def test_no_output_when_debug_disabled(self, capsys, monkeypatch, fixed_ts):
"""debug() should not print when MESH_DEBUG is not set."""
# Clean env should already have MESH_DEBUG unset
import meshmon.env
meshmon.env._config = None
log.debug("debug message")
captured = capsys.readouterr()
assert captured.out == ""
assert captured.err == ""
def test_prints_when_debug_enabled(self, capsys, monkeypatch, fixed_ts):
"""debug() should print when MESH_DEBUG=1."""
monkeypatch.setenv("MESH_DEBUG", "1")
import meshmon.env
meshmon.env._config = None
log.debug("debug message")
captured = capsys.readouterr()
assert captured.out == f"[{fixed_ts}] DEBUG: debug message\n"
def test_debug_prefix(self, capsys, monkeypatch, fixed_ts):
"""debug() output should include DEBUG: prefix."""
monkeypatch.setenv("MESH_DEBUG", "1")
import meshmon.env
meshmon.env._config = None
log.debug("test")
captured = capsys.readouterr()
assert captured.out == f"[{fixed_ts}] DEBUG: test\n"
class TestErrorLog:
"""Test the error() function."""
def test_prints_to_stderr(self, capsys, fixed_ts):
"""error() should print to stderr."""
log.error("error message")
captured = capsys.readouterr()
assert captured.out == ""
assert captured.err == f"[{fixed_ts}] ERROR: error message\n"
def test_includes_error_prefix(self, capsys, fixed_ts):
"""error() output should include ERROR: prefix."""
log.error("test error")
captured = capsys.readouterr()
assert captured.err == f"[{fixed_ts}] ERROR: test error\n"
def test_includes_timestamp(self, capsys, fixed_ts):
"""error() output should include timestamp."""
log.error("test")
captured = capsys.readouterr()
assert captured.err == f"[{fixed_ts}] ERROR: test\n"
class TestWarnLog:
"""Test the warn() function."""
def test_prints_to_stderr(self, capsys, fixed_ts):
"""warn() should print to stderr."""
log.warn("warning message")
captured = capsys.readouterr()
assert captured.out == ""
assert captured.err == f"[{fixed_ts}] WARN: warning message\n"
def test_includes_warn_prefix(self, capsys, fixed_ts):
"""warn() output should include WARN: prefix."""
log.warn("test warning")
captured = capsys.readouterr()
assert captured.err == f"[{fixed_ts}] WARN: test warning\n"
def test_includes_timestamp(self, capsys, fixed_ts):
"""warn() output should include timestamp."""
log.warn("test")
captured = capsys.readouterr()
assert captured.err == f"[{fixed_ts}] WARN: test\n"
class TestLogMessageFormatting:
"""Test message formatting across all log functions."""
def test_info_handles_special_characters(self, capsys, fixed_ts):
"""info() should handle special characters in messages."""
log.info("Message with 'quotes' and \"double quotes\"")
captured = capsys.readouterr()
assert captured.out == (
f"[{fixed_ts}] Message with 'quotes' and \"double quotes\"\n"
)
def test_error_handles_newlines(self, capsys, fixed_ts):
"""error() should handle newlines in messages."""
log.error("Line1\nLine2")
captured = capsys.readouterr()
assert captured.err == f"[{fixed_ts}] ERROR: Line1\nLine2\n"
def test_warn_handles_unicode(self, capsys, fixed_ts):
"""warn() should handle unicode characters."""
log.warn("Warning: \u26a0 Alert!")
captured = capsys.readouterr()
assert captured.err == f"[{fixed_ts}] WARN: Warning: \u26a0 Alert!\n"