feat: Environment Configuration and Logging Baseline
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
|
||||
from tenbackward.logging_setup import (
|
||||
JsonFormatter,
|
||||
configure_json_logging,
|
||||
log_error,
|
||||
log_run_summary,
|
||||
log_startup,
|
||||
)
|
||||
|
||||
|
||||
def _capture(logger: logging.Logger) -> io.StringIO:
|
||||
buf = io.StringIO()
|
||||
handler = logging.StreamHandler(buf)
|
||||
handler.setFormatter(JsonFormatter())
|
||||
logger.handlers = [handler]
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.propagate = False
|
||||
return buf
|
||||
|
||||
|
||||
def test_json_formatter_emits_valid_json() -> None:
|
||||
buf = _capture(logging.getLogger("tenbackward.test"))
|
||||
logging.getLogger("tenbackward.test").info("hello", extra={"foo": 1})
|
||||
line = buf.getvalue().strip()
|
||||
payload = json.loads(line)
|
||||
assert payload["message"] == "hello"
|
||||
assert payload["level"] == "INFO"
|
||||
assert payload["logger"] == "tenbackward.test"
|
||||
assert payload["foo"] == 1
|
||||
assert "ts" in payload
|
||||
|
||||
|
||||
def test_json_formatter_redacts_secret_keys() -> None:
|
||||
buf = _capture(logging.getLogger("tenbackward.test"))
|
||||
logging.getLogger("tenbackward.test").info(
|
||||
"msg",
|
||||
extra={"access_token": "secret-value", "site_url": "https://x"},
|
||||
)
|
||||
payload = json.loads(buf.getvalue().strip())
|
||||
assert payload["access_token"] == "[REDACTED]"
|
||||
assert payload["site_url"] == "https://x"
|
||||
|
||||
|
||||
def test_log_run_summary_emits_one_line_with_counters() -> None:
|
||||
buf = _capture(logging.getLogger("tenbackward"))
|
||||
log_run_summary(3, 2, 1, 1, ["x"])
|
||||
lines = [line for line in buf.getvalue().splitlines() if line.strip()]
|
||||
assert len(lines) == 1
|
||||
payload = json.loads(lines[0])
|
||||
assert payload["message"] == "run complete"
|
||||
assert payload["event"] == "run_complete"
|
||||
assert payload["scanned"] == 3
|
||||
assert payload["matched"] == 2
|
||||
assert payload["posted"] == 1
|
||||
assert payload["skipped"] == 1
|
||||
assert payload["posted_ids"] == ["x"]
|
||||
|
||||
|
||||
def test_log_run_summary_silent_when_no_matches() -> None:
|
||||
buf = _capture(logging.getLogger("tenbackward"))
|
||||
log_run_summary(0, 0, 0, 0, [])
|
||||
assert buf.getvalue() == ""
|
||||
|
||||
|
||||
def test_log_startup_emits_single_info_line() -> None:
|
||||
buf = _capture(logging.getLogger("tenbackward"))
|
||||
log_startup(version="0.1.0", site_url="https://x", run_at="09:00", tz="Europe/Berlin")
|
||||
payload = json.loads(buf.getvalue().strip())
|
||||
assert payload["event"] == "startup"
|
||||
assert payload["version"] == "0.1.0"
|
||||
|
||||
|
||||
def test_log_error_redacts_secret_keys_and_exposes_exc_type() -> None:
|
||||
buf = _capture(logging.getLogger("tenbackward"))
|
||||
log_error("boom", exc=RuntimeError("x"), access_token="abc")
|
||||
payload = json.loads(buf.getvalue().strip())
|
||||
assert payload["level"] == "ERROR"
|
||||
assert payload["event"] == "boom"
|
||||
assert payload["access_token"] == "[REDACTED]"
|
||||
assert payload["exc_type"] == "RuntimeError"
|
||||
|
||||
|
||||
def test_configure_json_logging_is_idempotent() -> None:
|
||||
configure_json_logging()
|
||||
configure_json_logging()
|
||||
root = logging.getLogger()
|
||||
json_handlers = [h for h in root.handlers if isinstance(h.formatter, JsonFormatter)]
|
||||
assert len(json_handlers) == 1
|
||||
Reference in New Issue
Block a user