// SPDX-License-Identifier: Apache-2.0 // Copyright (c) 2024-2026 Quad4.io package common import ( "errors" "fmt" "io" "net" "os" "syscall" "testing" "time" ) func TestFormattedErrorsWrapSentinels(t *testing.T) { hash := []byte{0x01, 0x02} if !errors.Is(ErrLinkNoPathf(hash), ErrLinkNoPath) { t.Fatal("ErrLinkNoPathf should wrap ErrLinkNoPath") } if !errors.Is(ErrTransportNoPathForLinkRelayf(hash), ErrTransportNoPathForLinkRelay) { t.Fatal("ErrTransportNoPathForLinkRelayf should wrap ErrTransportNoPathForLinkRelay") } if !errors.Is(ErrTransportIfaceNotReadyForPRf("radio"), ErrTransportIfaceNotReadyForPR) { t.Fatal("ErrTransportIfaceNotReadyForPRf should wrap ErrTransportIfaceNotReadyForPR") } if !errors.Is(ErrNoPathToDestinationf(hash), ErrNoPathToDestination) { t.Fatal("ErrNoPathToDestinationf should wrap ErrNoPathToDestination") } if !errors.Is(ErrPathRequestThrottledf(hash, time.Second), ErrPathRequestThrottled) { t.Fatal("ErrPathRequestThrottledf should wrap ErrPathRequestThrottled") } if !errors.Is(ErrIdentityNotFoundf(hash), ErrIdentityNotFound) { t.Fatal("ErrIdentityNotFoundf should wrap ErrIdentityNotFound") } if !errors.Is(ErrConfigf("bad port %d", 0), ErrConfig) { t.Fatal("ErrConfigf should wrap ErrConfig") } if !errors.Is(ErrCorruptionf("table"), ErrCorruption) { t.Fatal("ErrCorruptionf should wrap ErrCorruption") } if !errors.Is(ErrSandboxf("pledge"), ErrSandbox) { t.Fatal("ErrSandboxf should wrap ErrSandbox") } if !errors.Is(ErrPortConflictf("udp :4242"), ErrPortConflict) { t.Fatal("ErrPortConflictf should wrap ErrPortConflict") } if !errors.Is(ErrDiskf("persist"), ErrDisk) { t.Fatal("ErrDiskf should wrap ErrDisk") } if !errors.Is(ErrOOMf("budget"), ErrOOM) { t.Fatal("ErrOOMf should wrap ErrOOM") } if !errors.Is(ErrCPUf("throttle"), ErrCPU) { t.Fatal("ErrCPUf should wrap ErrCPU") } } func TestClassifyIOError(t *testing.T) { if !errors.Is(ClassifyIOError(syscall.EADDRINUSE), ErrPortConflict) { t.Fatal("EADDRINUSE should classify as ErrPortConflict") } if !errors.Is(ClassifyIOError(syscall.ENOSPC), ErrDisk) { t.Fatal("ENOSPC should classify as ErrDisk") } if !errors.Is(ClassifyIOError(syscall.ENOMEM), ErrOOM) { t.Fatal("ENOMEM should classify as ErrOOM") } plain := errors.New("something else") if ClassifyIOError(plain) != plain { t.Fatal("unknown errors should pass through") } if ClassifyIOError(nil) != nil { t.Fatal("nil should stay nil") } already := fmt.Errorf("%w: detail", ErrDisk) if ClassifyIOError(already) != already { t.Fatal("already-classified errors should pass through unchanged") } } func TestIsPortConflictUnwrapsNetAndSyscall(t *testing.T) { if IsPortConflict(nil) { t.Fatal("nil is not a port conflict") } if !IsPortConflict(ErrPortConflict) { t.Fatal("sentinel should match") } sys := &os.SyscallError{Syscall: "bind", Err: syscall.EADDRINUSE} op := &net.OpError{Op: "listen", Net: "tcp", Err: sys} if !IsPortConflict(op) { t.Fatal("net.OpError wrapping SyscallError EADDRINUSE should match") } if !IsPortConflict(errors.New("listen tcp :80: bind: address already in use")) { t.Fatal("message fallback should match") } if IsPortConflict(errors.New("connection refused")) { t.Fatal("unrelated error should not match") } } func TestIsDiskFullAndIsOOM(t *testing.T) { if IsDiskFull(nil) || IsOOM(nil) { t.Fatal("nil should be false") } if !IsDiskFull(ErrDisk) { t.Fatal("ErrDisk should match") } if !IsDiskFull(errors.New("write: no space left on device")) { t.Fatal("message fallback should match disk full") } if !IsOOM(ErrOOM) { t.Fatal("ErrOOM should match") } if !IsOOM(ErrMemoryBudgetExceeded) { t.Fatal("memory budget should count as OOM") } if !IsOOM(fmt.Errorf("alloc: %w", syscall.ENOMEM)) { t.Fatal("ENOMEM wrap should match") } if !IsOOM(errors.New("cannot allocate memory")) { t.Fatal("message fallback should match OOM") } } func TestWrapListenErrorRealPortConflict(t *testing.T) { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("listen: %v", err) } defer ln.Close() _, err = net.Listen("tcp", ln.Addr().String()) if err == nil { t.Fatal("expected second listen to fail") } wrapped := WrapListenError(err) if !errors.Is(wrapped, ErrPortConflict) { t.Fatalf("WrapListenError should classify as ErrPortConflict, got %v", wrapped) } if !errors.Is(wrapped, err) { t.Fatalf("original listen error should remain unwrapable, got %v", wrapped) } if WrapListenError(nil) != nil { t.Fatal("nil should stay nil") } } func TestWrapWriteError(t *testing.T) { if WrapWriteError(nil) != nil { t.Fatal("nil should stay nil") } if WrapWriteError(io.ErrShortWrite) != io.ErrShortWrite { t.Fatal("short write should pass through unchanged") } if !errors.Is(WrapWriteError(syscall.ENOSPC), ErrDisk) { t.Fatal("ENOSPC should classify as ErrDisk") } if !errors.Is(WrapWriteError(syscall.ENOMEM), ErrOOM) { t.Fatal("ENOMEM should classify as ErrOOM") } plain := errors.New("permission denied") if WrapWriteError(plain) != plain { t.Fatal("unknown write errors should pass through") } } func TestConfigValidateUsesErrConfig(t *testing.T) { cfg := DefaultConfig() cfg.SharedInstancePort = 0 err := cfg.Validate() if !errors.Is(err, ErrConfig) { t.Fatalf("Validate should wrap ErrConfig, got %v", err) } }