Files
minstrel/internal/api/admin_suspect_sources_test.go
T
bvandeusenandClaude Opus 5.5 2205b2927b
release / govulncheck (push) Successful in 15s
release / web (push) Successful in 1m23s
release / go (push) Successful in 1m36s
release / integration (push) Successful in 4m36s
release / android (push) Successful in 5m4s
release / Build signed APK (releases and dev) (push) Successful in 5m13s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 1m11s
release / Verify release artifacts (tag releases only) (push) Skipped
test: fix the Suspect sources tests CI rejected (#5410)
- staticcheck QF1001: the nil-vs-empty check in TestSourceMarkersFor is
  now an early continue instead of a negated conjunction.
- The page test mocks #lib/api/admin.js whole, so suspectSourcesNextOffset
  was undefined there. Its test moves to admin.suspect-sources.test.ts,
  beside admin.scan.test.ts, against the real module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 15:56:18 -04:00

117 lines
4.9 KiB
Go

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 len(got) == 0 && len(c.want) == 0 {
continue // nil and empty both mean "no markers"
}
if !slices.Equal(got, c.want) {
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)
}
}
}