feat: resolver cache layer
This commit is contained in:
parent
08db8b8fb1
commit
7758eb21fa
38
src/teampulse/resolver.py
Normal file
38
src/teampulse/resolver.py
Normal file
@ -0,0 +1,38 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from playwright.sync_api import Page
|
||||
|
||||
|
||||
class Resolver:
|
||||
def __init__(self, cache_path: Path, page: Page):
|
||||
self._cache_path = cache_path
|
||||
self._page = page
|
||||
self._cache: dict[str, str] = self._load_cache()
|
||||
|
||||
def resolve(self, display_name: str) -> str:
|
||||
if display_name in self._cache:
|
||||
return self._cache[display_name]
|
||||
|
||||
email = self._extract_email_from_profile(display_name)
|
||||
if email is None:
|
||||
return "<nicht auflösbar>"
|
||||
|
||||
self._cache[display_name] = email
|
||||
self._save_cache()
|
||||
return email
|
||||
|
||||
def _load_cache(self) -> dict[str, str]:
|
||||
if not self._cache_path.exists():
|
||||
return {}
|
||||
return json.loads(self._cache_path.read_text(encoding="utf-8"))
|
||||
|
||||
def _save_cache(self) -> None:
|
||||
self._cache_path.write_text(
|
||||
json.dumps(self._cache, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
def _extract_email_from_profile(self, display_name: str) -> str | None:
|
||||
# Implemented in Task 8 — browser interaction with Teams profile cards
|
||||
raise NotImplementedError
|
||||
69
tests/test_resolver_cache.py
Normal file
69
tests/test_resolver_cache.py
Normal file
@ -0,0 +1,69 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from teampulse.resolver import Resolver
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cache_path(tmp_path):
|
||||
return tmp_path / "cache.json"
|
||||
|
||||
|
||||
def test_resolve_returns_email_from_cache(cache_path):
|
||||
cache_path.write_text(json.dumps({"Max Mustermann": "m.mustermann@company.com"}))
|
||||
resolver = Resolver(cache_path=cache_path, page=MagicMock())
|
||||
assert resolver.resolve("Max Mustermann") == "m.mustermann@company.com"
|
||||
|
||||
|
||||
def test_resolve_unknown_name_calls_browser(cache_path):
|
||||
cache_path.write_text(json.dumps({}))
|
||||
mock_page = MagicMock()
|
||||
resolver = Resolver(cache_path=cache_path, page=mock_page)
|
||||
resolver._extract_email_from_profile = MagicMock(return_value="k.huber@company.com")
|
||||
|
||||
result = resolver.resolve("Klaus Huber")
|
||||
|
||||
resolver._extract_email_from_profile.assert_called_once_with("Klaus Huber")
|
||||
assert result == "k.huber@company.com"
|
||||
|
||||
|
||||
def test_resolve_caches_newly_resolved_email(cache_path):
|
||||
cache_path.write_text(json.dumps({}))
|
||||
resolver = Resolver(cache_path=cache_path, page=MagicMock())
|
||||
resolver._extract_email_from_profile = MagicMock(return_value="k.huber@company.com")
|
||||
|
||||
resolver.resolve("Klaus Huber")
|
||||
|
||||
saved = json.loads(cache_path.read_text())
|
||||
assert saved["Klaus Huber"] == "k.huber@company.com"
|
||||
|
||||
|
||||
def test_resolve_unresolvable_returns_placeholder(cache_path):
|
||||
cache_path.write_text(json.dumps({}))
|
||||
resolver = Resolver(cache_path=cache_path, page=MagicMock())
|
||||
resolver._extract_email_from_profile = MagicMock(return_value=None)
|
||||
|
||||
result = resolver.resolve("Unknown Person")
|
||||
assert result == "<nicht auflösbar>"
|
||||
|
||||
|
||||
def test_resolve_does_not_cache_unresolvable(cache_path):
|
||||
cache_path.write_text(json.dumps({}))
|
||||
resolver = Resolver(cache_path=cache_path, page=MagicMock())
|
||||
resolver._extract_email_from_profile = MagicMock(return_value=None)
|
||||
|
||||
resolver.resolve("Unknown Person")
|
||||
|
||||
saved = json.loads(cache_path.read_text())
|
||||
assert "Unknown Person" not in saved
|
||||
|
||||
|
||||
def test_creates_cache_file_if_missing(tmp_path):
|
||||
cache_path = tmp_path / "nonexistent.json"
|
||||
resolver = Resolver(cache_path=cache_path, page=MagicMock())
|
||||
resolver._extract_email_from_profile = MagicMock(return_value="a@b.com")
|
||||
resolver.resolve("Test User")
|
||||
assert cache_path.exists()
|
||||
Loading…
x
Reference in New Issue
Block a user