package web import ( "context" "errors" "net/http" "net/http/httptest" "net/url" "strings" "testing" "music-server/internal/backend" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var soundtracks = []string{ "Call of Duty: Modern Warfare", "Final Fantasy VII", "Joe & Mac", "Kingdom Hearts", "Lemmings (SNES + Mega Drive)", "MG", "Metal Gear Solid 4 - Guns of the Patriots", "Pokémon HeartGold & SoulSilver", "Resident Evil 2 (Remake)", "Ys III - Wanderers from Ys (+The Oath in Felghana)", } func newCache(names []string) *backend.SearchCache { return backend.NewSearchCache(func(context.Context) ([]string, error) { return names, nil }) } func searchData(t *testing.T) []backend.SoundtrackData { data, err := newCache(soundtracks).Get(context.Background()) require.NoError(t, err) return data } func TestSearch(t *testing.T) { data := searchData(t) tests := []struct { query string want []string }{ // Exact (case-insensitive) substring {"resident", []string{"Resident Evil 2 (Remake)"}}, // Ignoring spaces and special characters {"joeandmac", []string{"Joe & Mac"}}, {"snes+mega", []string{"Lemmings (SNES + Mega Drive)"}}, {"pokemon", []string{"Pokémon HeartGold & SoulSilver"}}, // Letters in order {"codmw", []string{"Call of Duty: Modern Warfare"}}, {"mgs4gotp", []string{"Metal Gear Solid 4 - Guns of the Patriots"}}, {"re2r", []string{"Resident Evil 2 (Remake)"}}, // No match {"zzz", nil}, // Regex special characters are plain text {"(remake)", []string{"Resident Evil 2 (Remake)"}}, {"[", nil}, } for _, tt := range tests { t.Run(tt.query, func(t *testing.T) { assert.Equal(t, tt.want, search(tt.query, data)) }) } } func TestSearchEmptyReturnsEverything(t *testing.T) { assert.Equal(t, soundtracks, search("", searchData(t))) } func TestSearchOrdering(t *testing.T) { // "MG" is an exact match, "Metal Gear Solid..." only matches by letters in order. results := search("mg", searchData(t)) require.GreaterOrEqual(t, len(results), 2) assert.Equal(t, "MG", results[0]) assert.Contains(t, results, "Metal Gear Solid 4 - Guns of the Patriots") } func TestSearchHandler(t *testing.T) { handler := NewSearchHandler(newCache(soundtracks)) req := httptest.NewRequest(http.MethodPost, "/find", strings.NewReader(url.Values{"search_term": {"kingdom"}}.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") rec := httptest.NewRecorder() handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusOK, rec.Code) assert.Contains(t, rec.Body.String(), "Kingdom Hearts") assert.NotContains(t, rec.Body.String(), "Final Fantasy") } func TestSearchHandlerLoadError(t *testing.T) { cache := backend.NewSearchCache(func(context.Context) ([]string, error) { return nil, errors.New("db down") }) rec := httptest.NewRecorder() NewSearchHandler(cache).ServeHTTP(rec, httptest.NewRequest(http.MethodPost, "/find", nil)) assert.Equal(t, http.StatusInternalServerError, rec.Code) }