PlexLibrary, PlexShow, PlexSeason, PlexItem, PlexPerson, PlexCollection, PlexState, PlexLink, PlexPlaylist and PlexPlaylistItem leave app/models.py for app/plex/models.py; app.tables registers them. Every import is re-pointed; no schema change.
82 lines
3.7 KiB
Python
82 lines
3.7 KiB
Python
""" "Clear resume point" has to reach Plex — the case the status-only guard skipped.
|
|
|
|
`POST /api/plex/item/{rk}/state {"status": "new"}` backs the *Clear resume point* / *Mark unwatched*
|
|
buttons. It deletes the local row and mirrors the un-mark to Plex — but it used to decide whether to
|
|
push from the PREVIOUS STATUS alone (`prev_status != "new"`), and an in-progress item's row is
|
|
`status="new"` carrying a position: that is how the importer maps a bare `viewOffset`
|
|
(`_plex_watch_to_state`). So for the one case the button exists for, nothing was ever sent to Plex,
|
|
Plex kept the offset, and the next pull imported it straight back — the user cleared *Protector*
|
|
twice and it returned to Continue watching at 92s both times (2026-09-09).
|
|
|
|
One `unscrobble` clears the view count AND the offset (measured against the live server), so the
|
|
push itself needed no change; what needed fixing is WHEN it is scheduled.
|
|
|
|
DB lane (`db` fixture) — skipped when no Postgres is reachable.
|
|
"""
|
|
|
|
from types import SimpleNamespace
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
import app.state as state
|
|
from app.auth import current_user
|
|
from app.db import get_db
|
|
from app.main import app
|
|
from app.models import User
|
|
from app.plex.models import PlexItem, PlexLibrary, PlexState
|
|
from app.plex import watch_sync as plex_watch
|
|
|
|
|
|
def _seed(db, *, status: str, position: int):
|
|
lib = PlexLibrary(plex_key="1", title="Movies", kind="movie", enabled=True)
|
|
db.add(lib)
|
|
db.flush()
|
|
it = PlexItem(
|
|
rating_key="98614", library_id=lib.id, kind="movie", title="Protector", duration_s=5495
|
|
)
|
|
db.add(it)
|
|
user = User(email="clear@example.com")
|
|
db.add(user)
|
|
db.flush()
|
|
db.add(PlexState(user_id=user.id, item_id=it.id, status=status, position_seconds=position))
|
|
db.commit()
|
|
return it, user
|
|
|
|
|
|
def _clear(db, user, it, monkeypatch, override_dep) -> list[tuple]:
|
|
"""Drive the endpoint with the Plex link satisfied and the background push recorded."""
|
|
pushes: list[tuple] = []
|
|
monkeypatch.setattr(state, "_configured_cache", True) # pass the instance-setup request gate
|
|
monkeypatch.setattr(plex_watch, "link_for_push", lambda db_, uid: SimpleNamespace(id=1))
|
|
monkeypatch.setattr(
|
|
plex_watch,
|
|
"push_state_to_plex",
|
|
lambda user_id, item_id, rating_key, action, *rest: pushes.append((rating_key, action)),
|
|
)
|
|
override_dep(get_db, db.info["request"])
|
|
override_dep(current_user, lambda: user)
|
|
resp = TestClient(app).post(f"/api/plex/item/{it.rating_key}/state", json={"status": "new"})
|
|
assert resp.status_code == 200, resp.text
|
|
return pushes
|
|
|
|
|
|
def test_clearing_an_in_progress_item_tells_plex(db, monkeypatch, override_dep):
|
|
# The regression: status "new" + a position IS the in-progress shape, and it must push.
|
|
it, user = _seed(db, status="new", position=92)
|
|
assert _clear(db, user, it, monkeypatch, override_dep) == [("98614", "unwatched")]
|
|
assert db.query(PlexState).count() == 0
|
|
|
|
|
|
def test_clearing_a_watched_item_still_tells_plex(db, monkeypatch, override_dep):
|
|
# The case the old guard did cover — kept, so the fix cannot narrow into a new hole.
|
|
it, user = _seed(db, status="watched", position=0)
|
|
assert _clear(db, user, it, monkeypatch, override_dep) == [("98614", "unwatched")]
|
|
|
|
|
|
def test_unhiding_touches_plex_not_at_all(db, monkeypatch, override_dep):
|
|
# `hidden` is a Siftlode-only concept "with no Plex analogue, so it never touches Plex" — the
|
|
# endpoint's own docstring. The old status-only guard pushed here anyway, un-marking an item on
|
|
# Plex that the user had merely hidden.
|
|
it, user = _seed(db, status="hidden", position=0)
|
|
assert _clear(db, user, it, monkeypatch, override_dep) == []
|