// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT package nfsfs import ( "errors" "os" "path/filepath" "syscall" "testing" ) // The create and write error wrappers map the errno families the // protocol knows: a missing parent is NoEnt, a permission problem is // Permission, a collision is Exist, and the rest stay raw. func TestWrapErrFamilies(t *testing.T) { // The wrapper level: each mapped errno. cases := []struct { err error want error }{ {nil, nil}, {&os.PathError{Op: "open", Err: syscall.EACCES}, ErrPermission}, {&os.PathError{Op: "open", Err: syscall.EPERM}, ErrPermission}, {&os.PathError{Op: "open", Err: syscall.EEXIST}, ErrExist}, {&os.PathError{Op: "open", Err: syscall.EISDIR}, ErrIsDir}, {&os.PathError{Op: "open", Err: syscall.ENOTDIR}, ErrNotDir}, {&os.PathError{Op: "write", Err: syscall.ENOSPC}, ErrNoSpace}, {&os.PathError{Op: "write", Err: syscall.EIO}, ErrIO}, } for _, c := range cases { if got := wrapCreateErr(c.err); !errors.Is(got, c.want) { t.Fatalf("create wrap of %v: %v, want %v", c.err, got, c.want) } } // The write wrapper answers a vanished path with stale, not noent. if got := wrapWriteErr(&os.PathError{Op: "open", Err: syscall.ENOENT}); !errors.Is(got, ErrStale) { t.Fatalf("write wrap of a vanished path: %v, want stale", got) } // An errno outside the families lands in the io error family with // its cause kept in the text. if got := wrapCreateErr(&os.PathError{Op: "open", Err: syscall.EDQUOT}); !errors.Is(got, ErrIO) { t.Fatalf("unmapped errno: %v, want the io error family", got) } } // A hard link to a missing target and one under a file both answer // their own sentinels. func TestLinkErrors(t *testing.T) { root := t.TempDir() l, err := NewLocal(root) if err != nil { t.Fatal(err) } rh, err := l.Root() if err != nil { t.Fatal(err) } if _, _, err := l.Link(nfsfsMissingHandle(), rh, "x"); err == nil { t.Fatal("a link to a missing target succeeded") } target := filepath.Join(root, "t.txt") if err := os.WriteFile(target, []byte("t"), 0o644); err != nil { t.Fatal(err) } th, _, err := l.Lookup(rh, "t.txt") if err != nil { t.Fatal(err) } if _, _, err := l.Link(th, nfsfsMissingHandle(), "x"); err == nil { t.Fatal("a link under a missing directory succeeded") } } // nfsfsMissingHandle builds a handle that resolves to nothing. func nfsfsMissingHandle() Handle { return Handle("nfs\x02\x00\x00\x00stale-handle-bytes") }