fix(feed): a malformed saved view cannot break the autotag cleanup, and a foreign private tag reads as missing

E32 review round 1 (B5, B6).

- The autotag orphan cleanup reads every user's feed views. A view whose tag pick was not a list
  ("tags": 5) raised for everyone's job, True passed as tag id 1, and ~66k ids pushed the NOT IN
  past psycopg's 65535 bind parameters. The views are now read defensively and the kept ids
  filtered in Python; the saved-views route holds filters.tags to a list of at most 500 ids, on
  create and on update.
- tags_by_category resolved any tag id, so another user's private tag landed in its real category
  and the feed summary answered differently than for an id that does not exist, which told the
  caller which foreign ids exist. It is owner-scoped now (pre-existing; S2's missing-tag branch
  made the difference visible).
This commit is contained in:
2026-10-04 02:14:04 +02:00
parent df4a79a1f1
commit 887b4e546c
6 changed files with 118 additions and 21 deletions
+15
View File
@@ -67,3 +67,18 @@ def test_duplicate_name_within_a_source_conflicts(db, monkeypatch, override_dep)
b = c.post("/api/saved-views", json={"name": "Dup", "filters": {}, "source": "plex"})
assert a.status_code == 200
assert b.status_code == 409 # same (user, source, name)
def test_a_feed_views_tag_pick_must_be_a_short_list_of_ids(db, monkeypatch, override_dep):
"""The autotag job reads every view's tag pick (E32 review): the route holds it to the shape the
feed sends, on create and on update."""
user = _user(db)
c = _client(db, user, monkeypatch, override_dep)
for bad in (5, "1", [1, "2"], [True], list(range(501))):
r = c.post("/api/saved-views", json={"name": "Bad", "filters": {"tags": bad}})
assert r.status_code == 422, (bad, r.text)
ok = c.post("/api/saved-views", json={"name": "Ok", "filters": {"tags": [1, 2]}})
assert ok.status_code == 200, ok.text
vid = ok.json()["id"]
assert c.patch(f"/api/saved-views/{vid}", json={"filters": {"tags": 5}}).status_code == 422
assert c.patch(f"/api/saved-views/{vid}", json={"filters": {"tags": []}}).status_code == 200