Files
wehub-resource-sync ead81af521
Deploy to GitHub Pages / deploy (push) Failing after 0s
CI / go (push) Has been cancelled
CI / build (darwin, macos-14) (push) Has been cancelled
CI / build (windows, windows-2025) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:31:13 +08:00

169 lines
5.1 KiB
Go

package netease
import (
"context"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"github.com/bjarneo/cliamp/provider"
)
func TestPlaylistsIncludesAccountListsAndCharts(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/user/playlist" {
t.Fatalf("unexpected path %s", r.URL.Path)
}
if got := r.URL.Query().Get("uid"); got != "42" {
t.Fatalf("uid = %q, want 42", got)
}
w.Write([]byte(`{"code":200,"playlist":[
{"id":10,"name":"Daily Picks","userId":42,"trackCount":12,"specialType":5},
{"id":11,"name":"Road Trip","userId":42,"trackCount":8,"specialType":0},
{"id":12,"name":"Saved Mix","userId":99,"trackCount":20,"specialType":0}
]}`))
}))
defer srv.Close()
p := newWithBase(Config{Enabled: true, UserID: "42"}, srv.URL)
lists, err := p.Playlists()
if err != nil {
t.Fatalf("Playlists() error = %v", err)
}
if len(lists) != 7 {
t.Fatalf("got %d playlists, want 7", len(lists))
}
if lists[0].ID != "user:10" || lists[0].Name != "Liked Songs" || lists[0].Section != "My Playlists" {
t.Fatalf("liked playlist = %+v", lists[0])
}
if lists[2].Section != "Saved Playlists" {
t.Fatalf("saved playlist section = %q", lists[2].Section)
}
if lists[3].ID != "chart:3778678" || lists[3].Section != "Charts" {
t.Fatalf("first chart = %+v", lists[3])
}
}
func TestTracksMapsSongs(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/playlist/detail" {
t.Fatalf("unexpected path %s", r.URL.Path)
}
if got := r.URL.Query().Get("id"); got != "10" {
t.Fatalf("id = %q, want 10", got)
}
w.Write([]byte(`{"code":200,"result":{"tracks":[
{"id":100,"name":"First Track","duration":123456,"no":3,
"artists":[{"name":"Artist One"},{"name":"Artist Two"}],
"album":{"name":"Album One"}}
]}}`))
}))
defer srv.Close()
p := newWithBase(Config{Enabled: true}, srv.URL)
tracks, err := p.Tracks("user:10")
if err != nil {
t.Fatalf("Tracks() error = %v", err)
}
if len(tracks) != 1 {
t.Fatalf("got %d tracks, want 1", len(tracks))
}
tr := tracks[0]
if tr.Path != "https://music.163.com/#/song?id=100" {
t.Fatalf("Path = %q", tr.Path)
}
if tr.Artist != "Artist One, Artist Two" || tr.Album != "Album One" {
t.Fatalf("metadata = artist %q album %q", tr.Artist, tr.Album)
}
if tr.DurationSecs != 124 {
t.Fatalf("DurationSecs = %d, want 124", tr.DurationSecs)
}
if tr.Meta(provider.MetaNetEaseID) != "100" {
t.Fatalf("MetaNetEaseID = %q", tr.Meta(provider.MetaNetEaseID))
}
}
func TestSearchTracks(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/search/get/web" {
t.Fatalf("unexpected path %s", r.URL.Path)
}
if got := r.URL.Query().Get("s"); got != "query" {
t.Fatalf("search query = %q, want query", got)
}
if got := r.URL.Query().Get("limit"); got != "5" {
t.Fatalf("limit = %q, want 5", got)
}
w.Write([]byte(`{"code":200,"result":{"songs":[
{"id":200,"name":"Search Hit","duration":1000,
"artists":[{"name":"Artist"}],"album":{"name":"Album"}}
]}}`))
}))
defer srv.Close()
p := newWithBase(Config{Enabled: true}, srv.URL)
tracks, err := p.SearchTracks(context.Background(), " query ", 5)
if err != nil {
t.Fatalf("SearchTracks() error = %v", err)
}
if len(tracks) != 1 || tracks[0].Title != "Search Hit" {
t.Fatalf("tracks = %+v", tracks)
}
}
func TestCookieHeaderFromNetscapeFileFiltersNetEaseCookies(t *testing.T) {
path := t.TempDir() + "/cookies.txt"
data := strings.Join([]string{
"# Netscape HTTP Cookie File",
".music.163.com\tTRUE\t/\tTRUE\t0\tMUSIC_U\tabc",
"#HttpOnly_.163.com\tTRUE\t/\tTRUE\t0\t__csrf\tdef",
".example.com\tTRUE\t/\tTRUE\t0\tOTHER\tignored",
"",
}, "\n")
if err := osWriteFile(path, data); err != nil {
t.Fatal(err)
}
header, err := cookieHeaderFromNetscapeFile(path)
if err != nil {
t.Fatalf("cookieHeaderFromNetscapeFile() error = %v", err)
}
if header != "MUSIC_U=abc; __csrf=def" {
t.Fatalf("header = %q", header)
}
}
func TestExtractBrowserCookieHeaderMissingYTDLPShowsInstallHint(t *testing.T) {
t.Setenv("PATH", t.TempDir())
_, err := extractBrowserCookieHeader(context.Background(), "chrome")
if err == nil {
t.Fatal("extractBrowserCookieHeader() error = nil, want missing yt-dlp error")
}
msg := err.Error()
if !strings.HasPrefix(msg, "yt-dlp not found. Install with: ") {
t.Fatalf("error = %q", msg)
}
if strings.TrimPrefix(msg, "yt-dlp not found. Install with: ") == "" {
t.Fatalf("missing install hint in error = %q", msg)
}
}
func TestLiveCheckLoginWithBrowser(t *testing.T) {
browser := os.Getenv("CLIAMP_NETEASE_LIVE_BROWSER")
if browser == "" {
t.Skip("set CLIAMP_NETEASE_LIVE_BROWSER to run live browser-cookie check")
}
acc, err := CheckLogin(context.Background(), browser)
if err != nil {
t.Fatalf("CheckLogin() error = %v", err)
}
if acc.UserID == "" {
t.Fatal("CheckLogin() returned empty user id")
}
}
func osWriteFile(path, data string) error {
return os.WriteFile(path, []byte(data), 0o644)
}