Files
nfs/internal/nfsclient/cb_test.go
T
petrbalvin a9b8039ef7
Test / test (push) Successful in 2m4s
Release / gates (push) Successful in 2m5s
Release / build (amd64, freebsd) (push) Successful in 1m27s
Release / build (amd64, linux) (push) Successful in 1m22s
Release / build (amd64, netbsd) (push) Successful in 1m19s
Release / build (amd64, openbsd) (push) Successful in 1m20s
Release / build (arm64, darwin) (push) Successful in 1m21s
Release / build (arm64, freebsd) (push) Successful in 1m26s
Release / build (arm64, linux) (push) Successful in 1m25s
Release / build (arm64, netbsd) (push) Successful in 1m31s
Release / build (arm64, openbsd) (push) Successful in 1m27s
Release / build (loong64, linux) (push) Successful in 1m37s
Release / build (riscv64, linux) (push) Successful in 1m21s
Release / release (push) Successful in 40s
feat: full NFSv4.2 server and client in pure Go
Assisted-by: GLM 5.3 Flash
2026-09-21 18:51:17 +02:00

86 lines
2.4 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: MIT
package nfsclient
import (
"net"
"testing"
"sourcedock.dev/petrbalvin/nfs/internal/nfs4"
"sourcedock.dev/petrbalvin/nfs/internal/rpc"
)
// The accessors answer copies of what the callback demux recorded.
func TestNotifiedAccessors(t *testing.T) {
c := &Client{}
if got := c.Notified(); len(got) != 0 {
t.Fatal("notified before any delivery")
}
if got := c.NotifiedLocks(); len(got) != 0 {
t.Fatal("locks notified before any delivery")
}
c.recMu.Lock()
c.notified = append(c.notified, nfs4.CBNotify{FH: []byte("d")})
c.notifiedLocks = append(c.notifiedLocks, nfs4.CBNotifyLock{Clientid: 5})
c.recMu.Unlock()
if n := c.Notified(); len(n) != 1 || string(n[0].FH) != "d" {
t.Fatalf("notified %+v", n)
}
if n := c.NotifiedLocks(); len(n) != 1 || n[0].Clientid != 5 {
t.Fatalf("locks %+v", n)
}
}
// The callback demux answers CB_NOTIFY and CB_NOTIFY_LOCK and records
// them for the caller.
func TestCBCompoundNotifies(t *testing.T) {
// The demux answers every callback on the wire; a pipe with a
// draining side stands in for the connection.
a, b := net.Pipe()
defer a.Close()
defer b.Close()
go func() {
buf := make([]byte, 4096)
for {
if _, err := b.Read(buf); err != nil {
return
}
}
}()
c := &Client{conn: a, cbProgram: 0x40000000}
st := nfs4.Stateid{1, 'D', 'D'}
notify := nfs4.AppendCBNotifyArgs(nil, st, []byte("dir"),
[]nfs4.Notify4{{Mask: nfs4.OfBits(nfs4.NotifyAddEntry),
Vals: nfs4.AppendNotifyAdd(nil, "new", 0, true)}})
lock := nfs4.AppendCBNotifyLockArgs(nil, []byte("f"), 9, []byte("owner"))
c.handleCBCall(1, buildCBCall(t, [][]byte{notify, lock}))
notes := c.Notified()
if len(notes) != 1 || !notes[0].Changes[0].Mask.Has(nfs4.NotifyAddEntry) {
t.Fatalf("notified %+v", notes)
}
if n := c.NotifiedLocks(); len(n) != 1 || n[0].Clientid != 9 {
t.Fatalf("locks %+v", n)
}
}
// buildCBCall frames one CB_COMPOUND call record the way the server
// sends it.
func buildCBCall(t *testing.T, ops [][]byte) []byte {
t.Helper()
args := nfs4.AppendCBCompoundArgs(nil, "cb", nfs4.MinorVersion, 0, ops)
record, err := rpc.AppendCall(nil, rpc.Call{
XID: 1, Program: nfs4.CBDefaultProgram, Version: nfs4.Version,
Procedure: 1,
Cred: rpc.Auth{Flavor: rpc.FlavorNone},
Verifier: rpc.Auth{Flavor: rpc.FlavorNone},
})
if err != nil {
t.Fatal(err)
}
return append(record, args...)
}