saving
This commit is contained in:
97
tests/unit/test_auth.py
Normal file
97
tests/unit/test_auth.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""Tests for `openrun.ingest.auth` — the web-friendly, MFA-resumable login flow.
|
||||
|
||||
We stub garth at the `sso.login` / `sso.resume_login` / `save` boundary so the
|
||||
two-step (password → MFA code) state machine is testable without a real Garmin
|
||||
account or network.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import openrun.ingest.auth as auth
|
||||
|
||||
|
||||
class _FakeClient:
|
||||
username = "athlete"
|
||||
oauth1_token = None
|
||||
oauth2_token = None
|
||||
|
||||
|
||||
def _stub_garth(monkeypatch):
|
||||
client = _FakeClient()
|
||||
monkeypatch.setattr(auth.garth, "client", client)
|
||||
monkeypatch.setattr(auth.garth, "save", lambda td: None)
|
||||
return client
|
||||
|
||||
|
||||
# --- token store helpers ---------------------------------------------------
|
||||
|
||||
def test_has_tokens(tmp_path: Path) -> None:
|
||||
assert auth.has_tokens(tmp_path) is False
|
||||
(tmp_path / "oauth1_token.json").write_text("{}")
|
||||
assert auth.has_tokens(tmp_path) is True
|
||||
|
||||
|
||||
def test_resume_no_tokens_is_false(tmp_path: Path, monkeypatch) -> None:
|
||||
called = {"n": 0}
|
||||
monkeypatch.setattr(auth.garth, "resume", lambda td: called.__setitem__("n", called["n"] + 1))
|
||||
assert auth.resume(tmp_path) is False
|
||||
assert called["n"] == 0 # short-circuits before touching garth
|
||||
|
||||
|
||||
def test_resume_with_tokens(tmp_path: Path, monkeypatch) -> None:
|
||||
(tmp_path / "oauth1_token.json").write_text("{}")
|
||||
monkeypatch.setattr(auth.garth, "resume", lambda td: None)
|
||||
assert auth.resume(tmp_path) is True
|
||||
|
||||
|
||||
def test_resume_swallows_bad_tokens(tmp_path: Path, monkeypatch) -> None:
|
||||
(tmp_path / "oauth1_token.json").write_text("garbage")
|
||||
|
||||
def _boom(td):
|
||||
raise ValueError("corrupt")
|
||||
|
||||
monkeypatch.setattr(auth.garth, "resume", _boom)
|
||||
assert auth.resume(tmp_path) is False
|
||||
|
||||
|
||||
# --- login state machine ---------------------------------------------------
|
||||
|
||||
def test_begin_login_no_mfa(tmp_path: Path, monkeypatch) -> None:
|
||||
client = _stub_garth(monkeypatch)
|
||||
monkeypatch.setattr(auth.garth.sso, "login", lambda *a, **k: ("oauth1", "oauth2"))
|
||||
|
||||
kind, payload = auth.begin_login("e@x.com", "pw", tmp_path)
|
||||
|
||||
assert kind == "ok"
|
||||
assert payload == "athlete"
|
||||
assert client.oauth1_token == "oauth1" and client.oauth2_token == "oauth2"
|
||||
|
||||
|
||||
def test_begin_login_needs_mfa(tmp_path: Path, monkeypatch) -> None:
|
||||
_stub_garth(monkeypatch)
|
||||
state = {"session": "opaque"}
|
||||
monkeypatch.setattr(auth.garth.sso, "login", lambda *a, **k: ("needs_mfa", state))
|
||||
|
||||
kind, payload = auth.begin_login("e@x.com", "pw", tmp_path)
|
||||
|
||||
assert kind == "needs_mfa"
|
||||
assert payload is state
|
||||
|
||||
|
||||
def test_complete_mfa(tmp_path: Path, monkeypatch) -> None:
|
||||
client = _stub_garth(monkeypatch)
|
||||
seen = {}
|
||||
|
||||
def _resume(state, code):
|
||||
seen["state"], seen["code"] = state, code
|
||||
return ("o1", "o2")
|
||||
|
||||
monkeypatch.setattr(auth.garth.sso, "resume_login", _resume)
|
||||
|
||||
user = auth.complete_mfa({"s": 1}, " 123456 ", tmp_path)
|
||||
|
||||
assert user == "athlete"
|
||||
assert seen["code"] == "123456" # trimmed
|
||||
assert client.oauth1_token == "o1" and client.oauth2_token == "o2"
|
||||
Reference in New Issue
Block a user