refactor(channels): shared tag-mutation hook + a channel_detail tag_ids test (R8 review)
Address the two remaining review findings on the tag work:
- extract useChannelTagActions — the optimistic attach/detach + create-and-attach flow that
the channel manager and the channel detail page had copy-pasted; the cache-shape-specific
optimistic update is injected (manager maps a ManagedChannel[]; detail patches one
ChannelDetail), so the two stay in sync;
- add a DB-lane test asserting GET /api/channels/{id} returns exactly this user's personal
tag links — not another user's, not the auto/system (user_id NULL) links.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""DB-backed regression (R8 Part 2): GET /api/channels/{id} returns exactly THIS user's personal
|
||||
tag links for the channel — not another user's, and not the auto/system links (user_id NULL) —
|
||||
so the channel detail page can reuse the manager's tag editor. Would ship broken if the tag_ids
|
||||
query ever dropped its user_id filter.
|
||||
|
||||
Uses the DB lane (`db` fixture) — skipped when no Postgres is reachable.
|
||||
"""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
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 Channel, ChannelTag, Tag, User
|
||||
|
||||
|
||||
def test_channel_detail_returns_only_this_users_tag_ids(db, monkeypatch):
|
||||
# published_at set so channel_detail skips the YouTube-enrich path (no quota / network in tests).
|
||||
ch = Channel(
|
||||
id="UC_tags_test",
|
||||
title="Tag Test Channel",
|
||||
published_at=datetime(2020, 1, 1, tzinfo=timezone.utc),
|
||||
)
|
||||
db.add(ch)
|
||||
me = User(email="me@example.com")
|
||||
other = User(email="other@example.com")
|
||||
db.add_all([me, other])
|
||||
db.flush()
|
||||
mine = Tag(user_id=me.id, name="Mine", category="other")
|
||||
theirs = Tag(user_id=other.id, name="Theirs", category="other")
|
||||
auto = Tag(user_id=None, name="English", category="language") # a system/auto tag
|
||||
db.add_all([mine, theirs, auto])
|
||||
db.flush()
|
||||
# My personal link, another user's link, and a system/auto link on the same channel — only MINE
|
||||
# must come back for `me`.
|
||||
db.add(ChannelTag(channel_id=ch.id, tag_id=mine.id, user_id=me.id, source="user"))
|
||||
db.add(ChannelTag(channel_id=ch.id, tag_id=theirs.id, user_id=other.id, source="user"))
|
||||
db.add(ChannelTag(channel_id=ch.id, tag_id=auto.id, user_id=None, source="auto"))
|
||||
db.commit()
|
||||
|
||||
# The request gate 503s an unconfigured instance; flip the process-level flag (auto-restored).
|
||||
monkeypatch.setattr(state, "_configured_cache", True)
|
||||
app.dependency_overrides[get_db] = lambda: db
|
||||
app.dependency_overrides[current_user] = lambda: me
|
||||
try:
|
||||
client = TestClient(app) # no context-manager: skip lifespan (scheduler/HLS sweep)
|
||||
resp = client.get(f"/api/channels/{ch.id}")
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
assert resp.status_code == 200, resp.text
|
||||
assert resp.json()["tag_ids"] == [mine.id]
|
||||
Reference in New Issue
Block a user