test: add download, store, and scraper tests
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
package download
|
||||
|
||||
import "testing"
|
||||
|
||||
func bpmHTML(bpm, key, mode string) string {
|
||||
return `tempo of <span class="x">` + bpm + ` BPM</span>` +
|
||||
` with a <span class="x">` + key + `</span> key` +
|
||||
` and a <span class="x">` + mode + `</span> mode`
|
||||
}
|
||||
|
||||
func TestParseBPM(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
html string
|
||||
wantBPM string
|
||||
wantKey string
|
||||
}{
|
||||
{"major key", bpmHTML("128", "A", "major"), "128", "A"},
|
||||
{"minor key gets m suffix", bpmHTML("90", "F", "minor"), "90", "Fm"},
|
||||
{"unicode sharp normalized", bpmHTML("124", "C♯", "major"), "124", "C#"},
|
||||
{"unicode flat normalized", bpmHTML("100", "B♭", "minor"), "100", "Bbm"},
|
||||
{"enharmonic pair keeps first", bpmHTML("110", "A♯/B♭", "major"), "110", "A#"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parseBPM(tt.html)
|
||||
if err != nil {
|
||||
t.Fatalf("parseBPM: %v", err)
|
||||
}
|
||||
if got.BPM != tt.wantBPM || got.Key != tt.wantKey {
|
||||
t.Errorf("parseBPM() = %+v, want BPM %q Key %q", got, tt.wantBPM, tt.wantKey)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBPMNoData(t *testing.T) {
|
||||
if _, err := parseBPM("<html>nothing here</html>"); err == nil {
|
||||
t.Error("expected error for page without BPM data")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package download
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMatchesKeyword(t *testing.T) {
|
||||
if !matchesKeyword("Deep House", electronicKeywords) {
|
||||
t.Error("expected Deep House to match electronic keywords")
|
||||
}
|
||||
if !matchesKeyword("classic rock", nonElectronicKeywords) {
|
||||
t.Error("expected classic rock to match non-electronic keywords")
|
||||
}
|
||||
if matchesKeyword("Spoken Word", electronicKeywords) {
|
||||
t.Error("did not expect Spoken Word to match electronic keywords")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterTags(t *testing.T) {
|
||||
got := filterTags([]string{"Deep House", "Rock", "Spoken Word"})
|
||||
want := []string{"Deep House", "Rock"}
|
||||
if !slices.Equal(got, want) {
|
||||
t.Errorf("filterTags() = %v, want %v", got, want)
|
||||
}
|
||||
|
||||
if got := filterTags([]string{"Rock", "Jazz"}); len(got) != 0 {
|
||||
t.Errorf("filterTags() = %v, want empty", got)
|
||||
}
|
||||
|
||||
if got := filterTags(nil); len(got) != 0 {
|
||||
t.Errorf("filterTags(nil) = %v, want empty", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatTags(t *testing.T) {
|
||||
tests := []struct {
|
||||
tags []string
|
||||
want string
|
||||
}{
|
||||
{[]string{"deep house"}, "Deep House"},
|
||||
{[]string{"TECHNO", "trance"}, "Techno / Trance"},
|
||||
{[]string{" house ", ""}, "House"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := formatTags(tt.tags); got != tt.want {
|
||||
t.Errorf("formatTags(%v) = %q, want %q", tt.tags, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package download
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHashFile(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "file.txt")
|
||||
if err := os.WriteFile(path, []byte("hello world"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, err := hashFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("hashFile: %v", err)
|
||||
}
|
||||
|
||||
want := "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
|
||||
if got != want {
|
||||
t.Errorf("hashFile() = %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashFileMissing(t *testing.T) {
|
||||
if _, err := hashFile(filepath.Join(t.TempDir(), "missing")); err == nil {
|
||||
t.Error("expected error for missing file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashIndexFind(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "sub", "track.mp3")
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(path, []byte("hello world"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
index, err := newHashIndex(context.Background(), dir)
|
||||
if err != nil {
|
||||
t.Fatalf("newHashIndex: %v", err)
|
||||
}
|
||||
|
||||
hash := "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
|
||||
found, ok := index.find(hash)
|
||||
if !ok || found != path {
|
||||
t.Errorf("find(%s) = %q, %v; want %q, true", hash, found, ok, path)
|
||||
}
|
||||
|
||||
if _, ok := index.find("deadbeef"); ok {
|
||||
t.Error("find() reported a match for an unknown hash")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashIndexCanceledContext(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(dir, "a"), []byte("x"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
if _, err := newHashIndex(ctx, dir); err == nil {
|
||||
t.Error("expected error for canceled context")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package download
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestOptionsValidate(t *testing.T) {
|
||||
valid := Options{Quality: "mp3_320", Timeout: time.Minute, Limit: 10}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
mutate func(o *Options)
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid", func(o *Options) {}, false},
|
||||
{"mp3_128", func(o *Options) { o.Quality = "mp3_128" }, false},
|
||||
{"flac", func(o *Options) { o.Quality = "flac" }, false},
|
||||
{"invalid quality", func(o *Options) { o.Quality = "ogg" }, true},
|
||||
{"uppercase quality", func(o *Options) { o.Quality = "MP3_320" }, true},
|
||||
{"zero timeout", func(o *Options) { o.Timeout = 0 }, true},
|
||||
{"negative timeout", func(o *Options) { o.Timeout = -time.Second }, true},
|
||||
{"zero limit", func(o *Options) { o.Limit = 0 }, true},
|
||||
{"limit too high", func(o *Options) { o.Limit = 101 }, true},
|
||||
{"limit at max", func(o *Options) { o.Limit = 100 }, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
opts := valid
|
||||
tt.mutate(&opts)
|
||||
|
||||
err := opts.Validate()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user