package archive
import (
"bytes"
"os"
"path/filepath"
"testing"
)
func TestRoundTripDir(t *testing.T) {
src := t.TempDir()
mustWrite(t, filepath.Join(src, "a.txt"), "alpha")
mustWrite(t, filepath.Join(src, "sub/b.txt"), "bravo")
if err := os.MkdirAll(filepath.Join(src, "empty"), 0o755); err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
if err := EncodeDir(&buf, src); err != nil {
t.Fatalf("EncodeDir: %v", err)
}
dest := t.TempDir()
if err := DecodeDir(&buf, dest); err != nil {
t.Fatalf("DecodeDir: %v", err)
}
for _, c := range []struct{ rel, want string }{
{"a.txt", "alpha"},
{"sub/b.txt", "bravo"},
} {
got, err := os.ReadFile(filepath.Join(dest, c.rel))
if err != nil {
t.Errorf("read %s: %v", c.rel, err)
continue
}
if string(got) != c.want {
t.Errorf("%s = %q, want %q", c.rel, got, c.want)
}
}
if _, err := os.Stat(filepath.Join(dest, "empty")); err != nil {
t.Errorf("empty dir missing: %v", err)
}
}
func TestDecodeRejectsPathEscape(t *testing.T) {
// Hand-craft a tarball with a "../evil" entry, then zstd it via EncodeDir
// indirection isn't feasible (EncodeDir won't emit ..). Instead, write
// a tar.zst by hand with one bad header.
t.Skip("path-escape rejection covered by safeJoin unit logic; would need a hand-crafted tar to assert at integration level")
}
func mustWrite(t *testing.T, path, body string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
t.Fatal(err)
}
}
func TestSafeJoinRejectsEscape(t *testing.T) {
base := "/tmp/abc"
if _, err := safeJoin(base, "../etc/passwd"); err == nil {
t.Error("expected escape rejection for ../etc/passwd")
}
if _, err := safeJoin(base, "sub/ok"); err != nil {
t.Errorf("safe entry rejected: %v", err)
}
}