"""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.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", "", ""]) 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