mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-10-16 22:07:30 +02:00
refactor: delete dead code, dedupe helpers, add tests for untested packages
Dead code Removed, each verified to have no non-test caller: promptSearchSelection (77 lines, superseded by promptSearchSelectionMenu), AddPlaylistByTrackIDs, download.NewWithVerifySSL, qobuz.Client.GetTrackMetadata, tidal.Client.trackSupportsAtmos (test-only, and the comment above GetDownloadable already documented its removal from the live path) and its now-orphaned test, yandex.canonicalTrackRequestID (a no-op returning its trimmed input; the call site uses strings.TrimSpace directly) and the unused defaultEstimatedKbps constant. Config keys that did nothing qobuz.download_booklets and misc.check_for_updates were declared, defaulted and documented in config.toml.example but read by no code. The whole [youtube] block went too: there is no YouTube provider. go-toml ignores unknown keys, verified, so existing user configs still load. Duplicated helpers tidal.stringify and yandex.firstNonEmpty were byte-identical copies of jsonutil.StringFromAny and jsonutil.FirstNonEmpty. displayArtistNames existed three times, byte-identical, in internal/app, provider/yandex and provider/beatport; it now lives in jsonutil. The CLI's normalizeCodec delegates to config.NormalizeCodec so the flag and the config file are checked by the same rules. Tests for packages that had none internal/jsonutil (0% -> 100%) and internal/netutil (0% -> 78%), the latter covering redactURL, which is a security control, and the zero-timeout contract that internal/download and the qobuz body copy depend on. internal/config rose to 82% with coverage of the new Validate(), the 0600 permissions, CloneConfigData not sharing slices, and a concurrent UpdateTokens test that exercises the mutex under -race. Test suite is ~85x faster Provider tests built clients with the default 60 requests/minute, so the rate limiter slept 1s per HTTP call: deezer took 31s, tidal 11s, qobuz 8s. Each provider test package now builds its config through a testConfigData helper that disables throttling. Whole suite: ~60s -> 0.7s, and 2.8s under -race. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
19 files changed
+701
-392
No files matched your search
@@ -0,0 +1,148 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRedactURLHidesCredentials(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
raw string
|
||||
contains []string
|
||||
absent []string
|
||||
}{
|
||||
{
|
||||
name: "user auth token",
|
||||
raw: "https://api.example.com/track?user_auth_token=sekrit&track_id=42",
|
||||
contains: []string{"user_auth_token=REDACTED", "track_id=42"},
|
||||
absent: []string{"sekrit"},
|
||||
},
|
||||
{
|
||||
name: "request signature",
|
||||
raw: "https://api.example.com/x?request_sig=abc123&app_id=9",
|
||||
contains: []string{"request_sig=REDACTED", "app_id=9"},
|
||||
absent: []string{"abc123"},
|
||||
},
|
||||
{
|
||||
name: "several at once",
|
||||
raw: "https://api.example.com/x?password=p&access_token=a&refresh_token=r&id=1",
|
||||
contains: []string{"password=REDACTED", "access_token=REDACTED", "refresh_token=REDACTED", "id=1"},
|
||||
absent: []string{"password=p", "access_token=a", "refresh_token=r"},
|
||||
},
|
||||
{
|
||||
name: "nothing sensitive is left untouched",
|
||||
raw: "https://api.example.com/track/42?limit=10",
|
||||
contains: []string{"limit=10", "/track/42"},
|
||||
},
|
||||
{
|
||||
name: "no query string",
|
||||
raw: "https://api.example.com/track/42",
|
||||
contains: []string{"https://api.example.com/track/42"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
u, err := url.Parse(tc.raw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := redactURL(u)
|
||||
for _, want := range tc.contains {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Errorf("redactURL(%q) = %q, missing %q", tc.raw, got, want)
|
||||
}
|
||||
}
|
||||
for _, bad := range tc.absent {
|
||||
if strings.Contains(got, bad) {
|
||||
t.Errorf("redactURL(%q) = %q, leaked %q", tc.raw, got, bad)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRedactURLNil(t *testing.T) {
|
||||
if got := redactURL(nil); got != "" {
|
||||
t.Errorf("redactURL(nil) = %q, want empty", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSensitiveParam(t *testing.T) {
|
||||
sensitive := []string{
|
||||
"user_auth_token", "api_token", "access_token", "refresh_token",
|
||||
"request_sig", "signature", "password", "secret", "token", "code", "auth", "key",
|
||||
}
|
||||
for _, name := range sensitive {
|
||||
if !isSensitiveParam(name) {
|
||||
t.Errorf("isSensitiveParam(%q) = false, want true", name)
|
||||
}
|
||||
}
|
||||
for _, name := range []string{"track_id", "limit", "offset", "app_id", "quality"} {
|
||||
if isSensitiveParam(name) {
|
||||
t.Errorf("isSensitiveParam(%q) = true, want false", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewHTTPClientHonoursVerifySSL(t *testing.T) {
|
||||
insecure := NewHTTPClient(5*time.Second, false, 4)
|
||||
tr, ok := insecure.Transport.(*loggingTransport)
|
||||
if !ok {
|
||||
t.Fatalf("transport is %T, want *loggingTransport", insecure.Transport)
|
||||
}
|
||||
base, ok := tr.base.(*http.Transport)
|
||||
if !ok {
|
||||
t.Fatalf("base transport is %T", tr.base)
|
||||
}
|
||||
if !base.TLSClientConfig.InsecureSkipVerify {
|
||||
t.Error("verifySSL=false should set InsecureSkipVerify")
|
||||
}
|
||||
|
||||
secure := NewHTTPClient(5*time.Second, true, 4)
|
||||
base = secure.Transport.(*loggingTransport).base.(*http.Transport)
|
||||
if base.TLSClientConfig.InsecureSkipVerify {
|
||||
t.Error("verifySSL=true must not skip verification")
|
||||
}
|
||||
if secure.Timeout != 5*time.Second {
|
||||
t.Errorf("Timeout = %v, want 5s", secure.Timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewHTTPClientZeroTimeoutMeansNoTimeout(t *testing.T) {
|
||||
// internal/download and the qobuz body copy rely on this: a zero timeout
|
||||
// must not be replaced with a default, or long track downloads abort.
|
||||
c := NewHTTPClient(0, true, 0)
|
||||
if c.Timeout != 0 {
|
||||
t.Errorf("Timeout = %v, want 0", c.Timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewHTTPClientDefaultsMaxConns(t *testing.T) {
|
||||
c := NewHTTPClient(time.Second, true, 0)
|
||||
base := c.Transport.(*loggingTransport).base.(*http.Transport)
|
||||
if base.MaxIdleConnsPerHost != defaultMaxConnsPerHost {
|
||||
t.Errorf("MaxIdleConnsPerHost = %d, want %d", base.MaxIdleConnsPerHost, defaultMaxConnsPerHost)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientPerformsRequest(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusTeapot)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
resp, err := NewHTTPClient(5*time.Second, true, 2).Get(ts.URL + "/x?token=sekrit")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
if resp.StatusCode != http.StatusTeapot {
|
||||
t.Errorf("status = %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user