"""Unit tests for ExtensionService._derive — the URL → (platform, slug) parser that gates the browser extension's "Add as source" button and pulls the creator slug on probe/add. Regression cover for #1485: Patreon serves the same creator under three URL shapes — bare `patreon.com/Atole`, `c/`, and `cw/` (the "creator workspace" URL you land on once SUBSCRIBED). The button used to vanish while subscribed because the pattern only matched the bare root and excluded `c/`. _derive is pure URL parsing (no DB / no async), so a session-less instance is fine to exercise directly. """ import pytest from backend.app.services.extension_service import ( ExtensionService, InvalidUrlError, UnknownPlatformError, ) _svc = ExtensionService(None) @pytest.mark.parametrize( "url, slug", [ # All three Patreon creator prefixes resolve to the same vanity slug. ("https://www.patreon.com/Atole", "Atole"), ("https://www.patreon.com/c/Atole", "Atole"), ("https://www.patreon.com/cw/Atole", "Atole"), # subscribed-view URL # A creator's inner page still derives the slug (trailing sub-path). ("https://www.patreon.com/cw/Atole/posts", "Atole"), ("https://www.patreon.com/Atole/membership", "Atole"), ("https://patreon.com/c/Atole", "Atole"), # bare host, no www ], ) def test_derive_patreon_creator_urls(url, slug): platform, got = _svc._derive(url) assert platform == "patreon" assert got == slug @pytest.mark.parametrize( "url", [ # Patreon's own nav pages must never read as a creator slug. "https://www.patreon.com/home", "https://www.patreon.com/settings", "https://www.patreon.com/search", "https://www.patreon.com/messages", "https://www.patreon.com/library", "https://www.patreon.com/notifications", "https://www.patreon.com/posts/12345", # post permalink "https://www.patreon.com/settings/billing", # nav sub-page ], ) def test_derive_patreon_nav_pages_rejected(url): with pytest.raises(UnknownPlatformError): _svc._derive(url) def test_derive_rejects_missing_scheme(): with pytest.raises(InvalidUrlError): _svc._derive("patreon.com/Atole")