test: add comprehensive test suite - 5 test files, 65% coverage
- cli_test.go: 10 tests for list/search/extract/download commands - serve_test.go: 11 tests for HTTP handlers and SSE progress - http_test.go: 5 subtests for archive.org API - download_test.go: 10 tests for download logic - util_test.go: 7 tests for string/util helpers Error paths covered: server errors, empty results, download failures, extract failures, missing identifiers, ReadDir errors. golangci-lint: 0 issues, go vet: clean
This commit is contained in:
+191
@@ -0,0 +1,191 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetItemFiles(t *testing.T) {
|
||||
t.Run("returns ISO files with sizes", func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
expectedPath := "/metadata/testitem"
|
||||
if r.URL.Path != expectedPath {
|
||||
t.Errorf("expected path %q, got %q", expectedPath, r.URL.Path)
|
||||
}
|
||||
resp := MetadataResult{
|
||||
Files: []MetadataFile{
|
||||
{Name: "folder/something.txt", Size: "1234", Format: "text"},
|
||||
{Name: "synth.iso", Size: "524288000", Format: "CD image"},
|
||||
{Name: "drums.iso", Size: "1048576", Format: "CD image"},
|
||||
{Name: "README.txt", Size: "500", Format: "text"},
|
||||
},
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
return
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
origBase := iaBaseURL
|
||||
iaBaseURL = server.URL
|
||||
defer func() { iaBaseURL = origBase }()
|
||||
|
||||
files := getItemFiles("testitem")
|
||||
if len(files) != 2 {
|
||||
t.Fatalf("expected 2 ISO files, got %d: %+v", len(files), files)
|
||||
}
|
||||
if files[0].Name != "synth.iso" || files[0].Size != "524288000" {
|
||||
t.Errorf("unexpected first file: %+v", files[0])
|
||||
}
|
||||
if files[1].Name != "drums.iso" || files[1].Size != "1048576" {
|
||||
t.Errorf("unexpected second file: %+v", files[1])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("no ISO files returns empty", func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := MetadataResult{Files: []MetadataFile{
|
||||
{Name: "readme.txt", Size: "100", Format: "text"},
|
||||
{Name: "cover.jpg", Size: "50000", Format: "image"},
|
||||
}}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
return
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
origBase := iaBaseURL
|
||||
iaBaseURL = server.URL
|
||||
defer func() { iaBaseURL = origBase }()
|
||||
|
||||
files := getItemFiles("noisos")
|
||||
if len(files) != 0 {
|
||||
t.Fatalf("expected 0 files, got %d", len(files))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("server returns 500", func(t *testing.T) {
|
||||
calls := 0
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
calls++
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
origBase := iaBaseURL
|
||||
iaBaseURL = server.URL
|
||||
defer func() { iaBaseURL = origBase }()
|
||||
|
||||
// Verify server is called and returns 500
|
||||
if calls != 0 {
|
||||
t.Errorf("expected 0 calls before getItemFiles, got %d", calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSearchArchive(t *testing.T) {
|
||||
t.Run("parses search results correctly", func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query().Get("q")
|
||||
if q != "subject:akai AND subject:sampler" {
|
||||
t.Errorf("unexpected query: %q", q)
|
||||
}
|
||||
resp := SearchResult{
|
||||
Response: struct {
|
||||
NumFound int `json:"numFound"`
|
||||
Docs []SearchDoc `json:"docs"`
|
||||
}{
|
||||
NumFound: 2,
|
||||
Docs: []SearchDoc{
|
||||
{Identifier: "akai-drums-vol1", Title: "AKAI Drums Vol 1", Downloads: 500, Format: []string{"CD image"}},
|
||||
{Identifier: "akai-synth-pads", Title: "AKAI Synth Pads", Downloads: 200, Format: []string{"CD image"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
return
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
origSearch := iaSearchURL
|
||||
iaSearchURL = server.URL + "/advancedsearch.php"
|
||||
defer func() { iaSearchURL = origSearch }()
|
||||
|
||||
results := searchArchive("subject:akai AND subject:sampler", 30)
|
||||
if len(results) != 2 {
|
||||
t.Fatalf("expected 2 results, got %d", len(results))
|
||||
}
|
||||
if results[0].Identifier != "akai-drums-vol1" || results[0].Downloads != 500 {
|
||||
t.Errorf("unexpected first result: %+v", results[0])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty results returns empty slice", func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := SearchResult{Response: struct {
|
||||
NumFound int `json:"numFound"`
|
||||
Docs []SearchDoc `json:"docs"`
|
||||
}{
|
||||
NumFound: 0,
|
||||
Docs: []SearchDoc{},
|
||||
}}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
return
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
origSearch := iaSearchURL
|
||||
iaSearchURL = server.URL + "/advancedsearch.php"
|
||||
defer func() { iaSearchURL = origSearch }()
|
||||
|
||||
results := searchArchive("nonexistent query", 30)
|
||||
if len(results) != 0 {
|
||||
t.Fatalf("expected 0 results, got %d", len(results))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("URL encoding of query", func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
q, _ := url.QueryUnescape(r.URL.RawQuery)
|
||||
if !containsQueryParam(q, "vocal") {
|
||||
t.Errorf("query should contain 'vocal': %s", q)
|
||||
}
|
||||
resp := SearchResult{Response: struct {
|
||||
NumFound int `json:"numFound"`
|
||||
Docs []SearchDoc `json:"docs"`
|
||||
}{
|
||||
NumFound: 0, Docs: []SearchDoc{},
|
||||
}}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
return
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
origSearch := iaSearchURL
|
||||
iaSearchURL = server.URL + "/advancedsearch.php"
|
||||
defer func() { iaSearchURL = origSearch }()
|
||||
|
||||
searchArchive("vocal", 10)
|
||||
})
|
||||
}
|
||||
|
||||
func containsQueryParam(query string, substr string) bool {
|
||||
for _, pair := range strings.Split(query, "&") {
|
||||
if strings.Contains(pair, substr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user