Files
Prompt-optimizer/tests/unit/test_logging.py
FullStackDev a5bf2ad59c feat: v0.2.0 sprint — ground truth eval, crossover/mutation, checkpointing, similarity guards, dataset loader, CLI commands, extended test coverage
Aggregates all v0.2.0 sprint work (GARAA-30 through GARAA-40) and fixes
2 integration tests that broke when the codebase went async (DSPyLLMAdapter
and full pipeline tests now properly await coroutines).

277 tests pass (260 unit + 17 integration).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-29 19:13:50 +00:00

190 lines
6.7 KiB
Python

"""Unit tests for structured logging configuration."""
from __future__ import annotations
import json
import logging
from pathlib import Path
from prometheus.cli.logging_setup import configure_logging, get_logger
class TestConfigureLogging:
def _count_handlers(self, name: str = "prometheus") -> int:
return len(logging.getLogger(name).handlers)
def test_default_creates_console_handler(self) -> None:
configure_logging(level=logging.INFO)
prom = logging.getLogger("prometheus")
assert len(prom.handlers) == 1
assert isinstance(prom.handlers[0], logging.StreamHandler)
prom.handlers.clear()
def test_json_format_produces_valid_json(self, capsys) -> None:
configure_logging(level=logging.INFO, log_format="json")
logger = get_logger("test_json")
logger.info("hello", extra={"structured": {"key": "value"}})
captured = capsys.readouterr()
# Output goes to stderr
line = captured.err.strip().split("\n")[-1]
data = json.loads(line)
assert data["message"] == "hello"
assert data["structured"]["key"] == "value"
assert data["level"] == "INFO"
assert "timestamp" in data
logging.getLogger("prometheus").handlers.clear()
def test_text_format_includes_structured_extras(self, capsys) -> None:
configure_logging(level=logging.INFO, log_format="text")
logger = get_logger("test_text")
logger.info("msg", extra={"structured": {"foo": "bar"}})
captured = capsys.readouterr()
assert "foo=bar" in captured.err
logging.getLogger("prometheus").handlers.clear()
def test_debug_level_shows_debug_messages(self, capsys) -> None:
configure_logging(level=logging.DEBUG)
logger = get_logger("test_debug")
logger.debug("debug msg")
captured = capsys.readouterr()
assert "debug msg" in captured.err
logging.getLogger("prometheus").handlers.clear()
def test_warning_level_hides_debug_messages(self, capsys) -> None:
configure_logging(level=logging.WARNING)
logger = get_logger("test_warn")
logger.debug("should not appear")
logger.info("also hidden")
captured = capsys.readouterr()
assert "should not appear" not in captured.err
assert "also hidden" not in captured.err
logging.getLogger("prometheus").handlers.clear()
def test_file_handler_writes_to_file(self, tmp_path: Path) -> None:
log_file = tmp_path / "test.log"
configure_logging(level=logging.INFO, log_file=str(log_file))
logger = get_logger("test_file")
logger.info("file message")
prom = logging.getLogger("prometheus")
# Flush handlers
for h in prom.handlers:
h.flush()
prom.handlers.clear()
content = log_file.read_text()
assert "file message" in content
def test_json_file_output(self, tmp_path: Path) -> None:
log_file = tmp_path / "test.json.log"
configure_logging(level=logging.INFO, log_format="json", log_file=str(log_file))
logger = get_logger("test_json_file")
logger.info("json file msg", extra={"structured": {"x": 1}})
prom = logging.getLogger("prometheus")
for h in prom.handlers:
h.flush()
prom.handlers.clear()
content = log_file.read_text().strip()
data = json.loads(content)
assert data["message"] == "json file msg"
assert data["structured"]["x"] == 1
def test_reconfigure_clears_old_handlers(self) -> None:
configure_logging(level=logging.INFO)
configure_logging(level=logging.DEBUG)
prom = logging.getLogger("prometheus")
assert len(prom.handlers) == 1
prom.handlers.clear()
def test_propagate_false_prevents_duplicate_output(self, capsys) -> None:
configure_logging(level=logging.INFO)
prom = logging.getLogger("prometheus")
assert prom.propagate is False
prom.handlers.clear()
class TestGetLogger:
def test_returns_child_of_prometheus(self) -> None:
logger = get_logger("mymodule")
assert logger.name == "prometheus.mymodule"
def test_inherits_level_from_parent(self) -> None:
configure_logging(level=logging.DEBUG)
logger = get_logger("child")
assert logger.getEffectiveLevel() <= logging.DEBUG
logging.getLogger("prometheus").handlers.clear()
class TestJsonFormatter:
def test_exception_included(self, capsys) -> None:
configure_logging(level=logging.ERROR, log_format="json")
logger = get_logger("test_exc")
try:
raise ValueError("boom")
except ValueError:
logger.error("failed", exc_info=True)
captured = capsys.readouterr()
line = captured.err.strip().split("\n")[-1]
data = json.loads(line)
assert "ValueError: boom" in data["exception"]
logging.getLogger("prometheus").handlers.clear()
class TestLoggingCLIIntegration:
"""Tests for CLI flags that configure logging."""
def test_verbose_flag_enables_info(self, tmp_path: Path) -> None:
"""Simulate what -v does — configure_logging at INFO level."""
configure_logging(level=logging.INFO)
logger = get_logger("evolution")
logger.info("test message")
prom = logging.getLogger("prometheus")
assert len(prom.handlers) == 1
prom.handlers.clear()
def test_debug_flag_enables_debug(self) -> None:
"""Simulate what --debug does — configure_logging at DEBUG level."""
configure_logging(level=logging.DEBUG)
logger = get_logger("evolution")
logger.debug("debug message")
prom = logging.getLogger("prometheus")
assert prom.level == logging.DEBUG
prom.handlers.clear()
def test_log_format_invalid_rejected(self) -> None:
"""Invalid log_format should be caught by OptimizationConfig validator."""
from pydantic import ValidationError
from prometheus.application.dto import OptimizationConfig
import pytest
with pytest.raises(ValidationError, match="log_format must be one of"):
OptimizationConfig(
seed_prompt="a",
task_description="b",
log_format="xml",
)
def test_log_format_text_and_json_accepted(self) -> None:
"""Both text and json log_format values should be valid."""
from prometheus.application.dto import OptimizationConfig
for fmt in ("text", "json"):
config = OptimizationConfig(
seed_prompt="a", task_description="b", log_format=fmt,
)
assert config.log_format == fmt