aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_search_video_merge.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests/test_search_video_merge.py')
-rw-r--r--packages/meshbay-hub/tests/test_search_video_merge.py185
1 files changed, 0 insertions, 185 deletions
diff --git a/packages/meshbay-hub/tests/test_search_video_merge.py b/packages/meshbay-hub/tests/test_search_video_merge.py
deleted file mode 100644
index e9ab258..0000000
--- a/packages/meshbay-hub/tests/test_search_video_merge.py
+++ /dev/null
@@ -1,185 +0,0 @@
-"""
-The reported symptom, end to end: one library shared by two groups.
-
-`test_search_source_merge.py` holds the merging rules in isolation. This holds
-the thing an operator actually saw — a node hosting two groups that were given
-the same video directory, and a Search view showing every film as two poster
-cards and every episode twice inside a show.
-
-Three pieces have to agree for that to come out right, and each lives in a
-different file:
-
- * `groupVideoEntries` (video-app.js) turns entries into films and shows;
- * `videoUnits` (search-page.js) turns those into merge units;
- * `mergeUnitEntries` (source-merge.js) folds them on the content hash.
-
-All three are read out of their real sources here rather than restated. The
-pipeline is assembled the way `search-page.js` assembles it, and then the
-result is passed through `groupVideoEntries` a second time — which is what
-`VideoApp` does with it — so what this counts is what the grid renders.
-
-See docs/refactoring-search.md.
-"""
-
-import json
-import re
-import shutil
-import subprocess
-from pathlib import Path
-
-import pytest
-
-STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static"
-VIDEO_APP = STATIC / "video-app.js"
-SEARCH_PAGE = STATIC / "search-page.js"
-MERGE = STATIC / "source-merge.js"
-
-pytestmark = pytest.mark.skipif(
- shutil.which("node") is None or not MERGE.exists(),
- reason="node or the SPA sources are not available")
-
-EXPORT = re.compile(r"^export \{[^}]*\};?\s*$", re.M)
-
-
-def _block(path, header):
- """One top-level `function name(...) {` ... `}` read out of a module."""
- src = path.read_text()
- m = re.search(r"^" + re.escape(header) + r".*?^\}", src, re.M | re.S)
- assert m, (
- f"{header} is no longer where this test reads it from in {path.name} — "
- "the Search view's de-duplication is untested until this is fixed")
- return m.group(0)
-
-
-@pytest.fixture(scope="module")
-def pipeline():
- root = re.search(r"^const SEARCH_VIDEO_ROOT = .*?;$", SEARCH_PAGE.read_text(), re.M)
- assert root, "SEARCH_VIDEO_ROOT moved — videoUnits cannot be lifted"
- return "\n".join([
- EXPORT.sub("", MERGE.read_text()),
- _block(VIDEO_APP, "function underVideoRoot(entry, videoRoot) {"),
- _block(VIDEO_APP, "function buildSeasons(episodes) {"),
- _block(VIDEO_APP, "function groupVideoEntries(entries, videoRoot) {"),
- root.group(0),
- _block(SEARCH_PAGE, "function videoUnits(entries) {"),
- ])
-
-
-def _grid(tmp_path, pipeline, entries, salt="reader", local=()):
- """What the poster grid ends up with, after the merge and VideoApp's own
- regrouping."""
- body = f"""
- const raw = {json.dumps(entries)};
- const local = new Set({json.dumps(list(local))});
- const merged = mergeUnitEntries(videoUnits(raw), {{
- salt: {json.dumps(salt)},
- isLocal: (g) => local.has(g),
- }});
- const {{ movies, shows }} = groupVideoEntries(merged, SEARCH_VIDEO_ROOT);
- console.log(JSON.stringify({{
- movies: movies.map((e) => ({{
- id: e.id, title: e.display_title || e.name, groupId: e.groupId,
- sources: e._sources.length,
- }})),
- shows: shows.map((s) => ({{
- title: s.title,
- groups: [...new Set(s.episodes.map((e) => e.groupId))].sort(),
- seasons: s.seasons.map((x) => ({{
- season: x.season,
- episodes: x.episodes.map((e) => `S${{e.season}}E${{e.episode}}`),
- }})),
- }})),
- }}));
- """
- script = tmp_path / "case.js"
- script.write_text(f"{pipeline}\n{body}\n")
- out = subprocess.run(
- ["node", str(script)], capture_output=True, text=True, timeout=30)
- assert out.returncode == 0, out.stderr
- return json.loads(out.stdout)
-
-
-# The shape of one shared library: a film, and a two-season show. Invented
-# titles — the real one this was found against is nobody's business here.
-def _library(group):
- """`path` is already prefixed the way search-page.js prefixes it."""
- def entry(file_id, name, **kw):
- return {
- "id": file_id, "name": name, "type": "video",
- "path": "__search__/shows", "size": 1,
- "groupId": group, "groupName": group.upper(), "groupOwner": "someone",
- "_tRef": f"t:{group}", "_gRef": f"g:{group}", "_connGen": 1,
- **kw,
- }
- files = [entry("film1", "a-film.mkv", display_title="Some Film")]
- for season in (1, 2):
- for ep in (1, 2, 3):
- files.append(entry(
- f"s{season}e{ep}", f"show.s0{season}e0{ep}.mkv",
- display_title="Some Saga", season=season, episode=ep))
- return files
-
-
-def test_a_shared_library_is_listed_once(tmp_path, pipeline):
- """
- The bug as reported: two groups, one directory, everything twice.
- """
- both = _library("demo35") + _library("media")
- grid = _grid(tmp_path, pipeline, both)
-
- assert [m["title"] for m in grid["movies"]] == ["Some Film"]
- assert grid["movies"][0]["sources"] == 2
-
- assert len(grid["shows"]) == 1
- show = grid["shows"][0]
- assert [s["season"] for s in show["seasons"]] == [1, 2]
- for season in show["seasons"]:
- assert season["episodes"] == [
- f"S{season['season']}E{n}" for n in (1, 2, 3)], (
- "an episode is listed more than once — this is the reported bug, "
- "in the season list under the synopsis")
-
-
-def test_a_show_streams_from_one_source(tmp_path, pipeline):
- """A season split across two nodes would open two connections and two
- metadata lookups for one show."""
- both = _library("demo35") + _library("media")
- show = _grid(tmp_path, pipeline, both)["shows"][0]
- assert len(show["groups"]) == 1
-
-
-def test_the_operators_own_node_serves_it(tmp_path, pipeline):
- """Both groups are on the operator's node in the reported case; when only
- one is, that one is the source."""
- both = _library("remote") + _library("mine")
- grid = _grid(tmp_path, pipeline, both, local=["mine"])
- assert grid["movies"][0]["groupId"] == "mine"
- assert grid["shows"][0]["groups"] == ["mine"]
-
-
-def test_one_group_is_unchanged(tmp_path, pipeline):
- """The overwhelmingly common case: nothing to merge, nothing different."""
- grid = _grid(tmp_path, pipeline, _library("solo"))
- assert [m["title"] for m in grid["movies"]] == ["Some Film"]
- assert grid["movies"][0]["sources"] == 1
- assert grid["movies"][0]["groupId"] == "solo"
- show = grid["shows"][0]
- assert show["groups"] == ["solo"]
- assert sum(len(s["episodes"]) for s in show["seasons"]) == 6
-
-
-def test_an_episode_only_one_group_has_is_kept(tmp_path, pipeline):
- """
- Merging must never subtract. A group holding one extra episode contributes
- it, whichever source the show settled on.
- """
- extra = _library("media")
- extra.append({
- "id": "s2e4", "name": "show.s02e04.mkv", "type": "video",
- "path": "__search__/shows", "size": 1,
- "groupId": "media", "groupName": "MEDIA", "groupOwner": "someone",
- "display_title": "Some Saga", "season": 2, "episode": 4,
- })
- grid = _grid(tmp_path, pipeline, _library("demo35") + extra)
- season2 = [s for s in grid["shows"][0]["seasons"] if s["season"] == 2][0]
- assert season2["episodes"] == ["S2E1", "S2E2", "S2E3", "S2E4"]