Channel, Subscription, Video, SearchFind, ExploredChannel and BlockedChannel (and LIVE_OR_UPCOMING) leave app/models.py for app/catalog/models.py; app.tables registers them. The User↔Subscription relationship names its other side as a type only. No schema change.
70 lines
2.0 KiB
Python
70 lines
2.0 KiB
Python
"""Subscribing when YouTube says no — and when "no" means "you already are".
|
|
|
|
The error no longer carries YouTube's body (it is the request quoted back, and it went into a log
|
|
line and a 422), so "already following" is recognised by the reason code and nothing else.
|
|
DB lane.
|
|
"""
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
from sqlalchemy import select
|
|
|
|
from app.catalog.models import Channel, Subscription
|
|
from app.routes import channels
|
|
from app.youtube.client import YouTubeError
|
|
|
|
|
|
def _youtube_refusing(reason: str):
|
|
class _Client:
|
|
def __init__(self, db, user):
|
|
pass
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *exc):
|
|
return False
|
|
|
|
def insert_subscription(self, channel_id):
|
|
raise YouTubeError(f"POST subscriptions -> 400: {reason}", reason=reason)
|
|
|
|
def get_channels(self, ids):
|
|
return []
|
|
|
|
return _Client
|
|
|
|
|
|
@pytest.fixture
|
|
def stub_channel(db):
|
|
db.add(Channel(id="UCdup", title="Already followed"))
|
|
db.commit()
|
|
return "UCdup"
|
|
|
|
|
|
@pytest.mark.parametrize("reason", ["subscriptionDuplicate", "quotaExceeded,subscriptionDuplicate"])
|
|
def test_already_following_on_youtube_is_recorded_not_refused(
|
|
db, make_user, monkeypatch, stub_channel, reason
|
|
):
|
|
user = make_user()
|
|
monkeypatch.setattr(channels, "YouTubeClient", _youtube_refusing(reason))
|
|
|
|
channels.subscribe(stub_channel, user=user, db=db)
|
|
|
|
sub = db.execute(
|
|
select(Subscription).where(
|
|
Subscription.user_id == user.id, Subscription.channel_id == stub_channel
|
|
)
|
|
).scalar_one()
|
|
assert sub is not None
|
|
|
|
|
|
@pytest.mark.parametrize("reason", ["quotaExceeded", "<none>", "<unparseable>"])
|
|
def test_any_other_refusal_is_answered_with_a_422(db, make_user, monkeypatch, stub_channel, reason):
|
|
user = make_user()
|
|
monkeypatch.setattr(channels, "YouTubeClient", _youtube_refusing(reason))
|
|
|
|
with pytest.raises(HTTPException) as caught:
|
|
channels.subscribe(stub_channel, user=user, db=db)
|
|
|
|
assert caught.value.status_code == 422
|