feat(plex): forbid duplicate collection names + a "New collection" button
Two follow-ups after testing the collection editing against real Plex, which revealed that Plex CONFLATES same-named collections (two rating_keys resolve to one merged member list) — a genuine footgun. - Duplicate-name guard: create and rename now reject (409) a title that already exists in the target section (case-insensitive), before any Plex write. create() can also derive the library from the seed item, so the caller needn't know the section key. The create dialogs surface the 409, and the New-collection panel warns inline before submit. - A "New collection" button on the Collections browse surface (admin only) opens a panel: a name field (with the live duplicate check) + a search to pick the FIRST title — Plex can't create an empty collection, it needs a seed. Creating opens the new collection's detail page straight away, so you can keep adding titles there. This is the B-route the user wanted, vs. the only prior path (a movie's "add to collection"). Verified live: the 409 guard (creating an existing name is blocked, no Plex write), and the full New-collection flow (name + first title → create → lands on the detail page); test collections created and deleted, real account left clean. Gate: tsc/lint/knip/prettier clean, 424 vitest; backend ruff clean, 7 collections tests (+ a duplicate-name-rejection assertion).
This commit is contained in:
@@ -7,7 +7,7 @@ from datetime import datetime, timezone
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import app.state as state
|
||||
from app.auth import current_user
|
||||
from app.auth import admin_user, current_user
|
||||
from app.db import get_db
|
||||
from app.main import app
|
||||
from app.models import PlexCollection, PlexItem, PlexLibrary, PlexShow, User
|
||||
@@ -94,6 +94,31 @@ def test_collection_cards_carry_montage_art_and_total_runtime(db, monkeypatch):
|
||||
assert card["total_runtime_s"] == 5400 # 3600 + 1800
|
||||
|
||||
|
||||
def test_create_collection_rejects_duplicate_name(db, monkeypatch):
|
||||
# Same-named collections conflate in Plex, so a create with an existing name (case-insensitive) is
|
||||
# rejected 409 — BEFORE any Plex call (the guard runs first, so the DB lane can assert it).
|
||||
monkeypatch.setattr(state, "_configured_cache", True)
|
||||
lib = PlexLibrary(plex_key="1", title="Movies", kind="movie", enabled=True)
|
||||
db.add(lib)
|
||||
db.flush()
|
||||
db.add(PlexCollection(rating_key="A", library_id=lib.id, title="Marvel"))
|
||||
db.add(PlexItem(rating_key="m1", library_id=lib.id, kind="movie", title="M1"))
|
||||
user = User(email="dup@example.com")
|
||||
db.add(user)
|
||||
db.commit()
|
||||
app.dependency_overrides[get_db] = lambda: db
|
||||
app.dependency_overrides[current_user] = lambda: user
|
||||
app.dependency_overrides[admin_user] = lambda: user
|
||||
try:
|
||||
r = TestClient(app).post(
|
||||
"/api/plex/collections",
|
||||
json={"library": "1", "title": "marvel", "item_rating_key": "m1"},
|
||||
)
|
||||
assert r.status_code == 409, r.text
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
def test_collection_detail_hero_and_ordered_members(db, monkeypatch):
|
||||
# The Collection info page: hero aggregates (year range, most-restrictive cert, total runtime) +
|
||||
# members ordered by release year then title.
|
||||
|
||||
Reference in New Issue
Block a user