mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 21:27:14 +01:00
Some checks are pending
crdt-sync pre-commit / pre-commit (push) Waiting to run
crdt-sync tests / test (push) Waiting to run
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
187 lines
5.7 KiB
Python
187 lines
5.7 KiB
Python
# Copyright (c) 2026 Krzysztof Rudnicki
|
|
"""Tests for ``tool/desktop_client``: picking up and validating the client JSON.
|
|
|
|
The Android client JSON Google offers for download has the same top-level
|
|
``installed`` key and project as a Desktop one, so the negative fixture here is
|
|
that exact shape: it must be refused, or the wrong client gets stored.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import stat
|
|
from typing import TYPE_CHECKING
|
|
|
|
import pytest
|
|
|
|
from tool import desktop_client as dc
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
_CLIENT_ID = "1-abc.apps.googleusercontent.com"
|
|
_HELD = "the-desktop-client-credential"
|
|
|
|
|
|
def _installed(**overrides: object) -> dict[str, object]:
|
|
body: dict[str, object] = {
|
|
"client_id": _CLIENT_ID,
|
|
"project_id": "kuhy-syncs",
|
|
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
"token_uri": "https://oauth2.googleapis.com/token",
|
|
"client_secret": _HELD,
|
|
"redirect_uris": ["http://localhost"],
|
|
}
|
|
body.update(overrides)
|
|
return body
|
|
|
|
|
|
def _text(**overrides: object) -> str:
|
|
return json.dumps({"installed": _installed(**overrides)})
|
|
|
|
|
|
def _android_text() -> str:
|
|
body = _installed()
|
|
del body["client_secret"]
|
|
del body["redirect_uris"]
|
|
return json.dumps({"installed": body})
|
|
|
|
|
|
def test_valid_desktop_json_parses() -> None:
|
|
client = dc.parse_client_json(_text())
|
|
assert client == dc.DesktopClient(_CLIENT_ID, _HELD, "kuhy-syncs")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("text", "needle"),
|
|
[
|
|
("{not json", "not valid JSON"),
|
|
("[]", "JSON object"),
|
|
(json.dumps({"web": _installed()}), "'installed'"),
|
|
(_text(project_id="other"), "other"),
|
|
(_text(client_id="plain"), "client_id"),
|
|
(_android_text(), "client_secret"),
|
|
(_text(redirect_uris=["https://x.example"]), "http://localhost"),
|
|
(json.dumps({"installed": "nope"}), "'installed'"),
|
|
],
|
|
)
|
|
def test_invalid_json_is_refused_with_a_reason(text: str, needle: str) -> None:
|
|
with pytest.raises(dc.ClientFileError, match=needle):
|
|
dc.parse_client_json(text)
|
|
|
|
|
|
def _touch(directory: Path, name: str, text: str, mtime: float) -> Path:
|
|
path = directory / name
|
|
path.write_text(text, encoding="utf-8")
|
|
os.utime(path, (mtime, mtime))
|
|
return path
|
|
|
|
|
|
def _wait(directory: Path, since: float, skipped: list[str]) -> dc.Pickup | None:
|
|
return dc.wait_for_client_json(
|
|
directory, since, attempts=3, pause=lambda: None, report=skipped.append
|
|
)
|
|
|
|
|
|
def test_pickup_ignores_old_files_and_the_android_json(tmp_path: Path) -> None:
|
|
_touch(tmp_path, "client_secret_old.json", _text(), 50.0)
|
|
_touch(tmp_path, "client_secret_android.json", _android_text(), 150.0)
|
|
_touch(tmp_path, "notes.json", _text(), 150.0)
|
|
skipped: list[str] = []
|
|
assert _wait(tmp_path, 100.0, skipped) is None
|
|
assert len(skipped) == 1
|
|
assert "client_secret_android.json" in skipped[0]
|
|
assert "client_secret" in skipped[0]
|
|
|
|
|
|
def test_pickup_takes_the_newest_valid_file(tmp_path: Path) -> None:
|
|
_touch(
|
|
tmp_path,
|
|
"client_secret_a.json",
|
|
_text(client_id="1-a.apps.googleusercontent.com"),
|
|
120.0,
|
|
)
|
|
newest = _touch(tmp_path, "client_secret_b.json", _text(), 130.0)
|
|
found = _wait(tmp_path, 100.0, [])
|
|
assert found is not None
|
|
assert found.path == newest
|
|
assert found.client.client_id == _CLIENT_ID
|
|
|
|
|
|
def test_pickup_waits_for_the_file_to_appear(tmp_path: Path) -> None:
|
|
def pause() -> None:
|
|
_touch(tmp_path, "client_secret_late.json", _text(), 500.0)
|
|
|
|
found = dc.wait_for_client_json(tmp_path, 100.0, attempts=3, pause=pause)
|
|
assert found is not None
|
|
|
|
|
|
def test_pickup_pauses_between_attempts_only(tmp_path: Path) -> None:
|
|
pauses: list[int] = []
|
|
result = dc.wait_for_client_json(
|
|
tmp_path, 0.0, attempts=3, pause=lambda: pauses.append(1)
|
|
)
|
|
assert result is None
|
|
assert len(pauses) == 2
|
|
|
|
|
|
def test_store_client_is_private_and_consumes_the_download(tmp_path: Path) -> None:
|
|
source = _touch(tmp_path, "client_secret_x.json", _text(), 1.0)
|
|
dest = tmp_path / "conf" / "desktop_client.json"
|
|
dc.store_client(source, dest)
|
|
assert stat.S_IMODE(dest.stat().st_mode) == 0o600
|
|
assert dc.load_client(dest).client_id == _CLIENT_ID
|
|
assert not source.exists()
|
|
|
|
|
|
def test_store_client_overwrites_and_tightens_an_existing_file(tmp_path: Path) -> None:
|
|
dest = tmp_path / "desktop_client.json"
|
|
dest.write_text("old", encoding="utf-8")
|
|
dest.chmod(0o644)
|
|
dc.store_client(_touch(tmp_path, "client_secret_y.json", _text(), 1.0), dest)
|
|
assert stat.S_IMODE(dest.stat().st_mode) == 0o600
|
|
assert dest.read_text(encoding="utf-8") == _text()
|
|
|
|
|
|
def test_load_client_points_at_the_register_script_when_missing(tmp_path: Path) -> None:
|
|
with pytest.raises(dc.ClientFileError, match=r"register_desktop_client\.sh"):
|
|
dc.load_client(tmp_path / "absent.json")
|
|
|
|
|
|
def test_main_pickup_stores_the_client(tmp_path: Path) -> None:
|
|
_touch(tmp_path, "client_secret_z.json", _text(), 200.0)
|
|
dest = tmp_path / "out" / "desktop_client.json"
|
|
code = dc.main(
|
|
[
|
|
"pickup",
|
|
"--since",
|
|
"100",
|
|
"--timeout",
|
|
"5",
|
|
"--downloads",
|
|
str(tmp_path),
|
|
"--dest",
|
|
str(dest),
|
|
]
|
|
)
|
|
assert code == 0
|
|
assert dc.load_client(dest).project_id == "kuhy-syncs"
|
|
|
|
|
|
def test_main_pickup_times_out(tmp_path: Path) -> None:
|
|
code = dc.main(
|
|
[
|
|
"pickup",
|
|
"--since",
|
|
"100",
|
|
"--timeout",
|
|
"0",
|
|
"--downloads",
|
|
str(tmp_path),
|
|
"--dest",
|
|
str(tmp_path / "d.json"),
|
|
]
|
|
)
|
|
assert code == 1
|