mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-20 13:30:46 +02:00
Go server was parsing fid at bytes 0-15 (Go format), but Rust uds_client sends opcode(1)+pad(3)+request_id(4)+fid(16) per sra-common::uds_proto::LocateRequest repr(C). Now Go parses: opcode at byte 0, request_id at bytes 4-7, fid at bytes 8-23. Matches the Rust wire format exactly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
232 lines
5.7 KiB
Go
232 lines
5.7 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) {
|
|
// Wire format: opcode(1) + pad(3) + request_id(4) + fid(16) = 24
|
|
req := LocateRequest{
|
|
Opcode: 1, // VolumeOpcode::Locate
|
|
RequestId: 42,
|
|
}
|
|
copy(req.Fid[:], "3,01637037")
|
|
|
|
buf := make([]byte, UdsRequestSize)
|
|
buf[0] = req.Opcode
|
|
binary.LittleEndian.PutUint32(buf[4:8], req.RequestId)
|
|
copy(buf[8:24], req.Fid[:])
|
|
|
|
// Verify fid is correctly placed at offset 8
|
|
fid := string(buf[8:18])
|
|
if fid != "3,01637037" {
|
|
t.Errorf("Expected fid='3,01637037', got '%s'", fid)
|
|
}
|
|
|
|
// Verify request_id
|
|
requestId := binary.LittleEndian.Uint32(buf[4:8])
|
|
if requestId != 42 {
|
|
t.Errorf("Expected request_id=42, got %d", requestId)
|
|
}
|
|
}
|
|
|
|
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)
|
|
// Wire format: opcode(1) + pad(3) + request_id(4) + fid(16)
|
|
reqBuf := make([]byte, UdsRequestSize)
|
|
reqBuf[0] = 1 // VolumeOpcode::Locate
|
|
binary.LittleEndian.PutUint32(reqBuf[4:8], 1) // request_id
|
|
copy(reqBuf[8:24], "3,01637037") // fid at offset 8
|
|
|
|
_, 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)
|
|
}
|
|
|
|
// DatPathLen should be 0 for error responses
|
|
datPathLen := binary.LittleEndian.Uint16(respBuf[24:26])
|
|
if datPathLen != 0 {
|
|
t.Errorf("Expected DatPathLen=0 for error, got %d", datPathLen)
|
|
}
|
|
}
|
|
|
|
// 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])
|
|
|
|
if tt.fid == "" && fid != "" {
|
|
t.Errorf("Expected empty fid, got '%s'", fid)
|
|
}
|
|
})
|
|
}
|
|
}
|