package api import ( "context" "encoding/json" "net/http" "net/http/httptest" "slices" "testing" ) // Real basenames from the operator's library (2026-10-08), each with the // labels it should carry. The negatives are the near misses that shaped the // list: a song called "Chain Reaction", a live album, an ordinary track. func TestSourceMarkersFor(t *testing.T) { cases := []struct { base string want []string }{ {"Daft Punk - Random Access Memories - 06 - Daft Punk - Doin' It Right (Music Video) ft. Panda Bear.mp3", []string{"music video"}}, {"Gorillaz - Humanz - 03 - Gorillaz - Saturnz Barz (Official Video).mp3", []string{"music video"}}, {"Gorillaz - Humanz - 05 - Gorillaz - Andromeda (Official Audio).mp3", []string{"official audio"}}, {"Gorillaz - Gorillaz - 06 - Gorillaz - P45 (Visualizer).mp3", []string{"visualiser"}}, {"Gorillaz - Demon Days - 16 - Gorillaz - Don Quixote's Christmas Bonanza (Visualiser).mp3", []string{"visualiser"}}, {"Artist - Album - 01 - Artist - Song (Lyric Video).mp3", []string{"lyric video"}}, {"Gorillaz - Humanz - 02 - FIRST TIME HEARING Gorillaz - Ascension REACTION.mp3", []string{"reaction"}}, {"Watsky - INTENTION - 05 - MANIAC Reacts to Watsky - AWW SHiT.mp3", []string{"reaction"}}, {"米津玄師 - diorama - 09 - 【MV】米津玄師 - 恋と病熱.mp3", []string{"MV"}}, {"Andora - Ego - 01 - Andora - Ego (feat. Will Stetson) MV.mp3", []string{"MV"}}, {"Jimmy Eat World - Something(s) Loud - 05 - Jimmy Eat World - Call to Love (Audio).mp3", []string{"[Audio]"}}, {"Record Heat - World War IV - 03 - Record Heat - Front Seat Feelin' [Audio].mp3", []string{"[Audio]"}}, {"Aphex Twin - Come To Daddy - 03 - Aphex Twin - Bucephalus Bouncing Ball (HQ).mp3", []string{"[HD]"}}, {"Artist - Album - 01 - Artist - Song (Official Lyric Video) [4K].mp3", []string{"lyric video", "[HD]"}}, {"Beck - Guero - 15 - Beck - Chain Reaction.mp3", nil}, {"Nirvana - MTV Unplugged in New York - 01 - About a Girl (live in New York).flac", nil}, {"Boards of Canada - Music Has the Right to Children - 05 - Roygbiv.flac", nil}, {"Artist - Album - 01 - Mvula.flac", nil}, } for _, c := range cases { got := sourceMarkersFor("/music/x/" + c.base) if !slices.Equal(got, c.want) && !(len(got) == 0 && len(c.want) == 0) { t.Errorf("%s:\n got %v\n want %v", c.base, got, c.want) } } } // The folder is not evidence: a marker word in a directory name must not // flag the files inside it. func TestSourceMarkersFor_ReadsOnlyTheBasename(t *testing.T) { if got := sourceMarkersFor("/music/Official Video Collection/01 - Song.flac"); len(got) != 0 { t.Errorf("directory name flagged the file: %v", got) } } // The integration test runs the same alternation through Postgres's ~*, so the // one pattern is exercised in both regex dialects it has to work in. func TestHandleListSuspectSources_Integration(t *testing.T) { h, pool := testHandlers(t) truncateLibrary(t, pool) admin := seedUser(t, pool, "suspect-admin", "pw", true) artist := seedArtist(t, pool, "Gorillaz") humanz := seedAlbum(t, pool, artist.ID, "Humanz", 2017) seedTrack(t, pool, humanz.ID, artist.ID, "Saturnz Barz (Official Video)", 1, 180_000) seedTrack(t, pool, humanz.ID, artist.ID, "Momentz (Visualizer)", 2, 200_000) seedTrack(t, pool, humanz.ID, artist.ID, "Andromeda [Audio]", 3, 190_000) seedTrack(t, pool, humanz.ID, artist.ID, "Chain Reaction", 4, 190_000) seedTrack(t, pool, humanz.ID, artist.ID, "Busted and Blue", 5, 190_000) gone := seedTrack(t, pool, humanz.ID, artist.ID, "Ascension (Lyric Video)", 6, 156_000) if _, err := pool.Exec(context.Background(), `UPDATE tracks SET missing_since = now() WHERE id = $1`, gone.ID); err != nil { t.Fatalf("mark missing: %v", err) } req := httptest.NewRequest(http.MethodGet, "/api/admin/library/suspect-sources?limit=50", nil) req = withUser(req, admin) w := httptest.NewRecorder() h.handleListSuspectSources(w, req) if w.Code != http.StatusOK { t.Fatalf("status %d: %s", w.Code, w.Body.String()) } var resp adminSuspectSourcesResponse if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { t.Fatalf("decode: %v", err) } // Three present files carry a marker. "Chain Reaction" and "Busted and // Blue" carry none, and the lyric video's file is missing. if resp.Total != 3 { t.Errorf("total = %d, want 3", resp.Total) } if len(resp.Groups) != 1 { t.Fatalf("groups = %d, want 1 (one folder)", len(resp.Groups)) } got := map[string][]string{} for _, tr := range resp.Groups[0].Tracks { got[tr.Title] = tr.Markers } want := map[string][]string{ "Saturnz Barz (Official Video)": {"music video"}, "Momentz (Visualizer)": {"visualiser"}, "Andromeda [Audio]": {"[Audio]"}, } if len(got) != len(want) { t.Fatalf("tracks = %v, want %v", got, want) } for title, labels := range want { if !slices.Equal(got[title], labels) { t.Errorf("%s: markers %v, want %v", title, got[title], labels) } } }