Remove logheader callback feature; keep Kanta-colored extra string in default header
This commit is contained in:
@@ -80,30 +80,6 @@ def test_log_change_appends_extra_string(capsys):
|
||||
assert f"{_TARGET}mydb.db{_RESET}" in captured.err
|
||||
|
||||
|
||||
def test_log_change_headerfmt_replaces_header(capsys):
|
||||
kanta_logger = logging.getLogger("kanta")
|
||||
kanta_logger.handlers.clear()
|
||||
configure_logging()
|
||||
log_change(
|
||||
"update",
|
||||
{},
|
||||
headerfmt=lambda action, user, extra: f"CUSTOM {action} {extra}",
|
||||
extra="7",
|
||||
)
|
||||
captured = capsys.readouterr()
|
||||
assert "CUSTOM update 7" in captured.err
|
||||
|
||||
|
||||
def test_log_change_headerfmt_none_falls_back(capsys):
|
||||
kanta_logger = logging.getLogger("kanta")
|
||||
kanta_logger.handlers.clear()
|
||||
configure_logging()
|
||||
log_change("update", {}, user="alice", headerfmt=lambda *args: None)
|
||||
captured = capsys.readouterr()
|
||||
assert "update" in captured.err
|
||||
assert "alice" in captured.err
|
||||
|
||||
|
||||
def test_log_change_log_diff_false(capsys):
|
||||
kanta_logger = logging.getLogger("kanta")
|
||||
kanta_logger.handlers.clear()
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
|
||||
from kanta import Kanta
|
||||
from kanta.callbacks import DictPost, DictPre
|
||||
from kanta.logging import _ACTION, _RESET, _TARGET, _USER, configure_logging
|
||||
|
||||
from .support import Data, make_kanta, read_changes
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_kanta_loggers():
|
||||
yield
|
||||
for name in ("kanta", "kanta.transaction", "kanta.bootstrap", "kanta.migration"):
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(logging.NOTSET)
|
||||
logger.propagate = True
|
||||
logger.handlers.clear()
|
||||
|
||||
|
||||
def test_logheader_rejects_async(tmp_path, format_config):
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
with pytest.raises(TypeError, match="must not be async"):
|
||||
|
||||
@kanta.logheader
|
||||
async def header(action: str) -> str:
|
||||
return action
|
||||
|
||||
|
||||
def test_logheader_rejects_bad_return_annotation(tmp_path, format_config):
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
with pytest.raises(TypeError, match="must return"):
|
||||
|
||||
@kanta.logheader
|
||||
def header(action: str) -> int:
|
||||
return 1
|
||||
|
||||
|
||||
def test_logheader_rejects_unknown_annotation(tmp_path, format_config):
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
with pytest.raises(TypeError, match="unsupported annotation"):
|
||||
|
||||
@kanta.logheader
|
||||
def header(action: str, bogus: int) -> str:
|
||||
return action
|
||||
|
||||
|
||||
def test_logheader_rejects_dict_extra(tmp_path, format_config):
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
with pytest.raises(TypeError, match="unsupported annotation"):
|
||||
|
||||
@kanta.logheader
|
||||
def header(action: str, extra: dict) -> str:
|
||||
return action
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_logheader_replaces_default_header(tmp_path, format_config, caplog):
|
||||
caplog.set_level(logging.INFO, logger="kanta.transaction")
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
@kanta.logheader
|
||||
def header(action: str, user: str, extra: str) -> str:
|
||||
return f"{user} {action} {extra}"
|
||||
|
||||
await kanta.open(log=False)
|
||||
with kanta.transaction(action="update", user="alice", extra="tgt") as data:
|
||||
data.counter = 1
|
||||
await kanta.close()
|
||||
|
||||
# caplog strips ANSI codes; the colors are verified via capsys below.
|
||||
assert "alice update tgt" in caplog.text
|
||||
# The diff body is still logged after the custom header.
|
||||
assert "counter" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_logheader_parts_are_colored_by_kanta(tmp_path, format_config, capsys):
|
||||
logging.getLogger("kanta").handlers.clear()
|
||||
configure_logging()
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
@kanta.logheader
|
||||
def header(action: str, user: str, extra: str) -> str:
|
||||
return f"{user} {action} {extra}"
|
||||
|
||||
await kanta.open(log=False)
|
||||
with kanta.transaction(action="update", user="alice", extra="tgt") as data:
|
||||
data.counter = 1
|
||||
await kanta.close()
|
||||
|
||||
err = capsys.readouterr().err
|
||||
assert f"{_USER}alice{_RESET}" in err
|
||||
assert f"{_ACTION}update{_RESET}" in err
|
||||
assert f"{_TARGET}tgt{_RESET}" in err
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_logheader_missing_user_and_extra_are_empty(
|
||||
tmp_path, format_config, caplog
|
||||
):
|
||||
caplog.set_level(logging.INFO, logger="kanta.transaction")
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
@kanta.logheader
|
||||
def header(action: str, user: str, extra: str) -> str:
|
||||
return f"<{user}><{extra}>"
|
||||
|
||||
await kanta.open(log=False)
|
||||
with kanta.transaction(action="update") as data:
|
||||
data.counter = 1
|
||||
await kanta.close()
|
||||
|
||||
assert "<><>" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_logheader_injects_states_and_kanta(tmp_path, format_config, caplog):
|
||||
caplog.set_level(logging.INFO, logger="kanta.transaction")
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
@kanta.logheader
|
||||
def header(
|
||||
action: str,
|
||||
previous: DictPre,
|
||||
current: DictPost,
|
||||
kanta: Kanta,
|
||||
) -> str:
|
||||
return (
|
||||
f"counter {previous.get('counter')}"
|
||||
f" -> {current.get('counter')} db={kanta.filename.name}"
|
||||
)
|
||||
|
||||
await kanta.open(log=False)
|
||||
with kanta.transaction(action="increment") as data:
|
||||
data.counter = 5
|
||||
await kanta.close()
|
||||
|
||||
assert "counter 0 -> 5 db=test.db" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_logheader_chain_first_non_none_wins(tmp_path, format_config, caplog):
|
||||
caplog.set_level(logging.INFO, logger="kanta.transaction")
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
@kanta.logheader
|
||||
def first(action: str) -> str | None:
|
||||
return None
|
||||
|
||||
@kanta.logheader
|
||||
def second(action: str, extra: str) -> str:
|
||||
return f"SECOND {extra}"
|
||||
|
||||
await kanta.open(log=False)
|
||||
with kanta.transaction(action="update", extra="marked") as data:
|
||||
data.counter = 1
|
||||
await kanta.close()
|
||||
|
||||
assert "SECOND marked" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_logheader_all_none_falls_back_to_default(
|
||||
tmp_path, format_config, caplog
|
||||
):
|
||||
caplog.set_level(logging.INFO, logger="kanta.transaction")
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
@kanta.logheader
|
||||
def header(action: str) -> str | None:
|
||||
return None
|
||||
|
||||
await kanta.open(log=False)
|
||||
with kanta.transaction(action="update", user="alice") as data:
|
||||
data.counter = 1
|
||||
await kanta.close()
|
||||
|
||||
assert "update" in caplog.text
|
||||
assert "alice" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_logheader_receives_formatted_user(tmp_path, format_config, caplog):
|
||||
caplog.set_level(logging.INFO, logger="kanta.transaction")
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
@kanta.logfmt(path="$user")
|
||||
def resolve_user(value: str) -> str | None:
|
||||
return "Alice"
|
||||
|
||||
@kanta.logheader
|
||||
def header(action: str, user: str) -> str:
|
||||
return f"actor={user}"
|
||||
|
||||
await kanta.open(log=False)
|
||||
with kanta.transaction(action="update", user="uuid-1") as data:
|
||||
data.counter = 1
|
||||
await kanta.close()
|
||||
|
||||
assert "actor=Alice" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_logheader_applies_to_bootstrap(tmp_path, format_config, caplog):
|
||||
caplog.set_level(logging.INFO, logger="kanta.bootstrap")
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
@kanta.logheader
|
||||
def header(action: str) -> str:
|
||||
return f"BOOT {action}"
|
||||
|
||||
await kanta.open()
|
||||
await kanta.close()
|
||||
|
||||
assert "BOOT bootstrap" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_transaction_extra_string_in_default_header(
|
||||
tmp_path, format_config, caplog
|
||||
):
|
||||
caplog.set_level(logging.INFO, logger="kanta.transaction")
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
await kanta.open(log=False)
|
||||
with kanta.transaction(action="export", extra="mydb.db") as data:
|
||||
data.counter = 1
|
||||
await kanta.close()
|
||||
|
||||
assert "export" in caplog.text
|
||||
assert "mydb.db" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_extra_is_not_persisted(tmp_path, format_config):
|
||||
path = tmp_path / "test.db"
|
||||
kanta = make_kanta(path, Data, format_config)
|
||||
|
||||
await kanta.open(log=False)
|
||||
with kanta.transaction(action="update", user="alice", extra="session-3") as data:
|
||||
data.counter = 1
|
||||
await kanta.close()
|
||||
|
||||
record = read_changes(path, format_config)[-1]
|
||||
assert record.a == "update"
|
||||
assert record.u == "alice"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_transaction_log_dict_toggles(tmp_path, format_config, caplog):
|
||||
caplog.set_level(logging.INFO, logger="kanta.transaction")
|
||||
kanta = make_kanta(tmp_path / "test.db", Data, format_config)
|
||||
|
||||
await kanta.open(log=False)
|
||||
with kanta.transaction(
|
||||
action="myaction", log={"header": True, "diff": False}
|
||||
) as data:
|
||||
data.counter = 1
|
||||
with kanta.transaction(
|
||||
action="otheraction", log={"header": False, "diff": True}
|
||||
) as data:
|
||||
data.counter = 2
|
||||
await kanta.close()
|
||||
|
||||
# First transaction: header only.
|
||||
assert "myaction" in caplog.text
|
||||
# Second transaction: diff only, no header.
|
||||
assert "otheraction" not in caplog.text
|
||||
assert "counter" in caplog.text
|
||||
Reference in New Issue
Block a user