Files
narratio/internal/fileops/fileops_test.go

126 lines
3.7 KiB
Go

package fileops
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestWriteFileAtomicOverwritesAndLeavesNoTempFile(t *testing.T) {
root := t.TempDir()
dst := filepath.Join(root, "out", "value.txt")
if err := WriteFileAtomic(dst, []byte("one"), 0o644); err != nil {
t.Fatalf("WriteFileAtomic(first) error = %v", err)
}
if err := WriteFileAtomic(dst, []byte("two"), 0o644); err != nil {
t.Fatalf("WriteFileAtomic(second) error = %v", err)
}
data, err := os.ReadFile(dst)
if err != nil {
t.Fatalf("ReadFile() error = %v", err)
}
if string(data) != "two" {
t.Fatalf("file content = %q, want %q", string(data), "two")
}
assertNoMatchingTempFiles(t, filepath.Dir(dst), "."+filepath.Base(dst)+".tmp-")
}
func TestWriteFileAtomicCleansTempFileOnInstallFailure(t *testing.T) {
root := t.TempDir()
blockedPath := filepath.Join(root, "blocked")
if err := os.MkdirAll(blockedPath, 0o755); err != nil {
t.Fatalf("MkdirAll(blockedPath) error = %v", err)
}
err := WriteFileAtomic(blockedPath, []byte("data"), 0o644)
if err == nil {
t.Fatal("WriteFileAtomic() error = nil, want install failure")
}
assertNoMatchingTempFiles(t, root, ".blocked.tmp-")
}
func TestCopyFileAtomicWithChecksumMatchesDestination(t *testing.T) {
root := t.TempDir()
src := filepath.Join(root, "source.txt")
dst := filepath.Join(root, "out", "copied.txt")
if err := os.WriteFile(src, []byte("copied-data"), 0o644); err != nil {
t.Fatalf("WriteFile(source) error = %v", err)
}
checksum, err := CopyFileAtomicWithChecksum(src, dst, 0o644)
if err != nil {
t.Fatalf("CopyFileAtomicWithChecksum() error = %v", err)
}
wantChecksum := "6e5c3f239e28cc315d57b2fcfc24169369c44a25802c0616a6d7081707fd24df"
if checksum != wantChecksum {
t.Fatalf("checksum = %q, want %q", checksum, wantChecksum)
}
data, err := os.ReadFile(dst)
if err != nil {
t.Fatalf("ReadFile(destination) error = %v", err)
}
if string(data) != "copied-data" {
t.Fatalf("destination content = %q, want %q", string(data), "copied-data")
}
assertNoMatchingTempFiles(t, filepath.Dir(dst), ".copied.txt.tmp-")
}
func TestCopyFileAtomicCleansTempFileOnInstallFailure(t *testing.T) {
root := t.TempDir()
src := filepath.Join(root, "source.txt")
if err := os.WriteFile(src, []byte("copied-data"), 0o644); err != nil {
t.Fatalf("WriteFile(source) error = %v", err)
}
blockedPath := filepath.Join(root, "blocked")
if err := os.MkdirAll(blockedPath, 0o755); err != nil {
t.Fatalf("MkdirAll(blockedPath) error = %v", err)
}
err := CopyFileAtomic(src, blockedPath, 0o644)
if err == nil {
t.Fatal("CopyFileAtomic() error = nil, want install failure")
}
assertNoMatchingTempFiles(t, root, ".blocked.tmp-")
}
func TestInstallDownloadedTempFileSetsPermissions(t *testing.T) {
root := t.TempDir()
tmpPath := filepath.Join(root, ".payload.tmp")
dst := filepath.Join(root, "out", "payload.json")
if err := os.WriteFile(tmpPath, []byte("{\"ok\":true}\n"), 0o600); err != nil {
t.Fatalf("WriteFile(temp) error = %v", err)
}
if err := InstallDownloadedTempFile(tmpPath, dst, 0o644); err != nil {
t.Fatalf("InstallDownloadedTempFile() error = %v", err)
}
if _, err := os.Stat(tmpPath); !os.IsNotExist(err) {
t.Fatalf("temp file still exists: stat err = %v", err)
}
info, err := os.Stat(dst)
if err != nil {
t.Fatalf("Stat(destination) error = %v", err)
}
if info.Mode().Perm() != 0o644 {
t.Fatalf("destination mode = %o, want 644", info.Mode().Perm())
}
}
func assertNoMatchingTempFiles(t *testing.T, dir, prefix string) {
t.Helper()
entries, err := os.ReadDir(dir)
if err != nil {
t.Fatalf("ReadDir(%q) error = %v", dir, err)
}
for _, e := range entries {
if strings.HasPrefix(e.Name(), prefix) {
t.Fatalf("unexpected temp file residue: %s", filepath.Join(dir, e.Name()))
}
}
}