Files
FabledSteward/tests/core/test_logging_setup.py
T
bvandeusenandClaude Opus 5.5 98854d94f9
CI / lint (push) Successful in 4s
CI / integration (push) Successful in 1m11s
CI / unit (push) Successful in 1m31s
CI / publish (push) Successful in 2m34s
feat(platform): tini as PID 1, Hypercorn, a log-level setting, the image on CI's Python, an airgapped boot gate before push (#5159)
- Dockerfile: python:3.14-slim, the Python CI tests on (was 3.13). tini from
  apt is PID 1 (ENTRYPOINT tini -- entrypoint.sh): it reaps the zombies ssh
  and ansible leave and forwards SIGTERM.
- cli: Hypercorn's serve() with one worker (run, stream and scheduler state is
  in-process), a 10 s graceful timeout, and SIGTERM/SIGINT as the shutdown
  trigger. Access and error lines go through Steward's logging. --debug keeps
  Quart's reloading dev server.
- Logging: steward/core/logging_setup.py installs one root handler before
  anything logs (until now a library's basicConfig on import was the only
  configuration). Its level is the `log.level` setting (DEBUG/INFO/WARNING/
  ERROR, default INFO), applied at boot and on save in Settings → General.
- CI publish: build, then "The image boots with no internet" (rule 164,
  snippet #3552), then push, so a broken image never reaches a tag (rule 177).
  On an --internal network with its own Postgres, the image must migrate and
  answer /health, run tini as PID 1, serve first-run setup with its assets
  from /static and no third-party host, and exit 0 on SIGTERM. Logs and
  clean-up run always.

Tests: tests/core/test_logging_setup.py; the smoke step itself.
Not in this commit: the dependency lockfile (it conflicts with rule 154 and
is waiting on the operator).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:29:21 -04:00

58 lines
2.0 KiB
Python

"""Logging and the server (#5159): one handler, the level a setting, and
Hypercorn's lines going through the same logging."""
import logging
import pytest
from steward.core import logging_setup
from steward.core.settings import DEFAULTS
@pytest.fixture
def root():
root = logging.getLogger()
handlers, level = list(root.handlers), root.level
yield root
root.handlers[:] = handlers
root.setLevel(level)
def test_configuring_twice_installs_one_handler(root):
logging_setup.configure_logging("INFO")
logging_setup.configure_logging("DEBUG")
ours = [h for h in root.handlers if getattr(h, "_steward", False)]
assert len(ours) == 1
assert ours[0].formatter._fmt == logging_setup.FORMAT
assert root.level == logging.DEBUG
@pytest.mark.parametrize("given, applied", [
("warning", "WARNING"), (" Error ", "ERROR"), ("DEBUG", "DEBUG"),
("loud", "INFO"), ("", "INFO"), (None, "INFO"),
])
def test_the_level_setting_is_applied_and_an_unknown_one_is_info(root, given, applied):
assert logging_setup.set_log_level(given) == applied
assert root.level == getattr(logging, applied)
def test_a_library_basic_config_after_ours_changes_nothing(root):
logging_setup.configure_logging("WARNING")
before = list(root.handlers)
logging.basicConfig(level=logging.DEBUG, format="%(message)s")
assert root.handlers == before and root.level == logging.WARNING
def test_the_level_is_a_setting_defaulting_to_info():
assert DEFAULTS["log.level"] == "INFO"
assert DEFAULTS["log.level"] in logging_setup.LEVELS
def test_the_server_is_one_hypercorn_worker_logging_through_steward():
from steward.cli import GRACEFUL_TIMEOUT_SECONDS, hypercorn_config
config = hypercorn_config("0.0.0.0", 5000)
assert config.bind == ["0.0.0.0:5000"]
assert config.workers == 1 # run, stream and scheduler state is in-process
assert config.graceful_timeout == GRACEFUL_TIMEOUT_SECONDS
assert config.accesslog is logging.getLogger("hypercorn.access")
assert config.errorlog is logging.getLogger("hypercorn.error")