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) { logSyncError("copy", "test.txt", syscall.ENOSPC) } func TestLogError_PermissionDenied(t *testing.T) { logSyncError("write", "test.txt", syscall.EACCES) } func TestLogError_NotExist(t *testing.T) { logSyncError("stat", "test.txt", fs.ErrNotExist) } func TestLogError_Nil(t *testing.T) { logSyncError("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") } }