diff options
Diffstat (limited to 'packages/meshbay-node/tests/test_startup_scan_enrichment.py')
| -rw-r--r-- | packages/meshbay-node/tests/test_startup_scan_enrichment.py | 92 |
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() |