feat(service-storage-harness): slice 3 — migrate MediaIndex onto harness (scoped replace_items, +service_id)
Register MediaIndex as a harness concern with ALTER TABLE migration to add service_id column (idempotent). Scope replace_items by service_id (FIXES latent global-clear bug where building for one Jellyfin wiped another's rows). Scope query by service_id (empty-string = all rows, backward-compat). Thread service_id through build_media_index + worker + query_media router. New regression test proves scoped replace preserves other services' rows. Backend: 321 pytest pass, ruff clean. Frontend: build green.
This commit is contained in:
@@ -241,6 +241,53 @@ class TestMediaIndexReplace:
|
||||
status = index.status()
|
||||
assert status.item_count == 2
|
||||
|
||||
def test_replace_scoped_by_service_id_preserves_other_services(self, index):
|
||||
"""Regression test: building for one Jellyfin must not wipe another's rows.
|
||||
|
||||
Before the migration, ``replace_items`` did ``DELETE FROM media_items``
|
||||
(global clear). This test locks in the fix: a scoped replace preserves
|
||||
rows belonging to a different service_id.
|
||||
"""
|
||||
rows_a = [
|
||||
{"id": "a1", "title": "Alpha Movie", "type": "Movie", "library_id": "l1", "library_name": "L1"},
|
||||
{"id": "a2", "title": "Alpha Show", "type": "Episode", "library_id": "l2", "library_name": "L2"},
|
||||
]
|
||||
rows_b = [
|
||||
{"id": "b1", "title": "Beta Movie", "type": "Movie", "library_id": "l1", "library_name": "L1"},
|
||||
]
|
||||
|
||||
index.replace_items(rows_a, service_id="svc-a")
|
||||
assert index.status().item_count == 2
|
||||
|
||||
# Replacing for svc-b must NOT wipe svc-a's rows.
|
||||
index.replace_items(rows_b, service_id="svc-b")
|
||||
assert index.status().item_count == 3 # 2 from svc-a + 1 from svc-b
|
||||
|
||||
# Querying svc-a returns only its rows.
|
||||
rows_a_result, total_a = index.query(
|
||||
library_ids=["l1", "l2"],
|
||||
media_types=["Movie", "Episode"],
|
||||
service_id="svc-a",
|
||||
)
|
||||
assert total_a == 2
|
||||
assert {r["id"] for r in rows_a_result} == {"a1", "a2"}
|
||||
|
||||
# Querying svc-b returns only its rows.
|
||||
rows_b_result, total_b = index.query(
|
||||
library_ids=["l1"],
|
||||
media_types=["Movie"],
|
||||
service_id="svc-b",
|
||||
)
|
||||
assert total_b == 1
|
||||
assert rows_b_result[0]["id"] == "b1"
|
||||
|
||||
# Querying with no service_id returns all rows (backward-compat).
|
||||
_, total_all = index.query(
|
||||
library_ids=["l1", "l2"],
|
||||
media_types=["Movie", "Episode"],
|
||||
)
|
||||
assert total_all == 3
|
||||
|
||||
|
||||
class TestMediaIndexMetadata:
|
||||
def test_set_and_read_metadata(self, index):
|
||||
|
||||
Reference in New Issue
Block a user