from pathlib import Path from karldns import DdnsStore from karldns.routers.admin import _admin_cleanup_notice, _admin_table_offsets from karldns.web.templates_admin import ( _account_rows, _admin_page_context, _cleanup_accounts_removed, _cleanup_last_run, _cleanup_summary, _cleanup_task, _event_rows, ) def test_admin_page_context_collects_counts_and_offset_rows(tmp_path: Path) -> None: store = DdnsStore(tmp_path / "ddns.sqlite3") first = store.create_account("first.example.net", "router") second = store.create_account("second.example.net", "router") store.log_update_event(first["hostname"], None, None, "first", "first detail", "198.51.100.1") store.log_update_event(second["hostname"], None, None, "second", "second detail", "198.51.100.2") context = _admin_page_context( store, table_offsets={"accounts": 1, "update_events": 1}, table_limit=1, notice_html=None, ) assert context.accounts[0]["hostname"] == "second.example.net" assert context.update_events[0]["hostname"] == "first.example.net" assert context.table_counts["accounts"] == 2 assert context.table_counts["update_events"] == 2 assert context.table_limit == 1 def test_admin_rows_render_empty_states_when_tables_have_no_rows() -> None: assert "No hostnames yet." in _account_rows([], csrf="csrf", admin_path="/admin") assert "No update events yet." in _event_rows([]) def test_admin_table_offsets_groups_query_values() -> None: assert _admin_table_offsets(accounts_offset=1, cleanup_runs_offset=2, update_events_offset=3) == { "accounts": 1, "cleanup_runs": 2, "update_events": 3, } def test_admin_cleanup_notice_is_empty_without_done_flag() -> None: assert _admin_cleanup_notice(cleanup_done=False, cleanup_unused=1, cleanup_events=2) is None assert "Removed 1 hostnames and 2 old events" in _admin_cleanup_notice( cleanup_done=True, cleanup_unused=1, cleanup_events=2, ) def test_cleanup_task_prefers_named_cleanup_task() -> None: task = _cleanup_task( [ {"name": "other", "next_run_at": "wrong"}, {"name": "cleanup", "next_run_at": "right"}, ] ) assert task["next_run_at"] == "right" def test_cleanup_summary_uses_task_and_last_run_fallback() -> None: summary = _cleanup_summary( cleanup_runs=[{"created_at": "2026-01-02T03:04:05+00:00", "unused_accounts_deleted": 3}], scheduled_tasks=[{"name": "cleanup", "interval_seconds": 3600}], fallback_interval_seconds=7200, ) assert summary.last_run == "2026-01-02T03:04:05+00:00" assert summary.next_run is None assert summary.status == "pending" assert summary.interval_seconds == 3600 assert summary.accounts_removed == 3 def test_cleanup_summary_helpers_prefer_task_and_default_missing_cleanup_values() -> None: task = {"last_finished_at": "2026-01-03T00:00:00+00:00"} cleanup = {"created_at": "2026-01-02T00:00:00+00:00"} assert _cleanup_last_run(task, cleanup) == "2026-01-03T00:00:00+00:00" assert _cleanup_last_run({}, cleanup) == "2026-01-02T00:00:00+00:00" assert _cleanup_accounts_removed({}) == 0