diff --git a/internal/subsonic/types_test.go b/internal/subsonic/types_test.go new file mode 100644 index 00000000..4ba10664 --- /dev/null +++ b/internal/subsonic/types_test.go @@ -0,0 +1,129 @@ +package subsonic + +import ( + "testing" + "time" + + "github.com/jackc/pgx/v5/pgtype" +) + +func TestUUIDRoundTrip(t *testing.T) { + cases := []string{ + "6ba7b810-9dad-11d1-80b4-00c04fd430c8", + "00000000-0000-0000-0000-000000000001", + } + for _, in := range cases { + u, ok := parseUUID(in) + if !ok { + t.Fatalf("parseUUID(%q) failed", in) + } + if got := uuidToID(u); got != in { + t.Errorf("uuidToID(parseUUID(%q)) = %q", in, got) + } + } +} + +func TestUUIDToIDZero(t *testing.T) { + var u pgtype.UUID + if got := uuidToID(u); got != "" { + t.Errorf("zero uuid → %q, want empty", got) + } +} + +func TestParseUUIDInvalid(t *testing.T) { + for _, in := range []string{"", "not-a-uuid", "xxx"} { + if _, ok := parseUUID(in); ok { + t.Errorf("parseUUID(%q) unexpectedly ok", in) + } + } +} + +func TestIndexLetter(t *testing.T) { + cases := map[string]string{ + "Radiohead": "R", + "the beatles": "T", + " massive": "M", + "4 Non Blondes": "#", + "": "?", + "*NSYNC": "?", + "Éowyn": "?", + } + for in, want := range cases { + if got := indexLetter(in); got != want { + t.Errorf("indexLetter(%q) = %q, want %q", in, got, want) + } + } +} + +func TestContentTypeForFormat(t *testing.T) { + cases := map[string]string{ + "mp3": "audio/mpeg", + "MP3": "audio/mpeg", + "flac": "audio/flac", + "ogg": "audio/ogg", + "oga": "audio/ogg", + "opus": "audio/opus", + "m4a": "audio/mp4", + "aac": "audio/mp4", + "wav": "audio/wav", + "unknown": "application/octet-stream", + } + for in, want := range cases { + if got := contentTypeForFormat(in); got != want { + t.Errorf("contentTypeForFormat(%q) = %q, want %q", in, got, want) + } + } +} + +func TestYearFromDate(t *testing.T) { + d := pgtype.Date{Valid: true, Time: time.Date(1997, 6, 16, 0, 0, 0, 0, time.UTC)} + if got := yearFromDate(d); got != 1997 { + t.Errorf("yearFromDate = %d", got) + } + if got := yearFromDate(pgtype.Date{}); got != 0 { + t.Errorf("yearFromDate(zero) = %d, want 0", got) + } +} + +func TestTimestamptzToRFC3339(t *testing.T) { + ts := pgtype.Timestamptz{ + Valid: true, + Time: time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC), + } + if got := timestamptzToRFC3339(ts); got != "2026-01-02T03:04:05Z" { + t.Errorf("rfc3339 = %q", got) + } + if got := timestamptzToRFC3339(pgtype.Timestamptz{}); got != "" { + t.Errorf("rfc3339(zero) = %q, want empty", got) + } +} + +func TestAtoiDefault(t *testing.T) { + cases := []struct { + in string + def int + want int + }{ + {"", 7, 7}, + {"42", 0, 42}, + {" 12 ", 0, 12}, + {"junk", 99, 99}, + } + for _, c := range cases { + if got := atoiDefault(c.in, c.def); got != c.want { + t.Errorf("atoiDefault(%q,%d) = %d, want %d", c.in, c.def, got, c.want) + } + } +} + +func TestClampInt(t *testing.T) { + if got := clampInt(5, 1, 10); got != 5 { + t.Errorf("in-range = %d", got) + } + if got := clampInt(-3, 1, 10); got != 1 { + t.Errorf("below = %d", got) + } + if got := clampInt(99, 1, 10); got != 10 { + t.Errorf("above = %d", got) + } +}