Archived
Add formatting and type checks
This commit is contained in:
1 parent
c0d82eaf26
commit
24747866bc
38 files changed
+800
-265
No files matched your search
+19
-5
@@ -138,8 +138,14 @@ def test_update_events_page_orders_raw_and_rollup_events_in_sql(tmp_path: Path)
|
||||
store.log_update_rollup("rollup-mid.example.net", "203.0.113.3", None, "unchanged", "ok", "198.51.100.3")
|
||||
|
||||
with store.connect() as conn:
|
||||
conn.execute("UPDATE update_events SET created_at = ? WHERE hostname = ?", ("2026-01-01T00:00:00+00:00", "raw-old.example.net"))
|
||||
conn.execute("UPDATE update_events SET created_at = ? WHERE hostname = ?", ("2026-01-03T00:00:00+00:00", "raw-new.example.net"))
|
||||
conn.execute(
|
||||
"UPDATE update_events SET created_at = ? WHERE hostname = ?",
|
||||
("2026-01-01T00:00:00+00:00", "raw-old.example.net"),
|
||||
)
|
||||
conn.execute(
|
||||
"UPDATE update_events SET created_at = ? WHERE hostname = ?",
|
||||
("2026-01-03T00:00:00+00:00", "raw-new.example.net"),
|
||||
)
|
||||
conn.execute(
|
||||
"UPDATE update_event_rollups SET last_seen_at = ? WHERE hostname = ?",
|
||||
("2026-01-02T00:00:00+00:00", "rollup-mid.example.net"),
|
||||
@@ -156,8 +162,14 @@ def test_update_events_support_keyset_pagination(tmp_path: Path) -> None:
|
||||
store.log_update_event("raw-old.example.net", "203.0.113.1", None, "updated", "ok", "198.51.100.1")
|
||||
store.log_update_event("raw-new.example.net", "203.0.113.2", None, "updated", "ok", "198.51.100.2")
|
||||
with store.connect() as conn:
|
||||
conn.execute("UPDATE update_events SET created_at = ? WHERE hostname = ?", ("2026-01-01T00:00:00+00:00", "raw-old.example.net"))
|
||||
conn.execute("UPDATE update_events SET created_at = ? WHERE hostname = ?", ("2026-01-02T00:00:00+00:00", "raw-new.example.net"))
|
||||
conn.execute(
|
||||
"UPDATE update_events SET created_at = ? WHERE hostname = ?",
|
||||
("2026-01-01T00:00:00+00:00", "raw-old.example.net"),
|
||||
)
|
||||
conn.execute(
|
||||
"UPDATE update_events SET created_at = ? WHERE hostname = ?",
|
||||
("2026-01-02T00:00:00+00:00", "raw-new.example.net"),
|
||||
)
|
||||
app = make_app(DdnsSettings(database_path=database_path, admin_password="admin"))
|
||||
client = TestClient(app)
|
||||
|
||||
@@ -247,7 +259,9 @@ def test_admin_can_rotate_links_and_password(tmp_path: Path) -> None:
|
||||
)
|
||||
assert update.status_code == 200
|
||||
assert "Update URL changed" in update.text
|
||||
assert client.get(f"/api/v1/router/update/{account['update_slug']}", params={"myip": "203.0.113.9"}).status_code == 404
|
||||
assert (
|
||||
client.get(f"/api/v1/router/update/{account['update_slug']}", params={"myip": "203.0.113.9"}).status_code == 404
|
||||
)
|
||||
|
||||
management = client.post(
|
||||
"/admin/hostnames/rotate",
|
||||
|
||||
+11
-20
@@ -116,8 +116,7 @@ def test_first_run_creates_schema_and_repeated_startup_is_idempotent(tmp_path: P
|
||||
def test_startup_migrates_legacy_cleanup_run_columns(tmp_path: Path) -> None:
|
||||
database_path = tmp_path / "ddns.sqlite3"
|
||||
with sqlite3.connect(database_path) as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
conn.execute("""
|
||||
CREATE TABLE cleanup_runs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_at TEXT NOT NULL,
|
||||
@@ -125,8 +124,7 @@ def test_startup_migrates_legacy_cleanup_run_columns(tmp_path: Path) -> None:
|
||||
abandoned_setups_deleted INTEGER NOT NULL,
|
||||
unused_accounts_deleted INTEGER NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
""")
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO cleanup_runs(
|
||||
@@ -144,12 +142,10 @@ def test_startup_migrates_legacy_cleanup_run_columns(tmp_path: Path) -> None:
|
||||
|
||||
with store.connect() as conn:
|
||||
columns = {row[1] for row in conn.execute("PRAGMA table_info(cleanup_runs)")}
|
||||
row = conn.execute(
|
||||
"""
|
||||
row = conn.execute("""
|
||||
SELECT unused_accounts_deleted
|
||||
FROM cleanup_runs
|
||||
"""
|
||||
).fetchone()
|
||||
""").fetchone()
|
||||
assert columns == {"id", "created_at", "unused_accounts_deleted"}
|
||||
assert tuple(row) == (1,)
|
||||
|
||||
@@ -225,16 +221,11 @@ def test_sqlite_startup_tuning_is_idempotent(tmp_path: Path) -> None:
|
||||
temp_store = conn.execute("PRAGMA temp_store").fetchone()[0]
|
||||
mmap_size = conn.execute("PRAGMA mmap_size").fetchone()[0]
|
||||
user_version = conn.execute("PRAGMA user_version").fetchone()[0]
|
||||
index_names = {
|
||||
row[1]
|
||||
for row in conn.execute(
|
||||
"""
|
||||
index_names = {row[1] for row in conn.execute("""
|
||||
SELECT type, name
|
||||
FROM sqlite_master
|
||||
WHERE type = 'index'
|
||||
"""
|
||||
)
|
||||
}
|
||||
""")}
|
||||
|
||||
assert busy_timeout == 10_000
|
||||
assert journal_mode == "wal"
|
||||
@@ -255,8 +246,7 @@ def test_accounts_schema_migration_removes_legacy_router_credentials(tmp_path: P
|
||||
database_path = tmp_path / "ddns.sqlite3"
|
||||
with sqlite3.connect(database_path) as conn:
|
||||
conn.execute("PRAGMA user_version = 2")
|
||||
conn.execute(
|
||||
"""
|
||||
conn.execute("""
|
||||
CREATE TABLE accounts (
|
||||
hostname TEXT PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
@@ -268,8 +258,7 @@ def test_accounts_schema_migration_removes_legacy_router_credentials(tmp_path: P
|
||||
accepted_at TEXT,
|
||||
disabled INTEGER NOT NULL DEFAULT 0
|
||||
)
|
||||
"""
|
||||
)
|
||||
""")
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO accounts(
|
||||
@@ -303,7 +292,9 @@ def test_accounts_schema_migration_removes_legacy_router_credentials(tmp_path: P
|
||||
assert account["hostname"] == "home.ddns.example.net"
|
||||
|
||||
|
||||
def test_rate_limit_flush_requeues_counts_after_database_failure(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_rate_limit_flush_requeues_counts_after_database_failure(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
store = DdnsStore(tmp_path / "ddns.sqlite3")
|
||||
assert store.allow_rate_limit("198.51.100.1:/api", 60) is True
|
||||
|
||||
|
||||
@@ -140,7 +140,10 @@ esac
|
||||
assert "KarlDNS: registering hostname" in result.stdout
|
||||
config = home_dir / ".config" / "karldns" / "updater.env"
|
||||
config_text = config.read_text(encoding="utf-8")
|
||||
assert "KARLDNS_UPDATE_URL='https://karldns.test/api/v1/router/update/karl_update_secret_12345678901234567890'" in config_text
|
||||
assert (
|
||||
"KARLDNS_UPDATE_URL='https://karldns.test/api/v1/router/update/karl_update_secret_12345678901234567890'"
|
||||
in config_text
|
||||
)
|
||||
assert "KARLDNS_HOSTNAME='abc.karldns.test'" in config_text
|
||||
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ def test_dashboard_unlock_cookie_rejects_password_hash_signed_forgery(tmp_path:
|
||||
|
||||
def test_settings_reject_unknown_constructor_fields() -> None:
|
||||
with pytest.raises(ValueError, match="Extra inputs are not permitted"):
|
||||
DdnsSettings(admn_password="typo")
|
||||
DdnsSettings(**{"admn_password": "typo"})
|
||||
|
||||
|
||||
def test_trusted_host_middleware_rejects_unlisted_host(tmp_path: Path) -> None:
|
||||
|
||||
+40
-13
@@ -58,7 +58,9 @@ def test_legacy_custom_account_form_is_rejected(tmp_path: Path) -> None:
|
||||
)
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.post("/accounts", data={"mode": "custom", "hostname": "home.example.net", "claim_secret": "legacy"})
|
||||
response = client.post(
|
||||
"/accounts", data={"mode": "custom", "hostname": "home.example.net", "claim_secret": "legacy"}
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
assert app.state.store.count_accounts() == 0
|
||||
@@ -174,14 +176,18 @@ def test_generated_hostname_must_be_activated_before_updates_work(tmp_path: Path
|
||||
account = app.state.store.get_account_by_management_slug(management_slug)
|
||||
|
||||
assert account["accepted_at"] is None
|
||||
assert client.get(f"/api/v1/router/update/{account['update_slug']}", params={"myip": "203.0.113.9"}).status_code == 404
|
||||
assert (
|
||||
client.get(f"/api/v1/router/update/{account['update_slug']}", params={"myip": "203.0.113.9"}).status_code == 404
|
||||
)
|
||||
|
||||
activated = client.post(f"/m/{management_slug}/activate", data={"csrf": _csrf(created.text)})
|
||||
|
||||
assert activated.status_code == 200
|
||||
assert "Hostname registered" in activated.text
|
||||
assert app.state.store.get_account_by_management_slug(management_slug)["accepted_at"]
|
||||
assert client.get(f"/api/v1/router/update/{account['update_slug']}", params={"myip": "203.0.113.9"}).status_code == 200
|
||||
assert (
|
||||
client.get(f"/api/v1/router/update/{account['update_slug']}", params={"myip": "203.0.113.9"}).status_code == 200
|
||||
)
|
||||
|
||||
|
||||
def test_old_magic_route_is_gone(tmp_path: Path) -> None:
|
||||
@@ -322,7 +328,10 @@ def test_management_dashboard_password_can_be_removed(tmp_path: Path) -> None:
|
||||
|
||||
unlock = client.post(
|
||||
f"/m/{account['management_slug']}/unlock",
|
||||
data={"csrf": _csrf(client.get(f"/m/{account['management_slug']}").text), "dashboard_password": "correct horse"},
|
||||
data={
|
||||
"csrf": _csrf(client.get(f"/m/{account['management_slug']}").text),
|
||||
"dashboard_password": "correct horse",
|
||||
},
|
||||
)
|
||||
assert unlock.status_code == 200
|
||||
|
||||
@@ -430,12 +439,18 @@ def test_management_dashboard_rotates_update_url(tmp_path: Path) -> None:
|
||||
assert "data-secret-toggle" in dashboard.text
|
||||
assert 'type="password"' in dashboard.text
|
||||
|
||||
rotated_update = client.post(f"/m/{account['management_slug']}/rotate", data={"csrf": _csrf(dashboard.text), "action": "update"})
|
||||
rotated_update = client.post(
|
||||
f"/m/{account['management_slug']}/rotate", data={"csrf": _csrf(dashboard.text), "action": "update"}
|
||||
)
|
||||
assert rotated_update.status_code == 200
|
||||
assert "Update URL changed. Update your router now." in rotated_update.text
|
||||
assert "data-secret-input" in rotated_update.text
|
||||
assert client.get(f"/api/v1/router/update/{account['update_slug']}", params={"myip": "203.0.113.9"}).status_code == 404
|
||||
statuses = {event["status"] for event in app.state.store.list_update_events_for_hostname("home.ddns.example.net", 10)}
|
||||
assert (
|
||||
client.get(f"/api/v1/router/update/{account['update_slug']}", params={"myip": "203.0.113.9"}).status_code == 404
|
||||
)
|
||||
statuses = {
|
||||
event["status"] for event in app.state.store.list_update_events_for_hostname("home.ddns.example.net", 10)
|
||||
}
|
||||
assert "dashboard_update_url_rotated" in statuses
|
||||
assert "dashboard_router_credentials_rotated" not in statuses
|
||||
|
||||
@@ -449,13 +464,19 @@ def test_management_dashboard_actions_are_scoped_to_slug_owner(tmp_path: Path) -
|
||||
client = TestClient(app)
|
||||
|
||||
dashboard = client.get(f"/m/{first['management_slug']}")
|
||||
rotated = client.post(f"/m/{first['management_slug']}/rotate", data={"csrf": _csrf(dashboard.text), "action": "update"})
|
||||
rotated = client.post(
|
||||
f"/m/{first['management_slug']}/rotate", data={"csrf": _csrf(dashboard.text), "action": "update"}
|
||||
)
|
||||
|
||||
assert rotated.status_code == 200
|
||||
assert store.get_account_by_hostname("first.ddns.example.net")["update_slug"] != first["update_slug"]
|
||||
assert store.get_account_by_hostname("second.ddns.example.net")["update_slug"] == second["update_slug"]
|
||||
assert client.get(f"/api/v1/router/update/{first['update_slug']}", params={"myip": "203.0.113.1"}).status_code == 404
|
||||
assert client.get(f"/api/v1/router/update/{second['update_slug']}", params={"myip": "203.0.113.2"}).status_code == 200
|
||||
assert (
|
||||
client.get(f"/api/v1/router/update/{first['update_slug']}", params={"myip": "203.0.113.1"}).status_code == 404
|
||||
)
|
||||
assert (
|
||||
client.get(f"/api/v1/router/update/{second['update_slug']}", params={"myip": "203.0.113.2"}).status_code == 200
|
||||
)
|
||||
|
||||
|
||||
def test_locked_dashboard_rejects_mutating_actions_without_unlock_cookie(tmp_path: Path) -> None:
|
||||
@@ -558,7 +579,10 @@ def test_public_examples_use_karldns_domain_and_no_shell_prompt(tmp_path: Path)
|
||||
assert "https://karldns.de/api/v1/router/update/<secret>" in response.text
|
||||
assert "http://127.0.0.1:8099/api/v1/router/update/<secret>" not in response.text
|
||||
assert "$ curl" not in response.text
|
||||
assert 'data-copy="curl -sS 'https://karldns.de/api/v1/router/update/<secret>?myip=203.0.113.10'"' in response.text
|
||||
assert (
|
||||
'data-copy="curl -sS 'https://karldns.de/api/v1/router/update/<secret>?myip=203.0.113.10'"'
|
||||
in response.text
|
||||
)
|
||||
|
||||
|
||||
def test_application_pages_use_stricter_script_csp(tmp_path: Path) -> None:
|
||||
@@ -579,7 +603,10 @@ def test_application_pages_use_stricter_script_csp(tmp_path: Path) -> None:
|
||||
assert "<style>" not in response.text
|
||||
assert "script-src 'self' 'unsafe-inline'" in response.headers["content-security-policy"]
|
||||
assert "style-src 'self'" in response.headers["content-security-policy"]
|
||||
assert "connect-src 'self' https://api4.ipify.org https://api6.ipify.org" in response.headers["content-security-policy"]
|
||||
assert (
|
||||
"connect-src 'self' https://api4.ipify.org https://api6.ipify.org"
|
||||
in response.headers["content-security-policy"]
|
||||
)
|
||||
assert "style-src 'self' 'unsafe-inline'" not in response.headers["content-security-policy"]
|
||||
bootstrap_css = client.get("/bootstrap-subset.css")
|
||||
assert bootstrap_css.status_code == 200
|
||||
@@ -622,7 +649,7 @@ def test_footer_links_and_privacy_page_are_available(tmp_path: Path) -> None:
|
||||
assert 'property="og:image:width" content="1200"' in response.text
|
||||
assert 'property="og:image:height" content="630"' in response.text
|
||||
assert 'name="twitter:card" content="summary_large_image"' in response.text
|
||||
assert 'application/ld+json' in response.text
|
||||
assert "application/ld+json" in response.text
|
||||
assert "NetworkingApplication" in response.text
|
||||
assert "Made with" in response.text
|
||||
assert "❤️" in response.text
|
||||
|
||||
@@ -16,7 +16,9 @@ def test_router_update_get_accepts_fritzbox_placeholders(tmp_path: Path) -> None
|
||||
database_path = tmp_path / "ddns.sqlite3"
|
||||
store = DdnsStore(database_path)
|
||||
account = store.create_account("home.ddns.example.net", "router")
|
||||
app = make_app(DdnsSettings(database_path=database_path, admin_password="admin", hostname_suffix="ddns.example.net"))
|
||||
app = make_app(
|
||||
DdnsSettings(database_path=database_path, admin_password="admin", hostname_suffix="ddns.example.net")
|
||||
)
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.get(
|
||||
@@ -182,7 +184,9 @@ def test_slug_update_rejects_hostname_outside_provider_suffix(tmp_path: Path) ->
|
||||
database_path = tmp_path / "ddns.sqlite3"
|
||||
store = DdnsStore(database_path)
|
||||
account = store.create_account("home.example.net", "router")
|
||||
app = make_app(DdnsSettings(database_path=database_path, admin_password="admin", hostname_suffix="ddns.example.net"))
|
||||
app = make_app(
|
||||
DdnsSettings(database_path=database_path, admin_password="admin", hostname_suffix="ddns.example.net")
|
||||
)
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.get(_update_path(account), params={"myip": "203.0.113.9"})
|
||||
|
||||
Reference in new issue
Block a user