package smartplaylist import ( "errors" "slices" "testing" "yellowjacket/backend/database" ) // The rule editor's value box asks the backend for what matches the // text typed (#279), rather than building every list from the whole // library loaded into the frontend. func TestSuggestValues(t *testing.T) { t.Parallel() db := database.NewTestDB(t) seedSmartPlaylistData(t, db) cases := []struct { field, needle string want []string }{ // Case-insensitive substring, distinct, sorted. {"artist", "q", []string{"QOTSA", "Queen"}}, {"album", "OPERA", []string{"A Night at the Opera"}}, // Genres come from the genre table, one value per genre, not // from a concatenated per-track column. {"genre", "rock", []string{ "Funk Rock", "Hard Rock", "Progressive Rock", "Rock", "Stoner Rock", }}, // Years are numbers and are suggested as their text. {"year", "199", []string{"1990", "1991"}}, // The needle is literal: a LIKE wildcard in it matches only itself. {"title", "%", []string{}}, {"composer", "_", []string{}}, } for _, tc := range cases { got, err := SuggestValues(db, tc.field, tc.needle) if err != nil { t.Fatalf("SuggestValues(%q, %q): %v", tc.field, tc.needle, err) } if !slices.Equal(got, tc.want) { t.Errorf("SuggestValues(%q, %q) = %q, want %q", tc.field, tc.needle, got, tc.want) } } } func TestSuggestValuesRefusesFieldsItDoesNotSuggest(t *testing.T) { t.Parallel() db := database.NewTestDB(t) for _, field := range []string{"duration", "file_path", "title; DROP TABLE x"} { if _, err := SuggestValues(db, field, ""); !errors.Is(err, errNoSuggestions) { t.Errorf("SuggestValues(%q) err = %v, want errNoSuggestions", field, err) } } }