This repository has been archived on 2026-10-28. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
karldns.de/tests/test_scripts.py
T

249 lines
7.6 KiB
Python
Raw Normal View History

2026-05-20 23:55:27 +02:00
from __future__ import annotations
import os
import subprocess
from pathlib import Path
from fastapi.testclient import TestClient
2026-05-21 17:23:08 +02:00
from karldns import DdnsSettings, make_app
2026-05-20 23:55:27 +02:00
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)
2026-05-21 17:23:08 +02:00
account = client.post("/api/v1/hostnames").json()
2026-05-20 23:55:27 +02:00
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
*'<ipaddr>'*|*'<ip6addr>'*) 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_CACHE_FILE": str(cache_file),
"KARLDNS_LOCK_FILE": str(lock_file),
2026-05-21 17:23:08 +02:00
"KARLDNS_IPV4": "203.0.113.25",
2026-05-20 23:55:27 +02:00
"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 "<ipaddr>" not in log_file.read_text(encoding="utf-8")
2026-05-21 17:23:08 +02:00
def test_installer_registers_generated_hostname_before_use(tmp_path: Path) -> None:
bin_dir = tmp_path / "bin"
home_dir = tmp_path / "home"
log_file = tmp_path / "curl.log"
bin_dir.mkdir()
home_dir.mkdir()
curl = bin_dir / "curl"
curl.write_text(
f"""#!/bin/sh
printf '%s\\n' "$*" >> {log_file}
output_file=""
previous=""
for argument in "$@"; do
if [ "$previous" = "-o" ]; then
output_file="$argument"
fi
previous="$argument"
done
case "$*" in
*'/karldns-update.sh'*)
cat > "$output_file" <<'EOS'
#!/bin/sh
printf 'updater placeholder\\n'
EOS
;;
*'/api/v1/hostnames'*)
printf '%s\\n' '{{"hostname":"abc.karldns.test","status":"pending","update_url":"https://karldns.test/api/v1/router/update/update-secret?myip=<ipaddr>&myipv6=<ip6addr>","dashboard_url":"https://karldns.test/m/manage-secret","registration_url":"https://karldns.test/api/v1/router/register/manage-secret"}}'
;;
*'/api/v1/router/register/manage-secret'*)
printf '%s\\n' 'good registered'
;;
*)
printf 'unexpected curl call: %s\\n' "$*" >&2
exit 1
;;
esac
""",
encoding="utf-8",
)
curl.chmod(0o700)
result = run_script(
"sh",
str(ROOT / "scripts" / "install-linux.sh"),
"--mode",
"user",
"--no-cron",
"--no-run",
env={
"HOME": str(home_dir),
"PATH": f"{bin_dir}{os.pathsep}{os.environ['PATH']}",
"KARLDNS_BASE_URL": "https://karldns.test",
"KARLDNS_RAW_BASE_URL": "https://raw.karldns.test",
},
)
log = log_file.read_text(encoding="utf-8")
assert "/api/v1/hostnames" in log
assert "/api/v1/router/register/manage-secret" in log
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/update-secret'" in config_text
assert "KARLDNS_HOSTNAME='abc.karldns.test'" in config_text
def test_user_installer_and_uninstaller_manage_cron_idempotently(tmp_path: Path) -> None:
bin_dir = tmp_path / "bin"
home_dir = tmp_path / "home"
log_file = tmp_path / "curl.log"
crontab_file = tmp_path / "crontab.txt"
bin_dir.mkdir()
home_dir.mkdir()
curl = bin_dir / "curl"
crontab = bin_dir / "crontab"
curl.write_text(
f"""#!/bin/sh
printf '%s\\n' "$*" >> {log_file}
output_file=""
previous=""
for argument in "$@"; do
if [ "$previous" = "-o" ]; then
output_file="$argument"
fi
previous="$argument"
done
case "$*" in
*'/karldns-update.sh'*)
cat > "$output_file" <<'EOS'
#!/bin/sh
printf 'updater placeholder\\n'
EOS
;;
*'/api/v1/hostnames'*)
printf '%s\\n' '{{"hostname":"abc.karldns.test","status":"pending","update_url":"https://karldns.test/api/v1/router/update/update-secret?myip=<ipaddr>&myipv6=<ip6addr>","dashboard_url":"https://karldns.test/m/manage-secret","registration_url":"https://karldns.test/api/v1/router/register/manage-secret"}}'
;;
*'/api/v1/router/register/manage-secret'*)
printf '%s\\n' 'good registered'
;;
*)
printf 'unexpected curl call: %s\\n' "$*" >&2
exit 1
;;
esac
""",
encoding="utf-8",
)
crontab.write_text(
f"""#!/bin/sh
if [ "$1" = "-l" ]; then
cat {crontab_file} 2>/dev/null || exit 1
exit 0
fi
cp "$1" {crontab_file}
""",
encoding="utf-8",
)
curl.chmod(0o700)
crontab.chmod(0o700)
env = {
"HOME": str(home_dir),
"PATH": f"{bin_dir}{os.pathsep}{os.environ['PATH']}",
"KARLDNS_BASE_URL": "https://karldns.test",
"KARLDNS_RAW_BASE_URL": "https://raw.karldns.test",
}
for _ in range(2):
run_script(
"sh",
str(ROOT / "scripts" / "install-linux.sh"),
"--mode",
"user",
"--no-run",
env=env,
)
cron_text = crontab_file.read_text(encoding="utf-8")
log = log_file.read_text(encoding="utf-8")
assert cron_text.count("# karldns-update start") == 1
assert "KARLDNS_CONFIG=" in cron_text
assert "karldns-update" in cron_text
assert log.count("/api/v1/hostnames") == 1
assert log.count("/api/v1/router/register/manage-secret") == 1
assert (home_dir / ".local" / "bin" / "karldns-update").exists()
run_script("sh", str(ROOT / "scripts" / "uninstall-linux.sh"), "--mode", "user", env=env)
assert "# karldns-update start" not in crontab_file.read_text(encoding="utf-8")
assert not (home_dir / ".local" / "bin" / "karldns-update").exists()
assert (home_dir / ".config" / "karldns" / "updater.env").exists()
run_script("sh", str(ROOT / "scripts" / "uninstall-linux.sh"), "--mode", "user", "--delete-config", env=env)
assert not (home_dir / ".config" / "karldns").exists()
assert not (home_dir / ".cache" / "karldns").exists()
def test_hosted_script_routes_redirect_to_github(tmp_path: Path) -> None:
app = make_app(DdnsSettings(database_path=tmp_path / "ddns.sqlite3", admin_password="admin"))
client = TestClient(app)
for path, target in {
"/install.sh": "install-linux.sh",
"/uninstall.sh": "uninstall-linux.sh",
"/karldns-update.sh": "karldns-update.sh",
}.items():
response = client.get(path, follow_redirects=False)
assert response.status_code == 302
assert response.headers["location"].endswith(f"/scripts/{target}")