testsAndMisc/python_pkg/endurain_import/tests/test_remote_dedupe.py
Krzysztof kuhy Rudnicki adf2e402df Grade against 3.14 too, not just run on it
`requires-python` and the CI pin already said 3.14, but ruff, mypy and pylint
were still grading `meta/` against 3.10 -- three tools judging an interpreter
nothing runs any more, and ruff withholding the pyupgrade fixes that only
become safe at the newer target. `pin_ci_python.py` rewrites these three knobs.

What the stale target was hiding: `datetime.timezone.utc` where `datetime.UTC`
is now correct (UP017), and ruff's stable formatter applying PEP 758 --
`except (A, B):` becomes `except A, B:`. That makes the source 3.14-only at the
syntax level, which is what `requires-python = ">=3.14"` already promised; kuhy
chose it deliberately over holding ruff a release back (2026-08-29).

Also drops a comment in `wsg_grabber/models.py` that the bump makes false: it
explained why the file used ``str, Enum`` instead of ``StrEnum`` under mypy at
3.10, sitting directly above a ``class Verdict(StrEnum)`` that had already
stopped obeying it.

ruff, ruff-format, mypy --strict and pylint all clean at the new target.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SfnJmxxsZ534AKk16rC2Gr
2026-08-29 11:50:39 +02:00

153 lines
5.0 KiB
Python

"""Skipping runs Endurain already holds.
RunnerUp's own uploader writes straight to the server and leaves no ledger
trace, so the server is the only place these runs are visible.
"""
from __future__ import annotations
from datetime import UTC, datetime
from pathlib import Path
import pytest
from python_pkg.endurain_import.__main__ import main
from python_pkg.endurain_import.tests.conftest import (
_env,
_file,
_patch_client,
_StubMainClient,
)
from python_pkg.endurain_import.upload import Outcome, Result
# The name encodes 23:51:04 LOCAL time; the UTC instant depends on the zone,
# so these derive it the same way production does rather than hardcoding it.
_RUN_NAME = "RunnerUp_2026-08-22-23-51-04_Running.tcx"
def _run_start() -> datetime:
return datetime(
2026, 8, 22, 23, 51, 4, tzinfo=datetime.now().astimezone().tzinfo
).astimezone(UTC)
def test_run_already_in_endurain_is_skipped_not_reuploaded(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, inbox: Path
) -> None:
"""RunnerUp's own uploader leaves no ledger trace; only the server knows."""
_env(monkeypatch, tmp_path, inbox)
_file(inbox, _RUN_NAME)
client = _StubMainClient(Result(Outcome.OK, 1, "ok"), remote=[_run_start()])
_patch_client(monkeypatch, client)
assert main() == 0
assert client.uploads == 0
assert (inbox / "processed" / _RUN_NAME).exists()
def test_remote_skip_is_recorded_so_the_next_run_needs_no_query(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, inbox: Path
) -> None:
_env(monkeypatch, tmp_path, inbox)
_file(inbox, _RUN_NAME)
_patch_client(
monkeypatch,
_StubMainClient(Result(Outcome.OK, 1, "ok"), remote=[_run_start()]),
)
assert main() == 0
assert _RUN_NAME in (tmp_path / "state" / "ledger.json").read_text()
def test_a_genuinely_new_run_still_uploads(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, inbox: Path
) -> None:
"""The remote check must not become a blanket refusal to upload."""
_env(monkeypatch, tmp_path, inbox)
_file(inbox, _RUN_NAME)
other = datetime(2026, 1, 1, tzinfo=UTC)
client = _StubMainClient(Result(Outcome.OK, 7, "ok"), remote=[other])
_patch_client(monkeypatch, client)
assert main() == 0
assert client.uploads == 1
def test_unavailable_query_falls_back_to_the_ledger(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, inbox: Path
) -> None:
"""A failed lookup must not read as 'nothing exists' -- nor block imports."""
_env(monkeypatch, tmp_path, inbox)
_file(inbox, _RUN_NAME)
client = _StubMainClient(Result(Outcome.OK, 1, "ok"), remote=None)
_patch_client(monkeypatch, client)
assert main() == 0
assert client.uploads == 1
def test_unparseable_filename_falls_back_to_the_ledger(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, inbox: Path
) -> None:
"""No timestamp in the name means the remote check cannot apply."""
_env(monkeypatch, tmp_path, inbox)
_file(inbox, "mystery.tcx")
client = _StubMainClient(Result(Outcome.OK, 1, "ok"), remote=[_run_start()])
_patch_client(monkeypatch, client)
assert main() == 0
assert client.uploads == 1
def test_user_id_is_configurable(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, inbox: Path
) -> None:
_env(monkeypatch, tmp_path, inbox, ENDURAIN_USER_ID="7")
_file(inbox, _RUN_NAME)
seen: list[int] = []
class _Recording(_StubMainClient):
def recent_start_times(self, user_id: int) -> list[datetime] | None:
seen.append(user_id)
return []
_patch_client(monkeypatch, _Recording(Result(Outcome.OK, 1, "ok")))
assert main() == 0
assert seen == [7]
def _patch_database(
monkeypatch: pytest.MonkeyPatch, result: list[datetime] | None
) -> None:
"""Stand in for the postgres reader; never touches a real container."""
from python_pkg.endurain_import import db_lookup
class _StubDatabase:
def recent_start_times(self, _user_id: int) -> list[datetime] | None:
return result
monkeypatch.setattr(db_lookup, "EndurainDatabase", _StubDatabase)
def test_database_fallback_catches_what_the_api_cannot(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, inbox: Path
) -> None:
"""API keys cannot read activities, so postgres answers instead."""
_file(inbox, _RUN_NAME)
client = _StubMainClient(Result(Outcome.OK, 1, "uploaded"), remote=None)
_env(monkeypatch, tmp_path, inbox, ENDURAIN_NO_DB="0")
_patch_client(monkeypatch, client)
_patch_database(monkeypatch, [_run_start()])
assert main() == 0
assert client.uploads == 0
def test_database_failure_falls_back_to_the_ledger(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, inbox: Path
) -> None:
"""Both backends unknown must not block the import."""
_file(inbox, _RUN_NAME)
client = _StubMainClient(Result(Outcome.OK, 1, "uploaded"), remote=None)
_env(monkeypatch, tmp_path, inbox, ENDURAIN_NO_DB="0")
_patch_client(monkeypatch, client)
_patch_database(monkeypatch, None)
assert main() == 0
assert client.uploads == 1