import pytest from backend.app.models import Artist from backend.app.services.artist_service import ArtistService pytestmark = pytest.mark.integration @pytest.mark.asyncio async def test_find_or_create_creates_new(db): svc = ArtistService(db) artist, created = await svc.find_or_create("Brand New") assert created is True assert artist.id is not None assert artist.slug == "brand-new" @pytest.mark.asyncio async def test_find_or_create_idempotent_on_same_name(db): svc = ArtistService(db) a1, c1 = await svc.find_or_create("Alice") a2, c2 = await svc.find_or_create("Alice") assert c1 is True assert c2 is False assert a1.id == a2.id @pytest.mark.asyncio async def test_find_or_create_rejects_empty_name(db): svc = ArtistService(db) with pytest.raises(ValueError): await svc.find_or_create(" ") @pytest.mark.asyncio async def test_autocomplete_ranks_exact_prefix_substring(db): db.add_all([ Artist(name="Alice", slug="alice"), Artist(name="Alice Cooper", slug="alice-cooper"), Artist(name="Malice", slug="malice"), Artist(name="Bob", slug="bob"), ]) await db.flush() svc = ArtistService(db) rows = await svc.autocomplete("alice") names = [r.name for r in rows] assert names[0] == "Alice" # exact first assert names[1] == "Alice Cooper" # then prefix assert "Malice" in names # then substring assert "Bob" not in names @pytest.mark.asyncio async def test_autocomplete_ignores_spacing_and_punctuation(db): """The same creator is spelled differently per platform — a Discord server "Tamada Heijun" must find the Patreon artist "TamadaHeijun" (milestone 429).""" db.add_all([ Artist(name="TamadaHeijun", slug="tamadaheijun"), Artist(name="Sabu Art", slug="sabu-art"), Artist(name="Bob", slug="bob"), ]) await db.flush() svc = ArtistService(db) assert [r.name for r in await svc.autocomplete("Tamada Heijun")] == ["TamadaHeijun"] assert [r.name for r in await svc.autocomplete("sabu_art")] == ["Sabu Art"] # All punctuation: no squashed arm, so it does not match everyone. assert await svc.autocomplete("--") == [] @pytest.mark.asyncio async def test_autocomplete_ranks_an_exact_match_ignoring_spacing_above_a_prefix(db): db.add_all([ Artist(name="Sabu Artworks", slug="sabu-artworks"), Artist(name="SabuArt", slug="sabuart"), ]) await db.flush() names = [r.name for r in await ArtistService(db).autocomplete("sabu art")] assert names == ["SabuArt", "Sabu Artworks"] @pytest.mark.asyncio async def test_autocomplete_empty_query_returns_empty(db): svc = ArtistService(db) assert await svc.autocomplete("") == [] @pytest.mark.asyncio async def test_rename_changes_name_not_slug(db): # #130: rename touches the display name ONLY — the slug (and every on-disk # path keyed off it) is immutable. svc = ArtistService(db) artist, _ = await svc.find_or_create("12345678") orig_slug = artist.slug renamed = await svc.rename(artist.id, "Kurotsuchi Machi") assert renamed.name == "Kurotsuchi Machi" assert renamed.slug == orig_slug # slug frozen fresh = await db.get(Artist, artist.id) assert fresh.name == "Kurotsuchi Machi" assert fresh.slug == orig_slug @pytest.mark.asyncio async def test_rename_rejects_empty_and_missing(db): svc = ArtistService(db) artist, _ = await svc.find_or_create("Someone") with pytest.raises(ValueError): await svc.rename(artist.id, " ") assert await svc.rename(999999, "Ghost") is None @pytest.mark.asyncio async def test_two_artists_can_share_a_display_name(db): # #130: name is non-unique now (two genuinely different creators can share a # display name); the immutable slug keeps them distinct. a = Artist(name="Same Name", slug="same-name") b = Artist(name="Same Name", slug="same-name-2") db.add_all([a, b]) await db.flush() assert a.id != b.id assert a.name == b.name assert a.slug != b.slug