package jsonutil import "testing" func TestStringFromAny(t *testing.T) { tests := []struct { name string in any want string }{ {"string", "hi", "hi"}, {"int", 7, "7"}, {"int64", int64(8), "8"}, {"float whole", float64(9), "9"}, {"float fractional", 44.1, "44.1"}, {"float no trailing zeroes", float64(100), "100"}, {"nil", nil, ""}, {"bool unsupported", true, ""}, {"map unsupported", map[string]any{}, ""}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if got := StringFromAny(tc.in); got != tc.want { t.Fatalf("StringFromAny(%#v) = %q, want %q", tc.in, got, tc.want) } }) } } func TestIntFromAny(t *testing.T) { tests := []struct { in any want int }{ {5, 5}, {int32(6), 6}, {int64(7), 7}, {8.9, 8}, {"10", 10}, {" 11 ", 11}, {"not a number", 0}, {nil, 0}, {true, 0}, } for _, tc := range tests { if got := IntFromAny(tc.in); got != tc.want { t.Errorf("IntFromAny(%#v) = %d, want %d", tc.in, got, tc.want) } } } func TestFloatFromAny(t *testing.T) { tests := []struct { in any want float64 }{ {1.5, 1.5}, {2, 2}, {int64(3), 3}, {"4.5", 0}, // strings are deliberately not parsed here {nil, 0}, } for _, tc := range tests { if got := FloatFromAny(tc.in); got != tc.want { t.Errorf("FloatFromAny(%#v) = %v, want %v", tc.in, got, tc.want) } } } func TestBoolFromAny(t *testing.T) { tests := []struct { in any want bool }{ {true, true}, {false, false}, {"true", true}, {"TRUE", true}, {" yes ", true}, {"1", true}, {"0", false}, {"no", false}, {1, true}, {0, false}, {int64(2), true}, {0.0, false}, {1.5, true}, {nil, false}, } for _, tc := range tests { if got := BoolFromAny(tc.in); got != tc.want { t.Errorf("BoolFromAny(%#v) = %v, want %v", tc.in, got, tc.want) } } } func TestFirstNonEmpty(t *testing.T) { if got := FirstNonEmpty("", " ", "found", "later"); got != "found" { t.Errorf("got %q", got) } if got := FirstNonEmpty(" trimmed "); got != "trimmed" { t.Errorf("got %q, want trimmed", got) } if got := FirstNonEmpty("", " "); got != "" { t.Errorf("got %q, want empty", got) } if got := FirstNonEmpty(); got != "" { t.Errorf("got %q, want empty", got) } } func TestNestedMap(t *testing.T) { m := map[string]any{"album": map[string]any{"title": "X"}, "n": 1} if got := NestedMap(m, "album")["title"]; got != "X" { t.Errorf("got %v", got) } if got := NestedMap(m, "missing"); len(got) != 0 { t.Errorf("missing key should give empty map, got %v", got) } if got := NestedMap(m, "n"); len(got) != 0 { t.Errorf("non-map value should give empty map, got %v", got) } } func TestNestedAnyAndString(t *testing.T) { m := map[string]any{ "album": map[string]any{ "artist": map[string]any{"name": "Nina Simone", "id": float64(42)}, }, } if got := NestedString(m, "album", "artist", "name"); got != "Nina Simone" { t.Errorf("got %q", got) } if got := NestedString(m, "album", "artist", "id"); got != "42" { t.Errorf("numeric id = %q, want 42", got) } if got := NestedString(m, "album", "nope", "name"); got != "" { t.Errorf("missing path = %q, want empty", got) } if got := NestedAny(m, "album", "artist", "name"); got != "Nina Simone" { t.Errorf("got %v", got) } if got := NestedAny(m); got == nil { t.Error("no keys should return the map itself") } // Traversing through a non-map must not panic. if got := NestedAny(m, "album", "artist", "name", "deeper"); got != nil { t.Errorf("over-deep path = %v, want nil", got) } } func TestTitleCase(t *testing.T) { tests := []struct{ in, want string }{ {"qobuz", "Qobuz"}, {"Tidal", "Tidal"}, {"", ""}, {"1st", "1st"}, {"ä", "ä"}, // only ASCII is uppercased } for _, tc := range tests { if got := TitleCase(tc.in); got != tc.want { t.Errorf("TitleCase(%q) = %q, want %q", tc.in, got, tc.want) } } } func TestDisplayArtistNames(t *testing.T) { tests := []struct { in []string want string }{ {nil, ""}, {[]string{}, ""}, {[]string{"A"}, "A"}, {[]string{"A", "B"}, "A & B"}, {[]string{"A", "B", "C"}, "A, B & C"}, {[]string{"A", "B", "C", "D"}, "A, B, C & D"}, } for _, tc := range tests { if got := DisplayArtistNames(tc.in); got != tc.want { t.Errorf("DisplayArtistNames(%v) = %q, want %q", tc.in, got, tc.want) } } }