package platform import ( "bytes" "os" "path/filepath" "testing" ) // streamAll runs the full Begin → Append* → Finalize cycle for one session into // dir and returns the final path. CDROP_NO_REVEAL keeps Finder/Explorer shut. func streamAll(t *testing.T, dir, sessionID, name string, chunks ...[]byte) string { t.Helper() t.Setenv("CDROP_NO_REVEAL", "1") if err := BeginStreamingDownload(dir, sessionID); err != nil { t.Fatalf("Begin: %v", err) } for i, c := range chunks { if err := AppendStreamingDownload(sessionID, c); err != nil { t.Fatalf("Append %d: %v", i, err) } } path, err := FinalizeStreamingDownload(sessionID, name) if err != nil { t.Fatalf("Finalize: %v", err) } return path } func TestStreamingDownload_WritesAllBytes(t *testing.T) { dir := t.TempDir() path := streamAll(t, dir, "sess-1", "greeting.txt", []byte("hello "), []byte("streamed "), []byte("world")) if got, want := path, filepath.Join(dir, "greeting.txt"); got != want { t.Errorf("path = %q, want %q", got, want) } got, err := os.ReadFile(path) if err != nil { t.Fatalf("ReadFile: %v", err) } if want := "hello streamed world"; string(got) != want { t.Errorf("content = %q, want %q", got, want) } // The .part temp must be gone after a successful finalize. if _, err := os.Stat(filepath.Join(dir, ".cdrop-sess-1.part")); !os.IsNotExist(err) { t.Errorf("temp file not cleaned up: %v", err) } } func TestStreamingDownload_NoCollisionOverwrite(t *testing.T) { dir := t.TempDir() // Pre-existing file with the same name must not be overwritten. if err := os.WriteFile(filepath.Join(dir, "f.bin"), []byte("original"), 0o644); err != nil { t.Fatal(err) } path := streamAll(t, dir, "sess-2", "f.bin", []byte("new")) if got, want := path, filepath.Join(dir, "f (1).bin"); got != want { t.Errorf("path = %q, want %q", got, want) } orig, _ := os.ReadFile(filepath.Join(dir, "f.bin")) if string(orig) != "original" { t.Errorf("original file was overwritten: %q", orig) } } func TestStreamingDownload_SanitizesTraversalName(t *testing.T) { dir := t.TempDir() // A crafted name with path components must collapse to a bare filename under // dir — it must never escape via "../". path := streamAll(t, dir, "sess-3", "../../etc/evil", []byte("x")) if filepath.Dir(path) != dir { t.Errorf("escaped dir: path = %q, dir = %q", path, dir) } if got := filepath.Base(path); got != "evil" { t.Errorf("base = %q, want %q", got, "evil") } } func TestStreamingDownload_AbortRemovesTemp(t *testing.T) { dir := t.TempDir() t.Setenv("CDROP_NO_REVEAL", "1") if err := BeginStreamingDownload(dir, "sess-4"); err != nil { t.Fatalf("Begin: %v", err) } if err := AppendStreamingDownload("sess-4", []byte("partial")); err != nil { t.Fatalf("Append: %v", err) } tmp := filepath.Join(dir, ".cdrop-sess-4.part") if _, err := os.Stat(tmp); err != nil { t.Fatalf("temp should exist mid-stream: %v", err) } AbortStreamingDownload("sess-4") if _, err := os.Stat(tmp); !os.IsNotExist(err) { t.Errorf("temp file not removed after abort: %v", err) } // A second append after abort must error, not panic or write a stray file. if err := AppendStreamingDownload("sess-4", []byte("late")); err == nil { t.Error("Append after abort: want error, got nil") } } func TestStreamingDownload_ReBeginReplacesStale(t *testing.T) { dir := t.TempDir() t.Setenv("CDROP_NO_REVEAL", "1") // First Begin + partial write, then a second Begin for the same session id // (a retried transfer) must truncate, not append to, the stale temp. if err := BeginStreamingDownload(dir, "sess-5"); err != nil { t.Fatalf("Begin 1: %v", err) } if err := AppendStreamingDownload("sess-5", []byte("STALE")); err != nil { t.Fatalf("Append 1: %v", err) } if err := BeginStreamingDownload(dir, "sess-5"); err != nil { t.Fatalf("Begin 2: %v", err) } if err := AppendStreamingDownload("sess-5", []byte("fresh")); err != nil { t.Fatalf("Append 2: %v", err) } path, err := FinalizeStreamingDownload("sess-5", "out.bin") if err != nil { t.Fatalf("Finalize: %v", err) } got, _ := os.ReadFile(path) if !bytes.Equal(got, []byte("fresh")) { t.Errorf("content = %q, want %q (stale bytes leaked)", got, "fresh") } } func TestStreamingDownload_FinalizeUnknownSession(t *testing.T) { if _, err := FinalizeStreamingDownload("never-began", "x"); err == nil { t.Error("Finalize unknown session: want error, got nil") } }