test: Add tests for convertDockerSwarmInfo, namespacePathsForDatastore, preserveFailedStorageBackups

- convertDockerSwarmInfo: 66.7%→100% (4 cases for nil, empty, populated structs)
- namespacePathsForDatastore: 92.3%→100% (removed unreachable dead code)
- preserveFailedStorageBackups: 91.3%→100% (6 cases for filtering, deduplication)
This commit is contained in:
rcourtman 2025-12-01 19:04:23 +00:00
parent 79272eda40
commit dfc0059bd9
3 changed files with 254 additions and 3 deletions

View file

@ -8134,9 +8134,6 @@ func namespacePathsForDatastore(ds models.PBSDatastore) []string {
seen[path] = struct{}{}
paths = append(paths, path)
}
if len(paths) == 0 {
paths = append(paths, "")
}
return paths
}

View file

@ -7,6 +7,7 @@ import (
"github.com/rcourtman/pulse-go-rewrite/internal/config"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
agentsdocker "github.com/rcourtman/pulse-go-rewrite/pkg/agents/docker"
)
func TestParseDurationEnv(t *testing.T) {
@ -1677,3 +1678,110 @@ func TestSchedulerHealth(t *testing.T) {
}
})
}
func TestConvertDockerSwarmInfo(t *testing.T) {
tests := []struct {
name string
input *agentsdocker.SwarmInfo
expected *models.DockerSwarmInfo
}{
{
name: "nil input returns nil",
input: nil,
expected: nil,
},
{
name: "empty struct returns empty struct",
input: &agentsdocker.SwarmInfo{},
expected: &models.DockerSwarmInfo{},
},
{
name: "all fields populated",
input: &agentsdocker.SwarmInfo{
NodeID: "node-abc123",
NodeRole: "manager",
LocalState: "active",
ControlAvailable: true,
ClusterID: "cluster-xyz789",
ClusterName: "my-swarm",
Scope: "swarm",
Error: "",
},
expected: &models.DockerSwarmInfo{
NodeID: "node-abc123",
NodeRole: "manager",
LocalState: "active",
ControlAvailable: true,
ClusterID: "cluster-xyz789",
ClusterName: "my-swarm",
Scope: "swarm",
Error: "",
},
},
{
name: "worker node with error",
input: &agentsdocker.SwarmInfo{
NodeID: "node-worker1",
NodeRole: "worker",
LocalState: "pending",
ControlAvailable: false,
ClusterID: "",
ClusterName: "",
Scope: "local",
Error: "connection refused",
},
expected: &models.DockerSwarmInfo{
NodeID: "node-worker1",
NodeRole: "worker",
LocalState: "pending",
ControlAvailable: false,
ClusterID: "",
ClusterName: "",
Scope: "local",
Error: "connection refused",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := convertDockerSwarmInfo(tt.input)
if tt.expected == nil {
if result != nil {
t.Errorf("expected nil, got %+v", result)
}
return
}
if result == nil {
t.Fatal("expected non-nil result")
}
if result.NodeID != tt.expected.NodeID {
t.Errorf("NodeID: expected %q, got %q", tt.expected.NodeID, result.NodeID)
}
if result.NodeRole != tt.expected.NodeRole {
t.Errorf("NodeRole: expected %q, got %q", tt.expected.NodeRole, result.NodeRole)
}
if result.LocalState != tt.expected.LocalState {
t.Errorf("LocalState: expected %q, got %q", tt.expected.LocalState, result.LocalState)
}
if result.ControlAvailable != tt.expected.ControlAvailable {
t.Errorf("ControlAvailable: expected %v, got %v", tt.expected.ControlAvailable, result.ControlAvailable)
}
if result.ClusterID != tt.expected.ClusterID {
t.Errorf("ClusterID: expected %q, got %q", tt.expected.ClusterID, result.ClusterID)
}
if result.ClusterName != tt.expected.ClusterName {
t.Errorf("ClusterName: expected %q, got %q", tt.expected.ClusterName, result.ClusterName)
}
if result.Scope != tt.expected.Scope {
t.Errorf("Scope: expected %q, got %q", tt.expected.Scope, result.Scope)
}
if result.Error != tt.expected.Error {
t.Errorf("Error: expected %q, got %q", tt.expected.Error, result.Error)
}
})
}
}

View file

@ -92,6 +92,152 @@ func TestPreserveFailedStorageBackupsSkipsDuplicates(t *testing.T) {
}
}
func TestPreserveFailedStorageBackupsEmptyPreserveMap(t *testing.T) {
instance := "pve01"
current := []models.StorageBackup{
{ID: "backup1", Instance: instance, Storage: "local"},
}
snapshot := models.StateSnapshot{
PVEBackups: models.PVEBackups{
StorageBackups: []models.StorageBackup{
{ID: "backup2", Instance: instance, Storage: "nas-share"},
},
},
}
merged, storages := preserveFailedStorageBackups(instance, snapshot, nil, current)
if len(merged) != 1 {
t.Fatalf("expected current unchanged with 1 backup, got %d", len(merged))
}
if storages != nil {
t.Fatalf("expected nil storages list, got %v", storages)
}
// Also test empty map (not nil)
merged2, storages2 := preserveFailedStorageBackups(instance, snapshot, map[string]struct{}{}, current)
if len(merged2) != 1 {
t.Fatalf("expected current unchanged with 1 backup, got %d", len(merged2))
}
if storages2 != nil {
t.Fatalf("expected nil storages list for empty map, got %v", storages2)
}
}
func TestPreserveFailedStorageBackupsNoMatchingBackups(t *testing.T) {
instance := "pve01"
current := []models.StorageBackup{
{ID: "backup1", Instance: instance, Storage: "local"},
}
snapshot := models.StateSnapshot{
PVEBackups: models.PVEBackups{
StorageBackups: []models.StorageBackup{
{ID: "backup2", Instance: instance, Storage: "other-storage"},
},
},
}
toPreserve := map[string]struct{}{
"nas-share": {}, // Storage not in snapshot
}
merged, storages := preserveFailedStorageBackups(instance, snapshot, toPreserve, current)
if len(merged) != 1 {
t.Fatalf("expected current unchanged with 1 backup, got %d", len(merged))
}
if storages != nil {
t.Fatalf("expected nil storages list when no matches, got %v", storages)
}
}
func TestPreserveFailedStorageBackupsWrongInstance(t *testing.T) {
instance := "pve01"
current := []models.StorageBackup{
{ID: "backup1", Instance: instance, Storage: "local"},
}
snapshot := models.StateSnapshot{
PVEBackups: models.PVEBackups{
StorageBackups: []models.StorageBackup{
{ID: "backup2", Instance: "pve02", Storage: "nas-share"}, // Wrong instance
},
},
}
toPreserve := map[string]struct{}{
"nas-share": {},
}
merged, storages := preserveFailedStorageBackups(instance, snapshot, toPreserve, current)
if len(merged) != 1 {
t.Fatalf("expected current unchanged (wrong instance skipped), got %d backups", len(merged))
}
if storages != nil {
t.Fatalf("expected nil storages list, got %v", storages)
}
}
func TestPreserveFailedStorageBackupsStorageNotInPreserveMap(t *testing.T) {
instance := "pve01"
current := []models.StorageBackup{
{ID: "backup1", Instance: instance, Storage: "local"},
}
snapshot := models.StateSnapshot{
PVEBackups: models.PVEBackups{
StorageBackups: []models.StorageBackup{
{ID: "backup2", Instance: instance, Storage: "nas-share"},
{ID: "backup3", Instance: instance, Storage: "other-storage"},
},
},
}
toPreserve := map[string]struct{}{
"nas-share": {}, // Only nas-share should be preserved
}
merged, storages := preserveFailedStorageBackups(instance, snapshot, toPreserve, current)
if len(merged) != 2 {
t.Fatalf("expected 2 backups (original + nas-share), got %d", len(merged))
}
if len(storages) != 1 || storages[0] != "nas-share" {
t.Fatalf("expected [nas-share], got %v", storages)
}
// Verify other-storage was not added
for _, b := range merged {
if b.Storage == "other-storage" {
t.Fatal("other-storage should not have been preserved")
}
}
}
func TestPreserveFailedStorageBackupsSortedStorageNames(t *testing.T) {
instance := "pve01"
current := []models.StorageBackup{}
snapshot := models.StateSnapshot{
PVEBackups: models.PVEBackups{
StorageBackups: []models.StorageBackup{
{ID: "backup1", Instance: instance, Storage: "zebra-storage"},
{ID: "backup2", Instance: instance, Storage: "alpha-storage"},
{ID: "backup3", Instance: instance, Storage: "middle-storage"},
},
},
}
toPreserve := map[string]struct{}{
"zebra-storage": {},
"alpha-storage": {},
"middle-storage": {},
}
merged, storages := preserveFailedStorageBackups(instance, snapshot, toPreserve, current)
if len(merged) != 3 {
t.Fatalf("expected 3 backups, got %d", len(merged))
}
expected := []string{"alpha-storage", "middle-storage", "zebra-storage"}
if !slices.Equal(storages, expected) {
t.Fatalf("expected sorted storages %v, got %v", expected, storages)
}
}
func TestStorageNamesForNode(t *testing.T) {
tests := []struct {
name string