| 1 | import json |
| 2 | import logging |
| 3 | |
| 4 | import pytest |
| 5 | from pydantic import ValidationError |
| 6 | |
| 7 | from app.settings import Settings, _StructuredLogFormatter, configure_application_logging |
| 8 | |
| 9 | |
| 10 | def test_research_log_level_defaults_to_warning() -> None: |
| 11 | assert Settings().research_log_level == "WARNING" |
| 12 | |
| 13 | |
| 14 | def test_info_log_level_configures_app_logger_without_adding_handlers() -> None: |
| 15 | root = logging.getLogger() |
| 16 | app_logger = logging.getLogger("app") |
| 17 | original_root, original_app, handlers = root.level, app_logger.level, list(root.handlers) |
| 18 | try: |
| 19 | configure_application_logging(Settings(research_log_level="info")) |
| 20 | assert root.level == logging.INFO |
| 21 | assert app_logger.getEffectiveLevel() == logging.INFO |
| 22 | assert root.handlers == handlers |
| 23 | finally: |
| 24 | root.setLevel(original_root) |
| 25 | app_logger.setLevel(original_app) |
| 26 | |
| 27 | |
| 28 | def test_invalid_research_log_level_is_rejected() -> None: |
| 29 | with pytest.raises(ValidationError, match="Unsupported research log level"): |
| 30 | Settings(research_log_level="verbose") |
| 31 | |
| 32 | |
| 33 | def test_structured_logs_redact_headers_and_provider_query_tokens() -> None: |
| 34 | record = logging.LogRecord( |
| 35 | "app.provider", logging.ERROR, __file__, 1, |
| 36 | "failure Authorization: Bearer header-secret url=https://provider.invalid/data?api_token=query-secret&fmt=json", |
| 37 | (), None, |
| 38 | ) |
| 39 | output = json.loads(_StructuredLogFormatter("research-engine", "AZURE").format(record)) |
| 40 | assert "header-secret" not in output["message"] |
| 41 | assert "query-secret" not in output["message"] |
| 42 | assert output["message"].count("<redacted>") == 2 |