test(api): end-to-end contextual ranking test for /api/radio
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -89,6 +89,72 @@ func seedTrack(t *testing.T, pool *pgxpool.Pool, albumID, artistID pgtype.UUID,
|
||||
return tr
|
||||
}
|
||||
|
||||
// seedTrackWithGenre is seedTrack but lets the caller set Genre. Used by
|
||||
// tests that exercise the contextual recommendation path, since session
|
||||
// vectors collect tags from tracks.genre.
|
||||
func seedTrackWithGenre(t *testing.T, pool *pgxpool.Pool, albumID, artistID pgtype.UUID, title string, trackNumber int, durationMs int32, genre string) dbq.Track {
|
||||
t.Helper()
|
||||
var tn *int32
|
||||
if trackNumber > 0 {
|
||||
v := int32(trackNumber)
|
||||
tn = &v
|
||||
}
|
||||
tr, err := dbq.New(pool).UpsertTrack(context.Background(), dbq.UpsertTrackParams{
|
||||
Title: title,
|
||||
AlbumID: albumID,
|
||||
ArtistID: artistID,
|
||||
TrackNumber: tn,
|
||||
DiscNumber: nil,
|
||||
DurationMs: durationMs,
|
||||
FilePath: "/seed/" + title + ".flac",
|
||||
FileSize: 1024,
|
||||
FileFormat: "flac",
|
||||
Bitrate: nil,
|
||||
Mbid: nil,
|
||||
Genre: &genre,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpsertTrack(%s): %v", title, err)
|
||||
}
|
||||
return tr
|
||||
}
|
||||
|
||||
// insertOpenSessionWithVector creates a play_session with ended_at NULL and
|
||||
// inserts a play_event in it whose session_vector_at_play is the given JSON.
|
||||
// Used to simulate "user is mid-listen" for the radio handler's current-vector
|
||||
// lookup. The play_event references a placeholder track inserted on the fly
|
||||
// so the FK is valid; the placeholder is not used for radio scoring.
|
||||
func insertOpenSessionWithVector(t *testing.T, pool *pgxpool.Pool, userID, anyArtistID pgtype.UUID, vectorJSON []byte) {
|
||||
t.Helper()
|
||||
q := dbq.New(pool)
|
||||
al, err := q.UpsertAlbum(context.Background(), dbq.UpsertAlbumParams{
|
||||
Title: "PlaceholderAlbum", SortTitle: "PlaceholderAlbum", ArtistID: anyArtistID,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("placeholder album: %v", err)
|
||||
}
|
||||
ph, err := q.UpsertTrack(context.Background(), dbq.UpsertTrackParams{
|
||||
Title: "Placeholder", AlbumID: al.ID, ArtistID: anyArtistID,
|
||||
FilePath: "/seed/placeholder.flac", DurationMs: 100_000, FileSize: 1024, FileFormat: "flac",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("placeholder track: %v", err)
|
||||
}
|
||||
var sessionID pgtype.UUID
|
||||
if err := pool.QueryRow(context.Background(),
|
||||
`INSERT INTO play_sessions (user_id, started_at, last_event_at, client_id)
|
||||
VALUES ($1, now() - interval '5 minutes', now(), 'test') RETURNING id`,
|
||||
userID).Scan(&sessionID); err != nil {
|
||||
t.Fatalf("insert session: %v", err)
|
||||
}
|
||||
if _, err := pool.Exec(context.Background(),
|
||||
`INSERT INTO play_events (user_id, track_id, session_id, started_at, session_vector_at_play)
|
||||
VALUES ($1, $2, $3, now() - interval '1 minute', $4)`,
|
||||
userID, ph.ID, sessionID, vectorJSON); err != nil {
|
||||
t.Fatalf("insert play_event: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// seedTrackWithFile creates a fresh temp directory, writes fileBody to
|
||||
// <dir>/<title>.<ext>, and inserts a Track row whose file_path points at it.
|
||||
// Returns the inserted Track and the directory (callers drop sidecar covers
|
||||
|
||||
Reference in New Issue
Block a user