"""APK distribution: /apk page, /apk/info, the artifact, and its QR. These are deliberately unauthenticated — a new member opens /apk before holding a token, exactly like /app. """ from __future__ import annotations import json def test_apk_page_is_public(client): r = client.get("/apk") assert r.status_code == 200, r.text assert "text/html" in r.headers["content-type"] body = r.text assert "/apk/dsh-sync.apk" in body assert "/apk/info" in body assert "bash android/build.sh" in body def test_apk_page_linked_from_app_shell(client): r = client.get("/app") assert 'href="/apk"' in r.text def test_apk_info_reports_unavailable_without_build(client, monkeypatch): """An unbuilt server must say so instead of 500ing or offering a dead link.""" from dsh_sync import storage from dsh_sync.main import create_app monkeypatch.setattr(storage, "apk_path", lambda cfg: cfg.data_dir / "dist" / "missing.apk") import dsh_sync.routers.pages as pages monkeypatch.setattr(pages, "apk_path", lambda cfg: cfg.data_dir / "dist" / "missing.apk") r = client.get("/apk/info") assert r.status_code == 200 data = r.json() assert data["available"] is False assert "build.sh" in data["build_hint"] def test_apk_download_404_when_not_built(client, monkeypatch): import dsh_sync.routers.pages as pages monkeypatch.setattr(pages, "apk_path", lambda cfg: cfg.data_dir / "dist" / "missing.apk") r = client.get("/apk/dsh-sync.apk") assert r.status_code == 404 assert "not been built" in r.json()["detail"] def test_apk_download_serves_artifact(client, monkeypatch): import dsh_sync.routers.pages as pages payload = b"PK\x03\x04fake-apk-bytes" real_path = pages.apk_path(client.app.state.cfg) real_path.parent.mkdir(parents=True, exist_ok=True) real_path.write_bytes(payload) monkeypatch.setattr(pages, "apk_meta_path", lambda cfg: cfg.data_dir / "dist" / "nope.json") r = client.get("/apk/dsh-sync.apk") assert r.status_code == 200 assert r.content == payload assert r.headers["content-type"] == "application/vnd.android.package-archive" assert "dsh-sync.apk" in r.headers["content-disposition"] def test_apk_info_merges_sidecar_metadata(client, monkeypatch): import dsh_sync.routers.pages as pages apk = pages.apk_path(client.app.state.cfg) apk.parent.mkdir(parents=True, exist_ok=True) apk.write_bytes(b"x" * 32) pages.apk_meta_path(client.app.state.cfg).write_text( json.dumps({"version": "9.9.9", "sha256": "deadbeef", "application_id": "com.dsh.sync"}), encoding="utf-8", ) data = client.get("/apk/info").json() assert data["available"] is True assert data["size_bytes"] == 32 assert data["version"] == "9.9.9" assert data["sha256"] == "deadbeef" def test_apk_info_survives_corrupt_sidecar(client): """A broken sidecar must not take the download down with it.""" import dsh_sync.routers.pages as pages apk = pages.apk_path(client.app.state.cfg) apk.parent.mkdir(parents=True, exist_ok=True) apk.write_bytes(b"y" * 16) pages.apk_meta_path(client.app.state.cfg).write_text("{not json", encoding="utf-8") data = client.get("/apk/info").json() assert data["available"] is True assert data["size_bytes"] == 16 assert "version" not in data def test_apk_qr_is_svg(client): r = client.get("/apk/qr") assert r.status_code == 200 assert r.headers["content-type"].startswith("image/svg+xml") assert "