aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_search_source_merge.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-02 16:21:48 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-02 16:21:48 +0200
commit10f8266e7152d7dc38dbfe2449327829bf020ad1 (patch)
tree5e72ac45b2e79008812663e251a4fcab62dc0650 /packages/meshbay-hub/tests/test_search_source_merge.py
parent313b72f15e8788ba3abcd3e44b5f7785fbc779fe (diff)
downloadmeshbay-10f8266e7152d7dc38dbfe2449327829bf020ad1.tar.gz
fix(hub): merge duplicate sources in Search's Music and Photos too
Phases 5-8 of docs/refactoring-search.md, extending the Videos merge outward. A library shared by two groups now lists each track once inside an album and each photo once inside a photo album, and a card served by several groups says "N sources" instead of naming one of them. Units come from each application's own grouping, never a copy of its keys. For Music that meant exporting foldKey: groupMusicEntries folds case to group but keeps the first-seen spelling to display, and which group is seen first is whichever index arrived first — so keying a unit on the display strings would let the chosen source change between page loads. A group whose connection fails is marked down and stops being chosen, so a unit fails over to another group that has the file. Eviction is not a failure. Every source being down still yields an entry: a tile that fails to load beats a film that vanished from the grid. sourceLabel now takes the whole unit rather than one entry. A show's poster entry is picked for its thumbnail, so a show in two groups whose cover episode sits in only one of them would have claimed a single source. SourceTag lives in group-name.js — source-merge.js must keep importing nothing (its test executes it standalone), and a copy in each of the three apps is three chances to disagree. test_search_files_unmerged.py holds the one thing that must not change: the Files explorer is not merged, because there each group is a folder and merging would remove a file from one of them. It also asserts the other three lists are merged, or deleting the merge outright would leave it passing and saying nothing. One plan item was dropped as wrong rather than built: the Music queue in onPreview needed no change. It filters by groupId and is reachable only from FilesPanel, which is not merged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AbwJDbNTkiRUh7HTWEoyss
Diffstat (limited to 'packages/meshbay-hub/tests/test_search_source_merge.py')
-rw-r--r--packages/meshbay-hub/tests/test_search_source_merge.py30
1 files changed, 26 insertions, 4 deletions
diff --git a/packages/meshbay-hub/tests/test_search_source_merge.py b/packages/meshbay-hub/tests/test_search_source_merge.py
index 078c38d..cadf095 100644
--- a/packages/meshbay-hub/tests/test_search_source_merge.py
+++ b/packages/meshbay-hub/tests/test_search_source_merge.py
@@ -328,13 +328,35 @@ def test_source_label(tmp_path, module_source):
_sources: [{ groupId: 'g1', groupName: 'G1' }] };
const many = { groupId: 'g1', groupName: 'G1',
_sources: [{ groupId: 'g1' }, { groupId: 'g2' }] };
+ const unmerged = { groupId: 'g9', groupName: 'G9' };
const bare = { };
console.log(JSON.stringify(
- [one, many, bare].map(sourceLabel)));
+ [one, many, unmerged, bare].map((e) => sourceLabel(e))));
"""
- one, many, bare = _run(tmp_path, module_source, body)
+ one, many, unmerged, bare = _run(tmp_path, module_source, body)
assert one == {"count": 1, "name": "G1", "groupId": "g1"}
# Which group was picked is deliberately not shown once there are several.
assert many == {"count": 2, "name": "", "groupId": None}
- # The single-group Group page, where entries carry no sources at all.
- assert bare == {"count": 1, "name": "", "groupId": None}
+ # An entry that never went through the merge is its own single source.
+ assert unmerged == {"count": 1, "name": "G9", "groupId": "g9"}
+ # The single-group Group page, where entries carry no group at all.
+ assert bare == {"count": 0, "name": "", "groupId": None}
+
+
+def test_source_label_counts_the_unit_not_the_cover(tmp_path, module_source):
+ """
+ A card stands for a unit; the entry it is drawn from is one file. A show's
+ poster entry is chosen for its *thumbnail*, so a show in two groups whose
+ cover episode sits in only one of them would have claimed a single source.
+ """
+ body = """
+ const cover = { id: 'e1', groupId: 'aa', groupName: 'AA',
+ _sources: [{ groupId: 'aa', groupName: 'AA' }] };
+ const rest = { id: 'e2', groupId: 'aa', groupName: 'AA',
+ _sources: [{ groupId: 'aa' }, { groupId: 'bb' }] };
+ console.log(JSON.stringify(
+ [sourceLabel(cover), sourceLabel([cover, rest])]));
+ """
+ alone, unit = _run(tmp_path, module_source, body)
+ assert alone["count"] == 1
+ assert unit["count"] == 2