mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-16 01:22:00 +00:00
829 lines
20 KiB
Go
829 lines
20 KiB
Go
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestGenerateResourceID(t *testing.T) {
|
|
tests := []struct {
|
|
prefix string
|
|
}{
|
|
{"i"},
|
|
{"r"},
|
|
{"vol"},
|
|
{"snap"},
|
|
{"key"},
|
|
{"eigw"},
|
|
{"ami"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.prefix, func(t *testing.T) {
|
|
id := GenerateResourceID(tt.prefix)
|
|
assert.True(t, strings.HasPrefix(id, tt.prefix+"-"))
|
|
// prefix + "-" + 17 hex chars
|
|
assert.Len(t, id, len(tt.prefix)+1+17)
|
|
|
|
// Verify uniqueness
|
|
id2 := GenerateResourceID(tt.prefix)
|
|
assert.NotEqual(t, id, id2)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGeneratePidFile(t *testing.T) {
|
|
|
|
// Simulate a sample process running (e.g cat)
|
|
cmd := exec.Command("cat")
|
|
cmd.Start()
|
|
|
|
err := WritePidFile("utilsunittest", cmd.Process.Pid)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
// Read the PID file and verify contents
|
|
pid, err := ReadPidFile("utilsunittest")
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, cmd.Process.Pid, pid)
|
|
|
|
// Test attempt to read a PID file that doesn't exist
|
|
_, err = ReadPidFile("nonexistentpidfile")
|
|
assert.Error(t, err)
|
|
|
|
// Cleanup
|
|
err = RemovePidFile("utilsunittest")
|
|
assert.NoError(t, err)
|
|
|
|
// Give some time before killing the process
|
|
//time.Sleep(2 * time.Second)
|
|
|
|
// Simulate process ending
|
|
|
|
}
|
|
|
|
func TestGenerateSocketFile(t *testing.T) {
|
|
|
|
socketPath := fmt.Sprintf("%s/%s", os.TempDir(), "utilsunittest")
|
|
|
|
name, err := GenerateSocketFile(socketPath)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.True(t, strings.HasSuffix(name, "utilsunittest.sock"))
|
|
|
|
// Test empty socket path
|
|
_, err = GenerateSocketFile("")
|
|
|
|
assert.Error(t, err)
|
|
|
|
}
|
|
|
|
func TestExecProcessAndKill(t *testing.T) {
|
|
|
|
// Simulate a sample process running (e.g sleep, 30 secs)
|
|
cmd := exec.Command("sleep", "30")
|
|
|
|
// Detach: new process group, no controlling terminal.
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
Setpgid: true, // put child in new process group
|
|
}
|
|
|
|
// Make it fully background-friendly:
|
|
// - close stdio so parent doesn't block on pipes
|
|
cmd.Stdin = nil
|
|
cmd.Stdout = nil
|
|
cmd.Stderr = nil
|
|
|
|
// Start (non-blocking). If command is missing, error.
|
|
if err := cmd.Start(); err != nil {
|
|
assert.Fail(t, "Failed to start command", err)
|
|
}
|
|
|
|
// IMPORTANT: reap the child to avoid zombies.
|
|
// Since we're "backgrounding" it, do Wait() in a goroutine.
|
|
go func(c *exec.Cmd) {
|
|
t.Log("Waiting for command to finish...")
|
|
_ = c.Wait() // ignore error; ensures kernel reaps the process
|
|
t.Log("Command finished.")
|
|
}(cmd)
|
|
|
|
err := WritePidFile("utilsunittest", cmd.Process.Pid)
|
|
|
|
t.Log("Started process with PID:", cmd.Process.Pid)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
// Test PID file removed
|
|
err = WaitForPidFileRemoval("utilsunittest", 100*time.Millisecond)
|
|
assert.Error(t, err) // Should timeout since file should still exist
|
|
|
|
time.Sleep(500 * time.Millisecond)
|
|
|
|
// Kill the process
|
|
err = StopProcess("utilsunittest")
|
|
assert.NoError(t, err)
|
|
|
|
// Test PID file removed
|
|
err = WaitForPidFileRemoval("utilsunittest", 1*time.Second)
|
|
assert.NoError(t, err) // Should timeout since file should still exist
|
|
|
|
// Verify process is killed
|
|
err = cmd.Process.Signal(syscall.Signal(0))
|
|
assert.Error(t, err) // Should return an error since process is killed
|
|
|
|
}
|
|
|
|
func TestUnmarshalJsonPayload(t *testing.T) {
|
|
type TestStruct struct {
|
|
Name string `json:"name"`
|
|
Value int `json:"value"`
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
jsonData string
|
|
expectError bool
|
|
validate func(t *testing.T, result *TestStruct)
|
|
}{
|
|
{
|
|
name: "Valid JSON",
|
|
jsonData: `{"name":"test","value":123}`,
|
|
expectError: false,
|
|
validate: func(t *testing.T, result *TestStruct) {
|
|
assert.Equal(t, "test", result.Name)
|
|
assert.Equal(t, 123, result.Value)
|
|
},
|
|
},
|
|
{
|
|
name: "Invalid JSON - malformed",
|
|
jsonData: `{"name":"test","value":}`,
|
|
expectError: true,
|
|
validate: nil,
|
|
},
|
|
{
|
|
name: "Invalid JSON - unknown field",
|
|
jsonData: `{"name":"test","value":123,"unknown":"field"}`,
|
|
expectError: true, // DisallowUnknownFields should cause error
|
|
validate: nil,
|
|
},
|
|
{
|
|
name: "Empty JSON",
|
|
jsonData: `{}`,
|
|
expectError: false,
|
|
validate: func(t *testing.T, result *TestStruct) {
|
|
assert.Equal(t, "", result.Name)
|
|
assert.Equal(t, 0, result.Value)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var result TestStruct
|
|
errResp := UnmarshalJsonPayload(&result, []byte(tt.jsonData))
|
|
|
|
if tt.expectError {
|
|
assert.NotNil(t, errResp, "Expected error response")
|
|
} else {
|
|
assert.Nil(t, errResp, "Expected no error response")
|
|
if tt.validate != nil {
|
|
tt.validate(t, &result)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarshalJsonPayload(t *testing.T) {
|
|
type TestStruct struct {
|
|
Name string `json:"name"`
|
|
Value int `json:"value"`
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
jsonData string
|
|
expectError bool
|
|
validate func(t *testing.T, result *TestStruct)
|
|
}{
|
|
{
|
|
name: "Valid JSON",
|
|
jsonData: `{"name":"test","value":456}`,
|
|
expectError: false,
|
|
validate: func(t *testing.T, result *TestStruct) {
|
|
assert.Equal(t, "test", result.Name)
|
|
assert.Equal(t, 456, result.Value)
|
|
},
|
|
},
|
|
{
|
|
name: "Invalid JSON",
|
|
jsonData: `{"name":"test","value":}`,
|
|
expectError: true,
|
|
validate: nil,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var result TestStruct
|
|
errResp := MarshalJsonPayload(&result, []byte(tt.jsonData))
|
|
|
|
if tt.expectError {
|
|
assert.NotNil(t, errResp, "Expected error response")
|
|
} else {
|
|
assert.Nil(t, errResp, "Expected no error response")
|
|
if tt.validate != nil {
|
|
tt.validate(t, &result)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateErrorPayload(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
code string
|
|
validate func(t *testing.T, payload []byte)
|
|
}{
|
|
{
|
|
name: "ValidationError",
|
|
code: "ValidationError",
|
|
validate: func(t *testing.T, payload []byte) {
|
|
assert.Contains(t, string(payload), "ValidationError")
|
|
assert.Contains(t, string(payload), "Code")
|
|
},
|
|
},
|
|
{
|
|
name: "InvalidInstanceType",
|
|
code: "InvalidInstanceType",
|
|
validate: func(t *testing.T, payload []byte) {
|
|
assert.Contains(t, string(payload), "InvalidInstanceType")
|
|
},
|
|
},
|
|
{
|
|
name: "CustomError",
|
|
code: "CustomError",
|
|
validate: func(t *testing.T, payload []byte) {
|
|
assert.Contains(t, string(payload), "CustomError")
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
payload := GenerateErrorPayload(tt.code)
|
|
assert.NotNil(t, payload)
|
|
assert.Greater(t, len(payload), 0)
|
|
if tt.validate != nil {
|
|
tt.validate(t, payload)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateErrorPayload(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
payload string
|
|
expectError bool
|
|
expectedCode string
|
|
}{
|
|
{
|
|
name: "Valid error payload",
|
|
payload: `{"Code":"ValidationError","Message":null}`,
|
|
expectError: true,
|
|
expectedCode: "ValidationError",
|
|
},
|
|
{
|
|
name: "Valid success payload (no Code field)",
|
|
payload: `{"ReservationId":"r-123","Instances":[]}`,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Empty payload",
|
|
payload: `{}`,
|
|
expectError: true, // Empty payload treated as error by ValidateErrorPayload
|
|
},
|
|
{
|
|
name: "Invalid JSON",
|
|
payload: `{invalid}`,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
responseError, err := ValidateErrorPayload([]byte(tt.payload))
|
|
|
|
if tt.expectError {
|
|
if tt.expectedCode != "" {
|
|
// Check for specific error code
|
|
assert.Error(t, err)
|
|
if responseError.Code != nil {
|
|
assert.Equal(t, tt.expectedCode, *responseError.Code)
|
|
}
|
|
}
|
|
} else {
|
|
// No error expected
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseNBDURI(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
uri string
|
|
wantType string
|
|
wantPath string
|
|
wantHost string
|
|
wantPort int
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "Unix socket",
|
|
uri: "nbd:unix:/run/user/1000/nbd-vol-123.sock",
|
|
wantType: "unix",
|
|
wantPath: "/run/user/1000/nbd-vol-123.sock",
|
|
},
|
|
{
|
|
name: "TCP address",
|
|
uri: "nbd://127.0.0.1:34305",
|
|
wantType: "inet",
|
|
wantHost: "127.0.0.1",
|
|
wantPort: 34305,
|
|
},
|
|
{
|
|
name: "TCP with hostname",
|
|
uri: "nbd://storage.local:9000",
|
|
wantType: "inet",
|
|
wantHost: "storage.local",
|
|
wantPort: 9000,
|
|
},
|
|
{
|
|
name: "Empty socket path",
|
|
uri: "nbd:unix:",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Missing port in TCP",
|
|
uri: "nbd://127.0.0.1",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Invalid port",
|
|
uri: "nbd://127.0.0.1:notaport",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Unsupported format",
|
|
uri: "http://example.com",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Empty string",
|
|
uri: "",
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
serverType, path, host, port, err := ParseNBDURI(tt.uri)
|
|
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tt.wantType, serverType)
|
|
assert.Equal(t, tt.wantPath, path)
|
|
assert.Equal(t, tt.wantHost, host)
|
|
assert.Equal(t, tt.wantPort, port)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarshalToXML(t *testing.T) {
|
|
type TestStruct struct {
|
|
Name string `xml:"Name"`
|
|
Value int `xml:"Value"`
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
input any
|
|
expectError bool
|
|
validate func(t *testing.T, xmlData []byte)
|
|
}{
|
|
{
|
|
name: "Valid struct",
|
|
input: TestStruct{
|
|
Name: "test",
|
|
Value: 123,
|
|
},
|
|
expectError: false,
|
|
validate: func(t *testing.T, xmlData []byte) {
|
|
assert.Contains(t, string(xmlData), "<Name>test</Name>")
|
|
assert.Contains(t, string(xmlData), "<Value>123</Value>")
|
|
},
|
|
},
|
|
{
|
|
name: "Pointer to struct",
|
|
input: &TestStruct{
|
|
Name: "pointer",
|
|
Value: 456,
|
|
},
|
|
expectError: false,
|
|
validate: func(t *testing.T, xmlData []byte) {
|
|
assert.Contains(t, string(xmlData), "<Name>pointer</Name>")
|
|
assert.Contains(t, string(xmlData), "<Value>456</Value>")
|
|
},
|
|
},
|
|
{
|
|
name: "Invalid type (channel)",
|
|
input: make(chan int),
|
|
expectError: true,
|
|
validate: nil,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
xmlData, err := MarshalToXML(tt.input)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, xmlData)
|
|
if tt.validate != nil {
|
|
tt.validate(t, xmlData)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateIAMXMLPayload(t *testing.T) {
|
|
type User struct {
|
|
UserName string `locationName:"UserName" type:"string"`
|
|
UserId string `locationName:"UserId" type:"string"`
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
action string
|
|
payload any
|
|
validate func(t *testing.T, result any)
|
|
}{
|
|
{
|
|
name: "CreateUser wrapping",
|
|
action: "CreateUser",
|
|
payload: User{
|
|
UserName: "testuser",
|
|
UserId: "AIDA12345",
|
|
},
|
|
validate: func(t *testing.T, result any) {
|
|
xmlBytes, err := MarshalToXML(result)
|
|
assert.NoError(t, err)
|
|
xmlStr := string(xmlBytes)
|
|
assert.Contains(t, xmlStr, "CreateUserResponse")
|
|
assert.Contains(t, xmlStr, "CreateUserResult")
|
|
assert.Contains(t, xmlStr, "testuser")
|
|
assert.Contains(t, xmlStr, "AIDA12345")
|
|
},
|
|
},
|
|
{
|
|
name: "ListUsers wrapping",
|
|
action: "ListUsers",
|
|
payload: User{
|
|
UserName: "admin",
|
|
UserId: "AIDA99999",
|
|
},
|
|
validate: func(t *testing.T, result any) {
|
|
xmlBytes, err := MarshalToXML(result)
|
|
assert.NoError(t, err)
|
|
xmlStr := string(xmlBytes)
|
|
assert.Contains(t, xmlStr, "ListUsersResponse")
|
|
assert.Contains(t, xmlStr, "ListUsersResult")
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := GenerateIAMXMLPayload(tt.action, tt.payload)
|
|
assert.NotNil(t, result)
|
|
if tt.validate != nil {
|
|
tt.validate(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestKillProcess(t *testing.T) {
|
|
// Create a test process
|
|
cmd := exec.Command("sleep", "60")
|
|
err := cmd.Start()
|
|
assert.NoError(t, err)
|
|
|
|
pid := cmd.Process.Pid
|
|
|
|
// Wait for the process to actually terminate
|
|
// On macOS, the process cleanup can take longer
|
|
|
|
go func() {
|
|
time.Sleep(500 * time.Millisecond)
|
|
// Kill the process
|
|
err = KillProcess(pid)
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
|
|
|
// Reap the process so it does not stay DEFUNCT
|
|
if err := cmd.Wait(); err != nil {
|
|
// often you expect a non nil error here since it was terminated by a signal
|
|
t.Logf("process exited after SIGTERM: %v", err)
|
|
}
|
|
|
|
// Wait for process to be reaped
|
|
//_ = cmd.Wait()
|
|
|
|
// On macOS, after Wait(), Signal(0) may not reliably detect dead process
|
|
// So we skip this check as it's platform-dependent behavior
|
|
// The fact that KillProcess returned no error is sufficient
|
|
|
|
// Test killing non-existent process
|
|
err = KillProcess(999999)
|
|
assert.Error(t, err, "Should error when killing non-existent process")
|
|
}
|
|
|
|
func TestStopProcess(t *testing.T) {
|
|
// Create and start a test process
|
|
cmd := exec.Command("sleep", "60")
|
|
err := cmd.Start()
|
|
assert.NoError(t, err)
|
|
|
|
// Write PID file
|
|
testName := "stopprocess-test"
|
|
|
|
err = WritePidFile(testName, cmd.Process.Pid)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
go func() {
|
|
time.Sleep(500 * time.Millisecond)
|
|
|
|
// Stop the process
|
|
err = StopProcess(testName)
|
|
assert.NoError(t, err)
|
|
|
|
// Verify PID file was removed
|
|
_, err = ReadPidFile(testName)
|
|
assert.Error(t, err, "PID file should be removed")
|
|
|
|
}()
|
|
|
|
// Reap the process so it does not stay DEFUNCT
|
|
if err := cmd.Wait(); err != nil {
|
|
// often you expect a non nil error here since it was terminated by a signal
|
|
t.Logf("process exited after SIGTERM: %v", err)
|
|
}
|
|
|
|
// Test stopping non-existent process
|
|
err = StopProcess("nonexistent-process")
|
|
assert.Error(t, err, "Should error when stopping non-existent process")
|
|
}
|
|
|
|
// Test file extraction process
|
|
|
|
func TestExtractDiskImageFromFile(t *testing.T) {
|
|
|
|
tmpDir, err := os.MkdirTemp("", "hive-utils-test-*")
|
|
|
|
t.Log("Temp dir:", tmpDir)
|
|
|
|
assert.NoError(t, err, "Temp dir should be created")
|
|
|
|
// Sample .xz (fail)
|
|
imagePath, err := ExtractDiskImageFromFile("/tmp/file.xz", tmpDir)
|
|
|
|
assert.Empty(t, imagePath, "Should be blank")
|
|
assert.Error(t, err, "Should error")
|
|
|
|
// Sample incorrect image (fail)
|
|
imagePath, err = ExtractDiskImageFromFile("../../tests/ebs.json", tmpDir)
|
|
|
|
assert.Empty(t, imagePath, "Should be blank")
|
|
assert.Error(t, err, "Should error")
|
|
assert.ErrorContains(t, err, "unsupported filetype")
|
|
|
|
// Sample incorrect image (fail)
|
|
imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image-bad.raw", tmpDir)
|
|
|
|
assert.NotEmpty(t, imagePath, "Should be blank")
|
|
assert.Error(t, err, "Should error")
|
|
assert.ErrorContains(t, err, "no valid disk image found")
|
|
|
|
// Sample raw
|
|
imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image.raw", tmpDir)
|
|
|
|
assert.NotEmpty(t, imagePath, "Should not be blank")
|
|
assert.Contains(t, imagePath, ".raw")
|
|
|
|
assert.NoError(t, err, "Should not error")
|
|
|
|
_, err = exec.LookPath("tar")
|
|
|
|
if err == nil {
|
|
|
|
// Sample .tgz
|
|
imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image2.tgz", tmpDir)
|
|
|
|
assert.NotEmpty(t, imagePath, "Should not be blank")
|
|
assert.Contains(t, imagePath, ".img")
|
|
|
|
assert.NoError(t, err, "Should not error")
|
|
|
|
// Sample .tar
|
|
imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image.tar", tmpDir)
|
|
|
|
assert.NotEmpty(t, imagePath, "Should not be blank")
|
|
assert.Contains(t, imagePath, ".raw")
|
|
|
|
assert.NoError(t, err, "Should not error")
|
|
|
|
// Sample .tar.gz
|
|
imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image.tar.gz", tmpDir)
|
|
|
|
assert.NotEmpty(t, imagePath, "Should not be blank")
|
|
assert.Contains(t, imagePath, ".raw")
|
|
|
|
assert.NoError(t, err, "Should not error")
|
|
|
|
// Sample xz
|
|
imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image.tar.xz", tmpDir)
|
|
|
|
assert.NotEmpty(t, imagePath, "Should not be blank")
|
|
assert.Contains(t, imagePath, ".raw")
|
|
|
|
assert.NoError(t, err, "Should not error")
|
|
|
|
// Sample tgz
|
|
imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image.tgz", tmpDir)
|
|
|
|
assert.NotEmpty(t, imagePath, "Should not be blank")
|
|
assert.Contains(t, imagePath, ".raw")
|
|
|
|
assert.NoError(t, err, "Should not error")
|
|
|
|
} else {
|
|
t.Skip("tar command not found, skipping archive extraction tests")
|
|
}
|
|
|
|
//err = os.RemoveAll(tmpDir)
|
|
//assert.NoError(t, err, "Could not remove temp dir")
|
|
|
|
}
|
|
|
|
func TestIsSocketURI(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
uri string
|
|
want bool
|
|
}{
|
|
{"Socket suffix", "/run/nbd-vol.sock", true},
|
|
{"Unix prefix", "unix:/run/nbd-vol", true},
|
|
{"Both", "unix:/run/nbd-vol.sock", true},
|
|
{"TCP URI", "nbd://127.0.0.1:9000", false},
|
|
{"Empty", "", false},
|
|
{"Random path", "/tmp/somefile.txt", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert.Equal(t, tt.want, IsSocketURI(tt.uri))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFormatNBDSocketURI(t *testing.T) {
|
|
assert.Equal(t, "nbd:unix:/run/nbd-vol.sock", FormatNBDSocketURI("/run/nbd-vol.sock"))
|
|
assert.Equal(t, "nbd:unix:/tmp/test.sock", FormatNBDSocketURI("/tmp/test.sock"))
|
|
}
|
|
|
|
func TestFormatNBDTCPURI(t *testing.T) {
|
|
assert.Equal(t, "nbd://127.0.0.1:9000", FormatNBDTCPURI("127.0.0.1", 9000))
|
|
assert.Equal(t, "nbd://storage.local:34305", FormatNBDTCPURI("storage.local", 34305))
|
|
}
|
|
|
|
func TestGenerateUniqueSocketFile(t *testing.T) {
|
|
path1, err := GenerateUniqueSocketFile("vol-123")
|
|
require.NoError(t, err)
|
|
assert.Contains(t, path1, "nbd-vol-123-")
|
|
assert.True(t, strings.HasSuffix(path1, ".sock"))
|
|
|
|
// Two calls should produce different paths (different timestamps)
|
|
time.Sleep(time.Nanosecond)
|
|
path2, err := GenerateUniqueSocketFile("vol-123")
|
|
require.NoError(t, err)
|
|
assert.NotEqual(t, path1, path2)
|
|
|
|
// Empty volume name
|
|
_, err = GenerateUniqueSocketFile("")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestGenerateXMLPayload(t *testing.T) {
|
|
type Inner struct {
|
|
Name string `locationName:"Name" type:"string"`
|
|
}
|
|
|
|
result := GenerateXMLPayload("DescribeInstancesResponse", Inner{Name: "test"})
|
|
assert.NotNil(t, result)
|
|
|
|
xmlBytes, err := MarshalToXML(result)
|
|
require.NoError(t, err)
|
|
xmlStr := string(xmlBytes)
|
|
assert.Contains(t, xmlStr, "DescribeInstancesResponse")
|
|
assert.Contains(t, xmlStr, "test")
|
|
}
|
|
|
|
func TestHumanBytes(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
b uint64
|
|
want string
|
|
}{
|
|
{"Zero", 0, "0 B"},
|
|
{"Bytes", 512, "512 B"},
|
|
{"KiB", 1024, "1.0 KiB"},
|
|
{"KiB fractional", 1536, "1.5 KiB"},
|
|
{"MiB", 1048576, "1.0 MiB"},
|
|
{"GiB", 1073741824, "1.0 GiB"},
|
|
{"Large GiB", 5368709120, "5.0 GiB"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert.Equal(t, tt.want, humanBytes(tt.b))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDirExists(t *testing.T) {
|
|
// Existing directory
|
|
assert.True(t, dirExists(os.TempDir()))
|
|
|
|
// Non-existent path
|
|
assert.False(t, dirExists("/nonexistent/path/should/not/exist"))
|
|
|
|
// File (not a directory)
|
|
tmpFile, err := os.CreateTemp("", "direxists-test-*")
|
|
require.NoError(t, err)
|
|
defer os.Remove(tmpFile.Name())
|
|
tmpFile.Close()
|
|
assert.False(t, dirExists(tmpFile.Name()))
|
|
}
|
|
|
|
func TestProgressWriter(t *testing.T) {
|
|
var total int
|
|
pw := progressWriter(func(n int) {
|
|
total += n
|
|
})
|
|
|
|
n, err := pw.Write([]byte("hello"))
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 5, n)
|
|
assert.Equal(t, 5, total)
|
|
|
|
n, err = pw.Write([]byte("world!"))
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 6, n)
|
|
assert.Equal(t, 11, total)
|
|
}
|
|
|
|
func TestGeneratePidFile_EmptyName(t *testing.T) {
|
|
_, err := GeneratePidFile("")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestGenerateSocketFile_EmptyName(t *testing.T) {
|
|
_, err := GenerateSocketFile("")
|
|
assert.Error(t, err)
|
|
}
|