import json import pytest from thoughtsync.app import create_app from thoughtsync.client_dist import APK_NAME, MANIFEST_NAME, advertisement, android_release from thoughtsync.config import Config # DB-free, like the rest of this suite — the test lane runs no Postgres. That is # why the advertisement is asserted through `advertisement()` rather than through # `/api/config`: the route is a one-line merge of this dict into a payload whose # other half needs a database, and testing it here tests the part that can be wrong. # # The two routes below ARE exercised, because neither opens a session: the metadata # route only stats files, and the download's 401 is returned before any token # lookup. PAYLOAD = b"not really an apk, but the server only ever stats it" @pytest.fixture def app(): return create_app() def place_client(payload: bytes = PAYLOAD, **overrides) -> dict: """Put a client + sidecar where the server looks. Overrides corrupt the pair.""" root = Config.client_root() root.mkdir(parents=True, exist_ok=True) (root / APK_NAME).write_bytes(payload) meta = { "version_name": "0.1.216", "version_code": 216, "size": len(payload), "sha256": "ab" * 32, } meta.update(overrides) (root / MANIFEST_NAME).write_text(json.dumps(meta), encoding="utf-8") return meta def test_absent_client_is_advertised_as_nothing_at_all(): """The KEY is missing, not null. A client testing for it then gets one unambiguous answer rather than having to tell "this server has no APK" apart from "this server predates the feature". """ assert android_release() is None assert advertisement() == {} def test_a_present_client_is_advertised_with_what_android_compares(): place_client() advertised = advertisement()["android_client"] assert advertised["version"] == "0.1.216" # The integer is what decides "is this newer", not the name — a name is a # string and sorts like one. assert advertised["version_code"] == 216 assert advertised["size"] == len(PAYLOAD) assert advertised["url"].endswith("/download") def test_a_sidecar_describing_a_different_build_counts_as_no_client(): """The likeliest real corruption: a new APK copied over an old sidecar. Serving one build while advertising another is worse than serving none — the phone would compare versions against a promise the bytes do not keep. """ place_client(size=999_999) assert android_release() is None assert advertisement() == {} def test_an_unreadable_sidecar_counts_as_no_client(): place_client() (Config.client_root() / MANIFEST_NAME).write_text("{ this is not json", encoding="utf-8") assert android_release() is None def test_a_sidecar_missing_a_field_counts_as_no_client(): root = Config.client_root() root.mkdir(parents=True, exist_ok=True) (root / APK_NAME).write_bytes(PAYLOAD) (root / MANIFEST_NAME).write_text(json.dumps({"version_name": "0.1.216"}), encoding="utf-8") assert android_release() is None def test_a_sidecar_with_no_apk_beside_it_counts_as_no_client(): root = Config.client_root() root.mkdir(parents=True, exist_ok=True) (root / MANIFEST_NAME).write_text(json.dumps({"version_name": "x", "version_code": 1, "size": 1, "sha256": ""})) assert android_release() is None async def test_metadata_endpoint_is_public_so_an_updater_can_ask_cheaply(app): place_client() resp = await app.test_client().get("/api/client/android") assert resp.status_code == 200 assert (await resp.get_json())["version_code"] == 216 async def test_metadata_404s_rather_than_describing_a_client_that_is_not_there(app): resp = await app.test_client().get("/api/client/android") assert resp.status_code == 404 async def test_the_bytes_need_authentication_even_though_the_version_does_not(app): """Anyone who can reach the port may ask what version exists; only an account or a linked device may pull the 55 MiB.""" place_client() resp = await app.test_client().get("/api/client/android/download") assert resp.status_code == 401