Files
seaweedfs/weed/server/volume_server_uds_test.go
T
pingqiuandClaude Opus 4.6 f20c05ea0a fix(uds): guard against nil VolumeServer in handleLocate
Prevent nil pointer dereference when VolumeServer is nil by adding
an early return with UdsStatusError before accessing vs.store.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 10:58:36 -08:00

225 lines
5.4 KiB
Go

package weed_server
import (
"encoding/binary"
"io"
"net"
"os"
"path/filepath"
"testing"
"time"
)
func TestLocateRequestResponseSize(t *testing.T) {
// Verify protocol sizes match spec
if UdsRequestSize != 24 {
t.Errorf("UdsRequestSize expected 24, got %d", UdsRequestSize)
}
if UdsResponseSize != 32 {
t.Errorf("UdsResponseSize expected 32, got %d", UdsResponseSize)
}
}
func TestLocateRequestSerialization(t *testing.T) {
req := LocateRequest{
Version: 1,
Flags: 0,
}
copy(req.Fid[:], "3,01637037")
buf := make([]byte, UdsRequestSize)
copy(buf[0:16], req.Fid[:])
binary.LittleEndian.PutUint32(buf[16:20], req.Version)
binary.LittleEndian.PutUint32(buf[20:24], req.Flags)
// Verify fid is correctly placed
fid := string(buf[0:10])
if fid != "3,01637037" {
t.Errorf("Expected fid='3,01637037', got '%s'", fid)
}
// Verify version
version := binary.LittleEndian.Uint32(buf[16:20])
if version != 1 {
t.Errorf("Expected version=1, got %d", version)
}
}
func TestLocateResponseSerialization(t *testing.T) {
resp := &LocateResponse{
Status: UdsStatusOk,
VolumeId: 3,
Offset: 1024,
Length: 4096,
}
buf := make([]byte, UdsResponseSize)
buf[0] = resp.Status
binary.LittleEndian.PutUint32(buf[4:8], resp.VolumeId)
binary.LittleEndian.PutUint64(buf[8:16], resp.Offset)
binary.LittleEndian.PutUint64(buf[16:24], resp.Length)
// Deserialize and verify
status := buf[0]
if status != UdsStatusOk {
t.Errorf("Expected status=0, got %d", status)
}
volumeId := binary.LittleEndian.Uint32(buf[4:8])
if volumeId != 3 {
t.Errorf("Expected volumeId=3, got %d", volumeId)
}
offset := binary.LittleEndian.Uint64(buf[8:16])
if offset != 1024 {
t.Errorf("Expected offset=1024, got %d", offset)
}
length := binary.LittleEndian.Uint64(buf[16:24])
if length != 4096 {
t.Errorf("Expected length=4096, got %d", length)
}
}
func TestUdsStatusCodes(t *testing.T) {
if UdsStatusOk != 0 {
t.Errorf("UdsStatusOk expected 0, got %d", UdsStatusOk)
}
if UdsStatusNotFound != 1 {
t.Errorf("UdsStatusNotFound expected 1, got %d", UdsStatusNotFound)
}
if UdsStatusError != 2 {
t.Errorf("UdsStatusError expected 2, got %d", UdsStatusError)
}
}
// TestUdsServerBasicConnection tests that we can create and connect to a UDS server
func TestUdsServerBasicConnection(t *testing.T) {
// Create temp socket path
tmpDir := t.TempDir()
socketPath := filepath.Join(tmpDir, "test.sock")
// Create a mock volume server (nil is fine since we won't make real lookups)
uds, err := NewUdsServer(nil, socketPath)
if err != nil {
t.Fatalf("Failed to create UDS server: %v", err)
}
defer uds.Stop()
uds.Start()
// Give server time to start
time.Sleep(10 * time.Millisecond)
// Try to connect
conn, err := net.Dial("unix", socketPath)
if err != nil {
t.Fatalf("Failed to connect to UDS server: %v", err)
}
defer conn.Close()
// Connection successful
}
// TestUdsServerProtocol tests the basic protocol exchange
func TestUdsServerProtocol(t *testing.T) {
// Create temp socket path
tmpDir := t.TempDir()
socketPath := filepath.Join(tmpDir, "test.sock")
// Create a mock volume server (vs=nil means handleLocate will return NotFound)
uds, err := NewUdsServer(nil, socketPath)
if err != nil {
t.Fatalf("Failed to create UDS server: %v", err)
}
defer uds.Stop()
uds.Start()
time.Sleep(10 * time.Millisecond)
// Connect
conn, err := net.Dial("unix", socketPath)
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()
// Send a request (will fail because vs is nil, but we're testing protocol)
reqBuf := make([]byte, UdsRequestSize)
copy(reqBuf[0:16], "3,01637037")
binary.LittleEndian.PutUint32(reqBuf[16:20], 1) // version
binary.LittleEndian.PutUint32(reqBuf[20:24], 0) // flags
_, err = conn.Write(reqBuf)
if err != nil {
t.Fatalf("Failed to write request: %v", err)
}
// Read response
respBuf := make([]byte, UdsResponseSize)
_, err = io.ReadFull(conn, respBuf)
if err != nil {
t.Fatalf("Failed to read response: %v", err)
}
// Since vs is nil, we expect UdsStatusError
status := respBuf[0]
if status != UdsStatusError {
t.Errorf("Expected UdsStatusError (%d) for nil VolumeServer, got %d", UdsStatusError, status)
}
}
// TestUdsSocketCleanup tests that existing socket is removed
func TestUdsSocketCleanup(t *testing.T) {
tmpDir := t.TempDir()
socketPath := filepath.Join(tmpDir, "test.sock")
// Create a dummy file at the socket path
if err := os.WriteFile(socketPath, []byte("dummy"), 0644); err != nil {
t.Fatalf("Failed to create dummy file: %v", err)
}
// NewUdsServer should remove the existing file
uds, err := NewUdsServer(nil, socketPath)
if err != nil {
t.Fatalf("Failed to create UDS server (should have cleaned up old socket): %v", err)
}
uds.Stop()
}
func TestFidParsing(t *testing.T) {
tests := []struct {
fid string
valid bool
}{
{"3,01637037", true},
{"1,abc123", true},
{"", false},
{"invalid", false},
}
for _, tt := range tests {
t.Run(tt.fid, func(t *testing.T) {
req := &LocateRequest{}
copy(req.Fid[:], tt.fid)
// Extract fid string using same logic as handleLocate
fidBytes := req.Fid[:]
fidLen := 0
for i, b := range fidBytes {
if b == 0 {
fidLen = i
break
}
fidLen = i + 1
}
fid := string(fidBytes[:fidLen])
// For empty fid, we expect empty string
if tt.fid == "" && fid != "" {
t.Errorf("Expected empty fid, got '%s'", fid)
}
})
}
}