Files
streamrip-go/cmd/rip/main_test.go
Joren 0748d5a325 improve lastfm playlist parity and fallback resolution
Queue resolved last.fm tracks as playlist media to preserve playlist semantics, and add a robust mirror-based parser fallback for last.fm temporary unavailable responses while keeping track order and duplicates.
2026-04-20 02:01:16 +02:00

207 lines
6.4 KiB
Go

package main
import "testing"
func TestParseFileInputJSONItems(t *testing.T) {
content := []byte(`[
{"source":"qobuz","media_type":"album","id":"0066991040005"},
{"source":"tidal","media_type":"track","id":3083287}
]`)
items, urls, repeated, jsonInput, err := parseFileInput(content)
if err != nil {
t.Fatalf("parseFileInput() error = %v", err)
}
if !jsonInput {
t.Fatalf("jsonInput = false, want true")
}
if len(urls) != 0 {
t.Fatalf("urls len = %d, want 0", len(urls))
}
if repeated != 0 {
t.Fatalf("repeated = %d, want 0", repeated)
}
if len(items) != 2 {
t.Fatalf("items len = %d, want 2", len(items))
}
if items[0].Source != "qobuz" || items[0].MediaType != "album" || items[0].ID != "0066991040005" {
t.Fatalf("unexpected first item: %+v", items[0])
}
if items[1].Source != "tidal" || items[1].MediaType != "track" || items[1].ID != "3083287" {
t.Fatalf("unexpected second item: %+v", items[1])
}
}
func TestParseFileInputTextURLsDedupes(t *testing.T) {
content := []byte("https://tidal.com/browse/track/3083287\nhttps://tidal.com/browse/track/3083287\nhttps://www.qobuz.com/fr-fr/album/example/0066991040005\n")
items, urls, repeated, jsonInput, err := parseFileInput(content)
if err != nil {
t.Fatalf("parseFileInput() error = %v", err)
}
if jsonInput {
t.Fatalf("jsonInput = true, want false")
}
if len(items) != 0 {
t.Fatalf("items len = %d, want 0", len(items))
}
if repeated != 1 {
t.Fatalf("repeated = %d, want 1", repeated)
}
if len(urls) != 2 {
t.Fatalf("urls len = %d, want 2", len(urls))
}
}
func TestParseFileInputRejectsInvalidJSONShape(t *testing.T) {
content := []byte(`{"source":"qobuz","media_type":"track","id":"1"}`)
_, _, _, jsonInput, err := parseFileInput(content)
if err == nil {
t.Fatalf("expected error for non-array json")
}
if !jsonInput {
t.Fatalf("jsonInput = false, want true")
}
}
func TestParseLastFMArgsDefaults(t *testing.T) {
opts, err := parseLastFMArgs([]string{"https://www.last.fm/user/x/playlists/123"}, "qobuz", "")
if err != nil {
t.Fatalf("parseLastFMArgs() error = %v", err)
}
if opts.Source != "qobuz" {
t.Fatalf("source = %q, want qobuz", opts.Source)
}
if opts.PlaylistURL == "" {
t.Fatalf("playlist url should not be empty")
}
}
func TestParseLastFMArgsOptions(t *testing.T) {
opts, err := parseLastFMArgs([]string{"--source", "tidal", "--fallback-source", "qobuz", "https://www.last.fm/user/x/playlists/123"}, "qobuz", "")
if err != nil {
t.Fatalf("parseLastFMArgs() error = %v", err)
}
if opts.Source != "tidal" || opts.FallbackSource != "qobuz" {
t.Fatalf("unexpected options: %+v", opts)
}
}
func TestExtractLastFMPlaylistInfoAndPairs(t *testing.T) {
html := `<h1 class="playlisting-playlist-header-title">Road &amp; Rain</h1>
<div data-playlisting-entry-count="2"></div>
<a href="/music/a" title="Dreams"></a>
<a href="/music/b" title="Fleetwood Mac"></a>
<a href="/music/c" title="Go Your Own Way"></a>
<a href="/music/d" title="Fleetwood Mac"></a>`
title, total, err := extractLastFMPlaylistInfo(html)
if err != nil {
t.Fatalf("extractLastFMPlaylistInfo() error = %v", err)
}
if title != "Road & Rain" {
t.Fatalf("title = %q, want %q", title, "Road & Rain")
}
if total != 2 {
t.Fatalf("total = %d, want 2", total)
}
pairs := extractLastFMTitleArtistPairs(html)
if len(pairs) != 2 {
t.Fatalf("pairs len = %d, want 2", len(pairs))
}
if pairs[0].Title != "Dreams" || pairs[0].Artist != "Fleetwood Mac" {
t.Fatalf("unexpected first pair: %+v", pairs[0])
}
}
func TestParseGlobalArgsNoDBBeforeCommand(t *testing.T) {
opts, err := parseGlobalArgs([]string{"-ndb", "url", "https://play.qobuz.com/album/0004228000522"})
if err != nil {
t.Fatalf("parseGlobalArgs() error = %v", err)
}
if !opts.noDB {
t.Fatalf("expected noDB true")
}
if opts.command != "url" {
t.Fatalf("command = %q, want %q", opts.command, "url")
}
if len(opts.commandArgs) != 1 {
t.Fatalf("command args len = %d, want 1", len(opts.commandArgs))
}
}
func TestParseGlobalArgsAllOfficialFlags(t *testing.T) {
opts, err := parseGlobalArgs([]string{
"--config-path", "/tmp/custom.toml",
"-f", "/tmp/music",
"--no-db",
"-q", "3",
"-c", "ogg",
"--no-progress",
"--no-ssl-verify",
"-v",
"search", "tidal", "track", "dreams",
})
if err != nil {
t.Fatalf("parseGlobalArgs() error = %v", err)
}
if opts.configPath != "/tmp/custom.toml" || opts.folder != "/tmp/music" {
t.Fatalf("unexpected path/folder opts: %+v", opts)
}
if !opts.noDB || !opts.qualitySet || opts.quality != 3 || !opts.codecSet || opts.codec != "VORBIS" {
t.Fatalf("unexpected quality/codec/db opts: %+v", opts)
}
if !opts.noProgress || !opts.noSSLVerify || !opts.verbose {
t.Fatalf("unexpected boolean opts: %+v", opts)
}
if opts.command != "search" {
t.Fatalf("command = %q, want search", opts.command)
}
}
func TestNormalizeCodecRejectsUnknown(t *testing.T) {
if _, err := normalizeCodec("wav"); err == nil {
t.Fatalf("expected error for unsupported codec")
}
}
func TestGroupLastFMResolvedTracksBySourcePreservesOrderAndDuplicates(t *testing.T) {
resolved := []resolvedLastFMTrack{
{Source: "tidal", ID: "1"},
{Source: "tidal", ID: "1"},
{Source: "qobuz", ID: "2"},
{Source: "tidal", ID: "3"},
{Source: "", ID: "4"},
}
groups := groupLastFMResolvedTracksBySource(resolved)
if len(groups["tidal"]) != 3 {
t.Fatalf("tidal ids len = %d, want 3", len(groups["tidal"]))
}
if len(groups["qobuz"]) != 1 {
t.Fatalf("qobuz ids len = %d, want 1", len(groups["qobuz"]))
}
if groups["tidal"][0] != "1" || groups["tidal"][1] != "1" || groups["tidal"][2] != "3" {
t.Fatalf("unexpected tidal ordering: %+v", groups["tidal"])
}
}
func TestExtractLastFMTracksFromMirrorMarkdown(t *testing.T) {
md := `Title: My Playlist | user playlists | Last.fm
| Play | Image | Loved | Name | Artist name | Buy | Options | Duration |
| --- | --- | --- | --- | --- | --- | --- | --- |
| [Play track](https://x) | [img](https://i) | x | [Song A](https://a) | [Artist A](https://aa) | | | 3:00 |
| [Play track](https://x) | [img](https://i) | x | [Song B](https://b) | [Artist B](https://bb) | | | 4:00 |`
title, tracks := extractLastFMTracksFromMirrorMarkdown(md)
if title != "My Playlist" {
t.Fatalf("title = %q, want %q", title, "My Playlist")
}
if len(tracks) != 2 {
t.Fatalf("tracks len = %d, want 2", len(tracks))
}
if tracks[0].Title != "Song A" || tracks[0].Artist != "Artist A" {
t.Fatalf("unexpected first track: %+v", tracks[0])
}
}