aboutsummaryrefslogtreecommitdiffstats
path: root/errors_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'errors_test.go')
-rw-r--r--errors_test.go150
1 files changed, 150 insertions, 0 deletions
diff --git a/errors_test.go b/errors_test.go
new file mode 100644
index 0000000..553939d
--- /dev/null
+++ b/errors_test.go
@@ -0,0 +1,150 @@
+package main
+
+import (
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strings"
+ "syscall"
+ "testing"
+)
+
+func TestCopyFile_Normal(t *testing.T) {
+ src := filepath.Join(t.TempDir(), "src.txt")
+ dst := filepath.Join(t.TempDir(), "dst.txt")
+ os.WriteFile(src, []byte("hello"), 0644)
+
+ if err := copyFile(src, dst, 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ data, _ := os.ReadFile(dst)
+ if string(data) != "hello" {
+ t.Errorf("content = %q, want %q", string(data), "hello")
+ }
+}
+
+func TestCopyFile_SrcNotExist(t *testing.T) {
+ dst := filepath.Join(t.TempDir(), "dst.txt")
+ err := copyFile("/nonexistent/file", dst, 0644)
+ if err == nil {
+ t.Fatal("expected error for nonexistent source")
+ }
+ if !os.IsNotExist(err) {
+ t.Errorf("expected ErrNotExist, got: %v", err)
+ }
+}
+
+func TestCopyFile_DstPermDenied(t *testing.T) {
+ if os.Getuid() == 0 {
+ t.Skip("running as root, permission test not applicable")
+ }
+
+ src := filepath.Join(t.TempDir(), "src.txt")
+ os.WriteFile(src, []byte("hello"), 0644)
+
+ dst := "/proc/nonexistent/file"
+ err := copyFile(src, dst, 0644)
+ if err == nil {
+ t.Fatal("expected error for invalid dst")
+ }
+}
+
+func TestRetry_TransientError(t *testing.T) {
+ calls := 0
+ err := retry("test", func() error {
+ calls++
+ if calls < 3 {
+ return &fs.PathError{Op: "open", Path: "/tmp/test", Err: fs.ErrNotExist}
+ }
+ return nil
+ })
+ if err != nil {
+ t.Errorf("expected success after retries, got: %v", err)
+ }
+ if calls != 3 {
+ t.Errorf("calls = %d, want 3", calls)
+ }
+}
+
+func TestRetry_PermanentError(t *testing.T) {
+ calls := 0
+ err := retry("test", func() error {
+ calls++
+ return &fs.PathError{Op: "write", Path: "/tmp/test", Err: syscall.ENOSPC}
+ })
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ if calls != 1 {
+ t.Errorf("calls = %d, want 1 (no retries for ENOSPC)", calls)
+ }
+}
+
+func TestIsRetryable(t *testing.T) {
+ tests := []struct {
+ err error
+ expected bool
+ }{
+ {fs.ErrNotExist, true},
+ {&fs.PathError{Op: "open", Path: "/tmp", Err: fs.ErrNotExist}, true},
+ {&fs.PathError{Op: "write", Path: "/tmp", Err: syscall.ENOSPC}, false},
+ {syscall.ENOSPC, false},
+ {syscall.EACCES, false},
+ }
+
+ for _, tt := range tests {
+ if got := isRetryable(tt.err); got != tt.expected {
+ t.Errorf("isRetryable(%v) = %v, want %v", tt.err, got, tt.expected)
+ }
+ }
+}
+
+func TestLogError_DiskFull(t *testing.T) {
+ logError("copy", "test.txt", syscall.ENOSPC)
+}
+
+func TestLogError_PermissionDenied(t *testing.T) {
+ logError("write", "test.txt", syscall.EACCES)
+}
+
+func TestLogError_NotExist(t *testing.T) {
+ logError("stat", "test.txt", fs.ErrNotExist)
+}
+
+func TestLogError_Nil(t *testing.T) {
+ logError("test", "file.txt", nil)
+}
+
+func TestSyncError(t *testing.T) {
+ err := &SyncError{
+ Path: "test.txt",
+ Op: "copy",
+ Err: syscall.ENOSPC,
+ }
+
+ if !strings.Contains(err.Error(), "copy") {
+ t.Errorf("error should contain op, got: %s", err.Error())
+ }
+ if !strings.Contains(err.Error(), "test.txt") {
+ t.Errorf("error should contain path, got: %s", err.Error())
+ }
+ if err.Unwrap() != syscall.ENOSPC {
+ t.Errorf("unwrap should return underlying error")
+ }
+}
+
+func TestCopyFileWithRetry_Success(t *testing.T) {
+ src := filepath.Join(t.TempDir(), "src.txt")
+ dst := filepath.Join(t.TempDir(), "dst.txt")
+ os.WriteFile(src, []byte("content"), 0644)
+
+ if err := copyFileWithRetry(src, dst, 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ data, _ := os.ReadFile(dst)
+ if string(data) != "content" {
+ t.Errorf("content = %q, want %q", string(data), "content")
+ }
+}