package streaming import ( "net/http" "net/http/httptest" "os" "path/filepath" "testing" ) func setupFolder(t *testing.T) (root string) { t.Helper() root = t.TempDir() if err := os.MkdirAll(filepath.Join(root, "Artist", "Album"), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(root, "Artist", "Album", "song.mp3"), []byte("0123456789"), 0o644); err != nil { t.Fatal(err) } return root } func TestResolvePathAllowsFileWithinFolder(t *testing.T) { root := setupFolder(t) got, err := ResolvePath(root, "Artist/Album/song.mp3") if err != nil { t.Fatalf("ResolvePath: %v", err) } want, _ := filepath.EvalSymlinks(filepath.Join(root, "Artist", "Album", "song.mp3")) if got != want { t.Errorf("ResolvePath = %q, want %q", got, want) } } func TestResolvePathRejectsTraversal(t *testing.T) { root := setupFolder(t) // A secret file just outside the music folder root. if err := os.WriteFile(filepath.Join(filepath.Dir(root), "secret.txt"), []byte("nope"), 0o644); err != nil { t.Fatal(err) } _, err := ResolvePath(root, "../secret.txt") if err == nil { t.Fatal("expected ResolvePath to reject a path escaping the folder root") } } func TestResolvePathRejectsDeepTraversal(t *testing.T) { root := setupFolder(t) _, err := ResolvePath(root, "Artist/../../../../../../etc/passwd") if err == nil { t.Fatal("expected ResolvePath to reject a deep traversal attempt") } } func TestServeFileSupportsRangeRequests(t *testing.T) { root := setupFolder(t) path := filepath.Join(root, "Artist", "Album", "song.mp3") req := httptest.NewRequest(http.MethodGet, "/stream/x", nil) req.Header.Set("Range", "bytes=2-5") rec := httptest.NewRecorder() if err := ServeFile(rec, req, path, "song.mp3"); err != nil { t.Fatalf("ServeFile: %v", err) } if rec.Code != http.StatusPartialContent { t.Fatalf("status = %d, want 206", rec.Code) } if got := rec.Body.String(); got != "2345" { t.Errorf("body = %q, want %q", got, "2345") } if cr := rec.Header().Get("Content-Range"); cr == "" { t.Error("expected Content-Range header on a range response") } } func TestServeFileFullBody(t *testing.T) { root := setupFolder(t) path := filepath.Join(root, "Artist", "Album", "song.mp3") req := httptest.NewRequest(http.MethodGet, "/stream/x", nil) rec := httptest.NewRecorder() if err := ServeFile(rec, req, path, "song.mp3"); err != nil { t.Fatalf("ServeFile: %v", err) } if rec.Code != http.StatusOK { t.Fatalf("status = %d, want 200", rec.Code) } if rec.Body.String() != "0123456789" { t.Errorf("body = %q", rec.Body.String()) } if rec.Header().Get("Accept-Ranges") != "bytes" { t.Error("expected Accept-Ranges: bytes header") } } func TestServeFileRejectsDirectory(t *testing.T) { root := setupFolder(t) req := httptest.NewRequest(http.MethodGet, "/stream/x", nil) rec := httptest.NewRecorder() if err := ServeFile(rec, req, filepath.Join(root, "Artist", "Album"), "Album"); err == nil { t.Fatal("expected error serving a directory") } } func TestMimeType(t *testing.T) { cases := map[string]string{ "song.mp3": "audio/mpeg", "song.flac": "audio/flac", "song.opus": "audio/opus", "song.m4a": "audio/mp4", "song.xyz": "application/octet-stream", } for path, want := range cases { if got := MimeType(path); got != want { t.Errorf("MimeType(%q) = %q, want %q", path, got, want) } } }