Merge pull request #1829 from perhapzz/test/add-fileutil-health-tests

test: add unit tests for pkg/fileutil and pkg/health
This commit is contained in:
Mauro
2026-03-26 10:42:13 +01:00
committed by GitHub
2 changed files with 523 additions and 0 deletions
+176
View File
@@ -0,0 +1,176 @@
package fileutil
import (
"os"
"path/filepath"
"sync"
"testing"
)
func TestWriteFileAtomic_Basic(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "test.txt")
data := []byte("hello picoclaw")
err := WriteFileAtomic(path, data, 0o644)
if err != nil {
t.Fatalf("WriteFileAtomic failed: %v", err)
}
got, err := os.ReadFile(path)
if err != nil {
t.Fatalf("ReadFile failed: %v", err)
}
if string(got) != string(data) {
t.Errorf("got %q, want %q", got, data)
}
}
func TestWriteFileAtomic_Permissions(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "secret.txt")
err := WriteFileAtomic(path, []byte("secret"), 0o600)
if err != nil {
t.Fatalf("WriteFileAtomic failed: %v", err)
}
info, err := os.Stat(path)
if err != nil {
t.Fatalf("Stat failed: %v", err)
}
// On Unix, check file mode (ignoring directory bits)
if got := info.Mode().Perm(); got != 0o600 {
t.Errorf("permissions = %o, want %o", got, 0o600)
}
}
func TestWriteFileAtomic_Overwrite(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "overwrite.txt")
// Write initial content
if err := WriteFileAtomic(path, []byte("old"), 0o644); err != nil {
t.Fatalf("first write failed: %v", err)
}
// Overwrite
if err := WriteFileAtomic(path, []byte("new"), 0o644); err != nil {
t.Fatalf("second write failed: %v", err)
}
got, _ := os.ReadFile(path)
if string(got) != "new" {
t.Errorf("got %q after overwrite, want %q", got, "new")
}
}
func TestWriteFileAtomic_EmptyData(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "empty.txt")
err := WriteFileAtomic(path, []byte{}, 0o644)
if err != nil {
t.Fatalf("WriteFileAtomic with empty data failed: %v", err)
}
got, _ := os.ReadFile(path)
if len(got) != 0 {
t.Errorf("expected empty file, got %d bytes", len(got))
}
}
func TestWriteFileAtomic_CreatesParentDirs(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "a", "b", "c", "deep.txt")
err := WriteFileAtomic(path, []byte("deep"), 0o644)
if err != nil {
t.Fatalf("WriteFileAtomic with nested dirs failed: %v", err)
}
got, _ := os.ReadFile(path)
if string(got) != "deep" {
t.Errorf("got %q, want %q", got, "deep")
}
}
func TestWriteFileAtomic_NoTempFileOnSuccess(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "clean.txt")
if err := WriteFileAtomic(path, []byte("data"), 0o644); err != nil {
t.Fatalf("WriteFileAtomic failed: %v", err)
}
// Verify no temp files remain
entries, _ := os.ReadDir(dir)
for _, e := range entries {
if e.Name() != "clean.txt" {
t.Errorf("unexpected file remaining: %s", e.Name())
}
}
}
func TestWriteFileAtomic_LargeFile(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "large.bin")
// 1MB of data
data := make([]byte, 1<<20)
for i := range data {
data[i] = byte(i % 256)
}
if err := WriteFileAtomic(path, data, 0o644); err != nil {
t.Fatalf("WriteFileAtomic with large file failed: %v", err)
}
got, _ := os.ReadFile(path)
if len(got) != len(data) {
t.Errorf("file size = %d, want %d", len(got), len(data))
}
}
func TestWriteFileAtomic_Concurrent(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "concurrent.txt")
var wg sync.WaitGroup
errs := make(chan error, 10)
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
data := []byte(string(rune('A' + n)))
if err := WriteFileAtomic(path, data, 0o644); err != nil {
errs <- err
}
}(i)
}
wg.Wait()
close(errs)
for err := range errs {
t.Errorf("concurrent write error: %v", err)
}
// File should exist and contain exactly 1 byte (last writer wins)
got, err := os.ReadFile(path)
if err != nil {
t.Fatalf("ReadFile after concurrent writes failed: %v", err)
}
if len(got) != 1 {
t.Errorf("expected 1 byte after concurrent writes, got %d", len(got))
}
}
func TestWriteFileAtomic_InvalidPath(t *testing.T) {
// /dev/null/impossible is not a valid path on any OS
err := WriteFileAtomic("/dev/null/impossible/file.txt", []byte("data"), 0o644)
if err == nil {
t.Error("expected error for invalid path, got nil")
}
}