feat(logging): seed month + posting events (#27)
This commit is contained in:
parent
41ee888d3b
commit
7340a66988
2 changed files with 112 additions and 0 deletions
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from datetime import date, datetime, timezone
|
||||
|
|
@ -28,6 +29,8 @@ from quartermaster.models import (
|
|||
Section,
|
||||
)
|
||||
|
||||
logger = logging.getLogger("quartermaster.month_service")
|
||||
|
||||
YEAR_MONTH_RE = re.compile(r"^\d{4}-(0[1-9]|1[0-2])$")
|
||||
|
||||
|
||||
|
|
@ -148,6 +151,10 @@ def create_month(db: Session, year_month: str) -> Month:
|
|||
)
|
||||
db.commit()
|
||||
db.refresh(month)
|
||||
logger.info(
|
||||
"created month snapshot",
|
||||
extra={"event": "month_created", "year_month": month.year_month},
|
||||
)
|
||||
return month
|
||||
|
||||
|
||||
|
|
@ -314,6 +321,15 @@ def add_posting(
|
|||
db.add(posting)
|
||||
db.commit()
|
||||
db.refresh(posting)
|
||||
logger.info(
|
||||
"added posting",
|
||||
extra={
|
||||
"event": "posting_added",
|
||||
"posting_id": posting.id,
|
||||
"month_entry_id": posting.month_entry_id,
|
||||
"amount": str(posting.amount),
|
||||
},
|
||||
)
|
||||
return posting
|
||||
|
||||
|
||||
|
|
@ -368,6 +384,10 @@ def delete_posting(
|
|||
entry = posting.entry
|
||||
db.delete(posting)
|
||||
db.commit()
|
||||
logger.info(
|
||||
"deleted posting",
|
||||
extra={"event": "posting_deleted", "posting_id": posting_id},
|
||||
)
|
||||
return entry
|
||||
|
||||
|
||||
|
|
@ -428,6 +448,10 @@ def close_month(db: Session, month: Month) -> Month:
|
|||
month.closed_at = datetime.now(timezone.utc)
|
||||
db.commit()
|
||||
db.refresh(month)
|
||||
logger.info(
|
||||
"closed month",
|
||||
extra={"event": "month_closed", "year_month": month.year_month},
|
||||
)
|
||||
return month
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,14 @@ from __future__ import annotations
|
|||
import io
|
||||
import json
|
||||
import logging
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
|
||||
from quartermaster import month_service, service
|
||||
from quartermaster.logging_config import LOG_CONFIG
|
||||
from quartermaster.models import Section
|
||||
|
||||
|
||||
def _build_formatter():
|
||||
|
|
@ -89,3 +95,85 @@ def test_log_config_loads_via_dictconfig():
|
|||
from quartermaster.logging_config import LOG_CONFIG
|
||||
|
||||
logging.config.dictConfig(LOG_CONFIG)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_quartermaster_logger():
|
||||
"""Ensure the quartermaster logger propagates to root so caplog can capture records.
|
||||
|
||||
logging.config.dictConfig (called in test_log_config_loads_via_dictconfig) sets
|
||||
propagate=False on the quartermaster logger. caplog injects its handler into the
|
||||
root logger, so records only reach it when propagation is enabled. This fixture
|
||||
temporarily re-enables propagation for every test in this module and restores the
|
||||
original value on teardown.
|
||||
"""
|
||||
qm_logger = logging.getLogger("quartermaster")
|
||||
original_propagate = qm_logger.propagate
|
||||
qm_logger.propagate = True
|
||||
yield
|
||||
qm_logger.propagate = original_propagate
|
||||
|
||||
|
||||
def _make_debt_minimum(db, name="Loan", amount=Decimal("100.00")):
|
||||
from quartermaster.models import Entry
|
||||
entry = Entry(section=Section.debt_minimum, name=name, amount=amount)
|
||||
db.add(entry)
|
||||
db.commit()
|
||||
db.refresh(entry)
|
||||
return entry
|
||||
|
||||
|
||||
def test_create_month_logs_month_created_event(db, caplog):
|
||||
caplog.set_level(logging.INFO, logger="quartermaster")
|
||||
month_service.create_month(db, "2026-05")
|
||||
|
||||
events = [r for r in caplog.records if getattr(r, "event", None) == "month_created"]
|
||||
assert len(events) == 1
|
||||
assert events[0].levelname == "INFO"
|
||||
|
||||
|
||||
def test_close_month_logs_month_closed_event(db, caplog):
|
||||
month = month_service.create_month(db, "2026-06")
|
||||
# move through active → closed with a zero-applied budget (no entries yet)
|
||||
month_service.activate_month(db, month)
|
||||
caplog.clear()
|
||||
caplog.set_level(logging.INFO, logger="quartermaster")
|
||||
|
||||
month_service.close_month(db, month)
|
||||
|
||||
events = [r for r in caplog.records if getattr(r, "event", None) == "month_closed"]
|
||||
assert len(events) == 1
|
||||
|
||||
|
||||
def test_add_posting_logs_posting_added_event(db, caplog):
|
||||
_make_debt_minimum(db)
|
||||
month = month_service.create_month(db, "2026-07")
|
||||
entry = month.entries[0]
|
||||
caplog.set_level(logging.INFO, logger="quartermaster")
|
||||
|
||||
month_service.add_posting(
|
||||
db, month, entry.id,
|
||||
occurred_on=date(2026, 7, 15),
|
||||
amount=Decimal("25.00"),
|
||||
)
|
||||
|
||||
events = [r for r in caplog.records if getattr(r, "event", None) == "posting_added"]
|
||||
assert len(events) == 1
|
||||
|
||||
|
||||
def test_delete_posting_logs_posting_deleted_event(db, caplog):
|
||||
_make_debt_minimum(db)
|
||||
month = month_service.create_month(db, "2026-08")
|
||||
entry = month.entries[0]
|
||||
posting = month_service.add_posting(
|
||||
db, month, entry.id,
|
||||
occurred_on=date(2026, 8, 15),
|
||||
amount=Decimal("10.00"),
|
||||
)
|
||||
caplog.clear()
|
||||
caplog.set_level(logging.INFO, logger="quartermaster")
|
||||
|
||||
month_service.delete_posting(db, month, posting.id)
|
||||
|
||||
events = [r for r in caplog.records if getattr(r, "event", None) == "posting_deleted"]
|
||||
assert len(events) == 1
|
||||
|
|
|
|||
Loading…
Reference in a new issue