summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_startup_scan_enrichment.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/tests/test_startup_scan_enrichment.py')
-rw-r--r--packages/meshbay-node/tests/test_startup_scan_enrichment.py92
1 files changed, 92 insertions, 0 deletions
diff --git a/packages/meshbay-node/tests/test_startup_scan_enrichment.py b/packages/meshbay-node/tests/test_startup_scan_enrichment.py
new file mode 100644
index 0000000..cdadad9
--- /dev/null
+++ b/packages/meshbay-node/tests/test_startup_scan_enrichment.py
@@ -0,0 +1,92 @@
+"""
+Regression: indexer.initial_scan() (run once at startup, daemon.py's
+_bg_scan) never itself calls on_change — that predates the Videos app, and
+every existing caller only cared about the scan finishing, not about
+notifying anyone. Enrichment (duration/thumb_hash/display_title/...) hangs
+entirely off on_change (daemon._broadcast_index_change).
+
+Without an explicit call to _on_index_change right after the startup scan,
+a file already on disk at boot — the common case, an existing library —
+would never get enriched at all: only a file added later, while the node
+is already running (seen by the watchdog), would trigger it. Found live
+against a real library after the first restart with this feature enabled.
+
+Enrichment only runs once a group has a video_root configured (a group
+with none set gets no TMDB/thumbnail work at all, docs/mediacenter.md
+§5.2/§10) — this test's fake roster reports the shared root itself as the
+configured video_root, so the enrichment-scheduling behaviour under test
+is exercised the same way a real operator's group would be.
+"""
+
+import asyncio
+
+import pytest
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
+from unittest.mock import MagicMock
+
+from meshbay_common.crypto import generate_gek
+from meshbay_node.config import Config, HubConfig, NodeConfig, GroupConfig, KeystoreConfig
+from meshbay_node.daemon import NodeDaemon
+from meshbay_node.indexer import DirectoryIndexer
+from meshbay_node.indexer.enrich import Enricher
+from meshbay_node.media_cache import MediaCache
+
+from conftest import one_root
+
+pytestmark = pytest.mark.asyncio
+
+
+def _free_port() -> int:
+ import socket
+ with socket.socket() as s:
+ s.bind(("127.0.0.1", 0))
+ return s.getsockname()[1]
+
+
+async def test_a_file_already_on_disk_at_startup_gets_enrichment_scheduled(tmp_path):
+ shared = tmp_path / "shared"
+ shared.mkdir()
+ (shared / "movie.mkv").write_bytes(b"not a real video, just needs to be indexed as one")
+
+ config = Config(
+ hub=HubConfig(url="http://localhost:9999", username="testuser"),
+ node=NodeConfig(quic_port=_free_port(), ui_port=_free_port()),
+ groups=[GroupConfig(
+ id="a" * 32, name="test-group", shared_dir=str(shared),
+ visibility="private", quic_port=29012,
+ )],
+ keystore=KeystoreConfig(path=tmp_path / "keystore.enc"),
+ data_dir=tmp_path / "data",
+ )
+ class _StubRoster:
+ async def video_root(self, group_id):
+ return "shared" # the root itself, i.e. "enrich the whole thing"
+
+ daemon = NodeDaemon(config)
+ daemon._broadcast_coalesce_secs = 0.01 # real value would make this test wait 0.5s
+ daemon._media_cache = MediaCache(db_path=tmp_path / "media_cache.db")
+ await daemon._media_cache.open()
+ daemon._enricher = Enricher(daemon._media_cache)
+ daemon._roster = _StubRoster()
+
+ sk_node = Ed25519PrivateKey.generate()
+ indexer = DirectoryIndexer(
+ roots=one_root(shared), group_id="a" * 32,
+ sk_node=sk_node, gek=generate_gek())
+
+ # Mirrors _bg_scan's actual sequence in daemon.py.
+ await indexer.initial_scan()
+ assert not daemon._enriched_attempted, (
+ "nothing should be scheduled before _on_index_change is ever called")
+
+ await daemon._on_index_change(indexer)
+ await asyncio.sleep(0.05) # let the coalescing timer fire _broadcast_index_change
+
+ entry = next(iter(indexer.index.entries))
+ assert entry.id in daemon._enriched_attempted, (
+ "a file already on disk at startup must get enrichment scheduled the "
+ "first time its group's index is broadcast, not only on a later "
+ "watchdog-detected change to it")
+
+ await daemon._media_cache.close()