import io import json import logging import logging.handlers import sys from src.log_fields import EXIT_CODE, OPERATION, PATH, REPOSITORY from src.logging_config import JsonFormatter, PlainFormatter, setup_logging from src.models import LoggingConfig, LoggingOutputConfig def test_json_formatter_keeps_context_fields(): record = logging.LogRecord( name="gitea_mirror.git", level=logging.ERROR, pathname=__file__, lineno=10, msg="git command failed", args=(), exc_info=None, ) setattr(record, OPERATION, "pull") setattr(record, REPOSITORY, "alex/repo") setattr(record, PATH, "/backup/alex/repo") setattr(record, EXIT_CODE, 128) data = json.loads(JsonFormatter().format(record)) assert data["level"] == "ERROR" assert data["logger"] == "gitea_mirror.git" assert data["message"] == "git command failed" assert data[OPERATION] == "pull" assert data[REPOSITORY] == "alex/repo" assert data[PATH] == "/backup/alex/repo" assert data[EXIT_CODE] == 128 assert "timestamp" in data def test_plain_formatter_appends_context_fields(): record = logging.LogRecord( name="gitea_mirror.git", level=logging.INFO, pathname=__file__, lineno=10, msg="repository cloned", args=(), exc_info=None, ) setattr(record, OPERATION, "clone") setattr(record, REPOSITORY, "alex/repo") message = PlainFormatter().format(record) assert message == ( f"INFO repository cloned {OPERATION}=clone {REPOSITORY}=alex/repo" ) def test_setup_logging_configures_console_and_rotating_file(tmp_path): log_path = tmp_path / "gitea-mirror.log" logger = logging.getLogger("test_setup_logging") setup_logging( LoggingConfig( level="DEBUG", outputs=[ LoggingOutputConfig( type="console", format="plain", stream="stdout", ), LoggingOutputConfig( type="console", format="json", stream="stderr", ), LoggingOutputConfig( type="file", path=str(log_path), format="json", max_size=1024, backup_count=2, ), ], ), logger=logger, ) assert logger.level == logging.DEBUG assert len(logger.handlers) == 3 assert logger.propagate is False stdout_handler, stderr_handler, file_handler = logger.handlers assert stdout_handler.stream is sys.stdout assert isinstance(stdout_handler.formatter, PlainFormatter) assert stderr_handler.stream is sys.stderr assert isinstance(stderr_handler.formatter, JsonFormatter) assert isinstance(file_handler, logging.handlers.RotatingFileHandler) assert isinstance(file_handler.formatter, JsonFormatter) assert file_handler.maxBytes == 1024 assert file_handler.backupCount == 2 def test_setup_logging_replaces_existing_handlers(): logger = logging.getLogger("test_setup_logging_replaces") old_stream = io.StringIO() logger.addHandler(logging.StreamHandler(old_stream)) setup_logging(LoggingConfig(), logger=logger) assert len(logger.handlers) == 1 assert isinstance(logger.handlers[0].formatter, PlainFormatter)