"""Unit tests for the Planner HTTP login helpers (token capture + MSAL cache).""" from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent SRC = ROOT / "src" for _p in (str(ROOT), str(SRC)): if _p not in sys.path: sys.path.insert(0, _p) from onedrive_planner_http import _pick_graph_token, _TokenStore # noqa: E402 GRAPH_PLANNER = "https://graph.microsoft.com/beta/planner/plans/TestPlanId?$expand=details" GRAPH_ME = "https://graph.microsoft.com/v1.0/me/photo/$value" NON_GRAPH = "https://planner.cloud.microsoft/webui/api/something" class _Request: def __init__(self, url: str, token: str | None = None) -> None: self.url = url self.headers = {"authorization": f"Bearer {token}"} if token else {} class TestTokenStore: def test_captures_planner_scoped_token(self): store = _TokenStore() store.on_request(_Request(GRAPH_PLANNER, "planner-tok")) assert store.token == "planner-tok" assert store.preferred is True def test_ignores_narrow_graph_token_inside_wait_window(self): store = _TokenStore(fallback_after_s=45) store.on_request(_Request(GRAPH_ME, "narrow-tok")) assert store.token is None # ...and records the URL so a failure is diagnosable from the log assert GRAPH_ME.split("?")[0] in store.graph_urls def test_falls_back_to_any_graph_token_after_window(self): store = _TokenStore(fallback_after_s=0) store.on_request(_Request(GRAPH_ME, "narrow-tok")) assert store.token == "narrow-tok" assert store.preferred is False def test_planner_token_overrides_earlier_fallback(self): store = _TokenStore(fallback_after_s=0) store.on_request(_Request(GRAPH_ME, "narrow-tok")) store.on_request(_Request(GRAPH_PLANNER, "planner-tok")) assert store.token == "planner-tok" assert store.preferred is True def test_fallback_does_not_override_planner_token(self): store = _TokenStore(fallback_after_s=0) store.on_request(_Request(GRAPH_PLANNER, "planner-tok")) store.on_request(_Request(GRAPH_ME, "narrow-tok")) assert store.token == "planner-tok" def test_non_graph_bearer_is_recorded_but_not_used(self): store = _TokenStore(fallback_after_s=0) store.on_request(_Request(NON_GRAPH, "spa-tok")) assert store.token is None assert "planner.cloud.microsoft" in store.bearer_hosts def test_requests_without_authorization_are_ignored(self): store = _TokenStore(fallback_after_s=0) store.on_request(_Request(GRAPH_PLANNER)) assert store.token is None assert store.graph_urls == set() class TestPickGraphToken: def test_picks_entry_with_graph_scopes(self): entries = [ {"target": "openid profile", "secret": "id-tok"}, {"target": "Tasks.Read Group.Read.All", "secret": "graph-tok"}, ] assert _pick_graph_token(entries) == "graph-tok" def test_prefers_the_entry_with_more_wanted_scopes(self): entries = [ {"target": "https://graph.microsoft.com/Tasks.Read", "secret": "one"}, {"target": "Tasks.Read Group.Read.All GroupMember.Read.All", "secret": "many"}, ] assert _pick_graph_token(entries) == "many" def test_returns_none_without_a_graph_entry(self): entries = [{"target": "openid profile offline_access", "secret": "id-tok"}] assert _pick_graph_token(entries) is None def test_skips_entries_with_no_secret(self): entries = [{"target": "Tasks.Read", "secret": ""}] assert _pick_graph_token(entries) is None def test_tolerates_junk_entries(self): assert _pick_graph_token([None, "nonsense", {}, 42]) is None