aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/tests')
-rw-r--r--packages/meshbay-node/tests/test_media_meta_request.py3
-rw-r--r--packages/meshbay-node/tests/test_season_and_search_requests.py5
-rw-r--r--packages/meshbay-node/tests/test_tmdb_language_change_clears_cache.py80
-rw-r--r--packages/meshbay-node/tests/test_tmdb_language_gate.py97
4 files changed, 184 insertions, 1 deletions
diff --git a/packages/meshbay-node/tests/test_media_meta_request.py b/packages/meshbay-node/tests/test_media_meta_request.py
index 9a1c5aa..0392845 100644
--- a/packages/meshbay-node/tests/test_media_meta_request.py
+++ b/packages/meshbay-node/tests/test_media_meta_request.py
@@ -64,6 +64,9 @@ def _session(index, media_cache, tmdb_client):
"media_cache": media_cache,
"tmdb_client": tmdb_client,
"tmdb_enabled": True,
+ # A configured node: the language gate (§9.7) holds every TMDB fetch
+ # until a language is chosen, so these routing tests must set one.
+ "daemon_state": {"tmdb_language": "en-US"},
}
session._group_id = None
session.sent = []
diff --git a/packages/meshbay-node/tests/test_season_and_search_requests.py b/packages/meshbay-node/tests/test_season_and_search_requests.py
index 7e81e63..bec7df9 100644
--- a/packages/meshbay-node/tests/test_season_and_search_requests.py
+++ b/packages/meshbay-node/tests/test_season_and_search_requests.py
@@ -19,7 +19,10 @@ pytestmark = pytest.mark.asyncio
def _session(media_cache=None, tmdb_client=None) -> WebRTCPeerSession:
session = WebRTCPeerSession.__new__(WebRTCPeerSession)
- session._ctx = {"media_cache": media_cache, "tmdb_client": tmdb_client}
+ # daemon_state carries a configured language: the gate (§9.7) holds every
+ # TMDB fetch until one is set, so these fetch/search tests must set one.
+ session._ctx = {"media_cache": media_cache, "tmdb_client": tmdb_client,
+ "daemon_state": {"tmdb_language": "en-US"}}
session._group_id = None
# Set because production always has one: `_dispatch_message` refuses every
# message until the handshake settles `_user_id`, so a session reaching any
diff --git a/packages/meshbay-node/tests/test_tmdb_language_change_clears_cache.py b/packages/meshbay-node/tests/test_tmdb_language_change_clears_cache.py
new file mode 100644
index 0000000..cf1ecdc
--- /dev/null
+++ b/packages/meshbay-node/tests/test_tmdb_language_change_clears_cache.py
@@ -0,0 +1,80 @@
+"""
+Changing the node's TMDB language wipes the cached fiches (`ops.set_tmdb_config`).
+
+The cache is keyed by TMDB id alone and records no language, so a fiche fetched
+under the old language would be served for its whole 30-day TTL. An operator who
+sets the language *after* browsing the library once — the ordinary order, since
+browsing is what triggers the lazy fetch — would keep seeing the old language
+otherwise (found live: a whole library indexed in English before "Français" was
+chosen, 2026-09-26). Only the language change clears; a no-op re-set or a
+token-only change must leave the cache alone, or every unrelated settings save
+would throw the library's metadata away.
+"""
+
+import pytest
+from meshbay_node import ops
+from meshbay_node.media_cache import MediaCache
+from meshbay_node.roster import Roster
+
+pytestmark = pytest.mark.asyncio
+
+
+async def _state(tmp_path):
+ roster = Roster(db_path=tmp_path / "roster.db")
+ await roster.open()
+ cache = MediaCache(db_path=tmp_path / "media_cache.db")
+ await cache.open()
+ state = {"roster": roster, "media_cache": cache, "node_user_id": "operator"}
+ return state, roster, cache
+
+
+async def _seed(cache):
+ await cache.set_tmdb_meta("1668", "tv", {"name": "Friends"})
+ await cache.set_season_meta("1668", 1, {"overview": "Season one"})
+
+
+async def test_changing_language_clears_the_metadata_cache(tmp_path):
+ state, roster, cache = await _state(tmp_path)
+ try:
+ await ops.set_tmdb_config(state, language="") # start at default/English
+ await _seed(cache)
+
+ await ops.set_tmdb_config(state, language="fr-FR")
+
+ assert await cache.get_tmdb_meta("1668", "tv") is None
+ assert await cache.get_season_meta("1668", 1) is None
+ finally:
+ await roster.close()
+ await cache.close()
+
+
+async def test_re_setting_the_same_language_keeps_the_cache(tmp_path):
+ state, roster, cache = await _state(tmp_path)
+ try:
+ await ops.set_tmdb_config(state, language="fr-FR")
+ await _seed(cache)
+
+ await ops.set_tmdb_config(state, language="fr-FR")
+
+ assert await cache.get_tmdb_meta("1668", "tv") == {"name": "Friends"}
+ assert await cache.get_season_meta("1668", 1) == {"overview": "Season one"}
+ finally:
+ await roster.close()
+ await cache.close()
+
+
+async def test_token_only_change_keeps_the_cache(tmp_path):
+ state, roster, cache = await _state(tmp_path)
+ try:
+ await ops.set_tmdb_config(state, language="fr-FR")
+ await _seed(cache)
+
+ # language=None means "leave the language" — not a language change,
+ # so the fiches stay.
+ await ops.set_tmdb_config(state, token="a-custom-token")
+
+ assert await cache.get_tmdb_meta("1668", "tv") == {"name": "Friends"}
+ assert await cache.get_season_meta("1668", 1) == {"overview": "Season one"}
+ finally:
+ await roster.close()
+ await cache.close()
diff --git a/packages/meshbay-node/tests/test_tmdb_language_gate.py b/packages/meshbay-node/tests/test_tmdb_language_gate.py
new file mode 100644
index 0000000..cd43fef
--- /dev/null
+++ b/packages/meshbay-node/tests/test_tmdb_language_gate.py
@@ -0,0 +1,97 @@
+"""
+The node does not query TMDB until a language is configured (§9.7).
+
+Browsing the Videos tab is what triggers the lazy `media_meta_req`, and it
+routinely happens before the operator has opened the settings and chosen a
+language. Querying then would fetch the whole library in TMDB's English default
+and throw it away the moment a language was picked — double the requests against
+TMDB's rate limit, for a result nobody asked for (found live 2026-09-26: a whole
+library indexed in English before "Français" was chosen). So an unset language
+answers confidence 0 and makes no call; a set language fetches once, in it.
+"""
+
+import pytest
+from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
+from meshbay_common.protocol import IndexEntry
+from meshbay_node.indexer.group_index import GroupIndex
+from meshbay_node.media_cache import MediaCache
+from meshbay_node.transport.webrtc_server import WebRTCPeerSession
+
+pytestmark = pytest.mark.asyncio
+
+
+class CountingTmdbClient:
+ def __init__(self):
+ self.calls = 0
+
+ async def search_movie(self, title, year=None):
+ self.calls += 1
+ return {"id": 42, "title": title, "release_date": "2001-01-01"}, 1.0
+
+ async def search_tv(self, title, year=None):
+ self.calls += 1
+ return {"id": 43, "name": title, "first_air_date": "2001-01-01"}, 1.0
+
+ async def movie_details(self, tmdb_id, language=None):
+ self.calls += 1
+ return {"title": "A Film", "genres": [{"name": "Drama"}],
+ "poster_path": "/p.jpg", "overview": "x"}
+
+ async def movie_credits(self, tmdb_id):
+ return {"cast": [], "crew": []}
+
+ async def fetch_image(self, url):
+ return b"img"
+
+ @staticmethod
+ def poster_url(path):
+ return f"https://image.tmdb.org/t/p/w500{path}"
+
+
+@pytest.fixture
+async def media_cache(tmp_path):
+ c = MediaCache(db_path=tmp_path / "media_cache.db")
+ await c.open()
+ yield c
+ await c.close()
+
+
+def _session(media_cache, tmdb_client, language):
+ index = GroupIndex(group_id="g" * 32, sk_node=Ed25519PrivateKey.generate())
+ entry = IndexEntry(id="f1", name="Some.Film.2001.mkv", path="movies", size=1,
+ type="video", added_at=0, display_title="Some Film")
+ index.add_entry(entry)
+ session = WebRTCPeerSession.__new__(WebRTCPeerSession)
+ session._ctx = {
+ "index": index,
+ "media_cache": media_cache,
+ "tmdb_client": tmdb_client,
+ "tmdb_enabled": True,
+ "daemon_state": {"tmdb_language": language},
+ }
+ session._group_id = None
+ session.sent = []
+ session._send = session.sent.append
+ return session, entry
+
+
+async def test_no_language_makes_no_tmdb_call(media_cache):
+ client = CountingTmdbClient()
+ session, entry = _session(media_cache, client, language="")
+
+ await session._do_media_meta_request({"file_id": entry.id})
+
+ assert client.calls == 0
+ assert session.sent[-1]["confidence"] == 0
+ # Nothing cached, so a later request in a real language still starts clean.
+ assert await media_cache.get_file_tmdb(entry.id) is None
+
+
+async def test_a_configured_language_fetches(media_cache):
+ client = CountingTmdbClient()
+ session, entry = _session(media_cache, client, language="fr-FR")
+
+ await session._do_media_meta_request({"file_id": entry.id})
+
+ assert client.calls > 0
+ assert session.sent[-1].get("tmdb_id") == "42"