from __future__ import annotations import os import subprocess from pathlib import Path from fastapi.testclient import TestClient from router_dyndns.ddns import DdnsSettings, make_app ROOT = Path(__file__).parents[1] def run_script(*args: str, env: dict[str, str] | None = None) -> subprocess.CompletedProcess[str]: merged_env = os.environ.copy() if env: merged_env.update(env) return subprocess.run( args, check=True, cwd=ROOT, env=merged_env, text=True, capture_output=True, ) def test_shell_scripts_parse() -> None: for script in ("install-linux.sh", "karldns-update.sh", "uninstall-linux.sh"): run_script("sh", "-n", str(ROOT / "scripts" / script)) def test_updater_strips_fritzbox_placeholders_and_caches_ip(tmp_path: Path) -> None: app = make_app( DdnsSettings( database_path=tmp_path / "ddns.sqlite3", admin_password="admin", public_base_url="https://karldns.de", hostname_suffix="karldns.de", ) ) client = TestClient(app) account = client.post("/api/v1/hostnames/magic", json={}).json() update_url = account["update_url"] script = tmp_path / "curl" log_file = tmp_path / "curl.log" cache_file = tmp_path / "last-ip" lock_file = tmp_path / "updater.lock" script.write_text( f"""#!/bin/sh printf '%s\\n' "$*" >> {log_file} case "$*" in *'/ip'*) printf '203.0.113.25\\n' ;; *''*|*''*) printf 'bad placeholder leaked\\n'; exit 1 ;; *'myip=203.0.113.25'*) printf 'good 203.0.113.25\\n' ;; *) printf 'bad request\\n'; exit 1 ;; esac """, encoding="utf-8", ) script.chmod(0o700) result = run_script( "sh", str(ROOT / "scripts" / "karldns-update.sh"), env={ "KARLDNS_UPDATE_URL": update_url, "KARLDNS_CURL": str(script), "KARLDNS_IP_URL": "https://karldns.de/ip", "KARLDNS_CACHE_FILE": str(cache_file), "KARLDNS_LOCK_FILE": str(lock_file), "KARLDNS_IPV6": "off", }, ) assert "good 203.0.113.25" in result.stdout assert cache_file.read_text(encoding="utf-8").strip() == "203.0.113.25|" assert "" not in log_file.read_text(encoding="utf-8")