test: Add tests for monitoring and notifications functions

- buildCephClusterModel: 0% → 100% (11 test cases)
- collectContainerRootUsage: 0% → 100% (18 test cases)
- NotificationManager getters/setters: 8 functions now tested

Overall coverage: 45.5% → 45.8%
This commit is contained in:
rcourtman 2025-12-01 17:33:36 +00:00
parent 74fe6b9223
commit b444793897
3 changed files with 1126 additions and 0 deletions

View file

@ -1,8 +1,12 @@
package monitoring
import (
"context"
"errors"
"math"
"testing"
"github.com/rcourtman/pulse-go-rewrite/pkg/proxmox"
)
func TestClampToInt64(t *testing.T) {
@ -296,3 +300,333 @@ func TestContainerDiskOverride_Fields(t *testing.T) {
t.Errorf("Total = %d, want 10737418240", override.Total)
}
}
// stubContainerDiskClient implements PVEClientInterface for testing collectContainerRootUsage
type stubContainerDiskClient struct {
stubPVEClient
storages []proxmox.Storage
storagesErr error
contentByStore map[string][]proxmox.StorageContent
contentErr map[string]error
}
func (s *stubContainerDiskClient) GetStorage(ctx context.Context, node string) ([]proxmox.Storage, error) {
if s.storagesErr != nil {
return nil, s.storagesErr
}
return s.storages, nil
}
func (s *stubContainerDiskClient) GetStorageContent(ctx context.Context, node, storage string) ([]proxmox.StorageContent, error) {
if s.contentErr != nil {
if err, ok := s.contentErr[storage]; ok {
return nil, err
}
}
if s.contentByStore != nil {
return s.contentByStore[storage], nil
}
return nil, nil
}
func TestCollectContainerRootUsage(t *testing.T) {
mon := &Monitor{}
t.Run("empty vmIDs list returns empty map", func(t *testing.T) {
client := &stubContainerDiskClient{}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{})
if len(result) != 0 {
t.Errorf("expected empty map, got %d entries", len(result))
}
})
t.Run("nil vmIDs list returns empty map", func(t *testing.T) {
client := &stubContainerDiskClient{}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", nil)
if len(result) != 0 {
t.Errorf("expected empty map, got %d entries", len(result))
}
})
t.Run("no storages returns empty map", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 0 {
t.Errorf("expected empty map, got %d entries", len(result))
}
})
t.Run("storage does not support container volumes", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "backup-store", Content: "backup,iso", Enabled: 1, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"backup-store": {{Volid: "backup-store:subvol-100-disk-0", VMID: 100, Used: 1024, Size: 4096}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 0 {
t.Errorf("expected empty map (storage doesn't support container volumes), got %d entries", len(result))
}
})
t.Run("GetStorageContent error is handled gracefully", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "local", Content: "rootdir", Enabled: 1, Active: 1},
{Storage: "zfs", Content: "rootdir", Enabled: 1, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"zfs": {{Volid: "zfs:subvol-100-disk-0", VMID: 100, Used: 2048, Size: 8192}},
},
contentErr: map[string]error{
"local": errors.New("storage offline"),
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
// Should continue to next storage and find the container in zfs
if len(result) != 1 {
t.Errorf("expected 1 entry from zfs storage, got %d", len(result))
}
if override, ok := result[100]; !ok || override.Used != 2048 {
t.Errorf("expected override for vmid 100 with Used=2048, got %+v", result)
}
})
t.Run("GetStorage error returns empty map", func(t *testing.T) {
client := &stubContainerDiskClient{
storagesErr: errors.New("API error"),
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 0 {
t.Errorf("expected empty map on GetStorage error, got %d entries", len(result))
}
})
t.Run("matching container root volume found", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "local", Content: "rootdir", Enabled: 1, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"local": {{Volid: "local:subvol-100-disk-0", VMID: 100, Used: 1024, Size: 4096}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 1 {
t.Fatalf("expected 1 entry, got %d", len(result))
}
override, ok := result[100]
if !ok {
t.Fatal("expected entry for vmid 100")
}
if override.Used != 1024 {
t.Errorf("Used = %d, want 1024", override.Used)
}
if override.Total != 4096 {
t.Errorf("Total = %d, want 4096", override.Total)
}
})
t.Run("volume with Used=0 is skipped", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "local", Content: "rootdir", Enabled: 1, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"local": {{Volid: "local:subvol-100-disk-0", VMID: 100, Used: 0, Size: 4096}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 0 {
t.Errorf("expected empty map (Used=0), got %d entries", len(result))
}
})
t.Run("volume with non-matching VMID is skipped", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "local", Content: "rootdir", Enabled: 1, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"local": {{Volid: "local:subvol-200-disk-0", VMID: 200, Used: 1024, Size: 4096}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 0 {
t.Errorf("expected empty map (VMID not in list), got %d entries", len(result))
}
})
t.Run("volume with non-root volid is skipped", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "local", Content: "rootdir", Enabled: 1, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"local": {{Volid: "local:subvol-100-disk-1", VMID: 100, Used: 1024, Size: 4096}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 0 {
t.Errorf("expected empty map (not root disk), got %d entries", len(result))
}
})
t.Run("volume with VMID=0 is skipped", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "local", Content: "rootdir", Enabled: 1, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"local": {{Volid: "local:subvol-100-disk-0", VMID: 0, Used: 1024, Size: 4096}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 0 {
t.Errorf("expected empty map (VMID=0), got %d entries", len(result))
}
})
t.Run("multiple containers multiple storages", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "local", Content: "rootdir", Enabled: 1, Active: 1},
{Storage: "zfs", Content: "images,rootdir", Enabled: 1, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"local": {
{Volid: "local:subvol-100-disk-0", VMID: 100, Used: 1024, Size: 4096},
{Volid: "local:subvol-101-disk-0", VMID: 101, Used: 2048, Size: 8192},
},
"zfs": {
{Volid: "zfs:subvol-102-disk-0", VMID: 102, Used: 3072, Size: 12288},
{Volid: "zfs:subvol-103-disk-0", VMID: 103, Used: 4096, Size: 16384},
},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100, 101, 102, 103})
if len(result) != 4 {
t.Fatalf("expected 4 entries, got %d", len(result))
}
expected := map[int]containerDiskOverride{
100: {Used: 1024, Total: 4096},
101: {Used: 2048, Total: 8192},
102: {Used: 3072, Total: 12288},
103: {Used: 4096, Total: 16384},
}
for vmid, want := range expected {
got, ok := result[vmid]
if !ok {
t.Errorf("missing entry for vmid %d", vmid)
continue
}
if got.Used != want.Used || got.Total != want.Total {
t.Errorf("vmid %d: got %+v, want %+v", vmid, got, want)
}
}
})
t.Run("storage not enabled is skipped", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "local", Content: "rootdir", Enabled: 0, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"local": {{Volid: "local:subvol-100-disk-0", VMID: 100, Used: 1024, Size: 4096}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 0 {
t.Errorf("expected empty map (storage not enabled), got %d entries", len(result))
}
})
t.Run("storage not active is skipped", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "local", Content: "rootdir", Enabled: 1, Active: 0},
},
contentByStore: map[string][]proxmox.StorageContent{
"local": {{Volid: "local:subvol-100-disk-0", VMID: 100, Used: 1024, Size: 4096}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 0 {
t.Errorf("expected empty map (storage not active), got %d entries", len(result))
}
})
t.Run("storage with empty name is skipped", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "", Content: "rootdir", Enabled: 1, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"": {{Volid: ":subvol-100-disk-0", VMID: 100, Used: 1024, Size: 4096}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 0 {
t.Errorf("expected empty map (storage name empty), got %d entries", len(result))
}
})
t.Run("vm-disk pattern also matches", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "local", Content: "images", Enabled: 1, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"local": {{Volid: "local:vm-100-disk-0", VMID: 100, Used: 5000, Size: 10000}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 1 {
t.Fatalf("expected 1 entry (vm-disk pattern), got %d", len(result))
}
if result[100].Used != 5000 {
t.Errorf("Used = %d, want 5000", result[100].Used)
}
})
t.Run("subvol content type works", func(t *testing.T) {
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "zfs", Content: "subvol", Enabled: 1, Active: 1},
},
contentByStore: map[string][]proxmox.StorageContent{
"zfs": {{Volid: "zfs:subvol-100-disk-0", VMID: 100, Used: 7777, Size: 9999}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
if len(result) != 1 {
t.Fatalf("expected 1 entry (subvol content type), got %d", len(result))
}
if result[100].Used != 7777 {
t.Errorf("Used = %d, want 7777", result[100].Used)
}
})
t.Run("PBS storage with Active=0 is queryable for backup content", func(t *testing.T) {
// PBS storages report Active=0 but should be queryable if they have backup content
// However, collectContainerRootUsage skips storages that don't support container volumes
// PBS with "backup" content won't match rootdir/images/subvol
client := &stubContainerDiskClient{
storages: []proxmox.Storage{
{Storage: "pbs", Content: "backup", Type: "pbs", Enabled: 1, Active: 0},
},
contentByStore: map[string][]proxmox.StorageContent{
"pbs": {{Volid: "pbs:subvol-100-disk-0", VMID: 100, Used: 1024, Size: 4096}},
},
}
result := mon.collectContainerRootUsage(context.Background(), client, "node1", []int{100})
// PBS with only "backup" content doesn't support container volumes
if len(result) != 0 {
t.Errorf("expected empty map (PBS backup-only storage), got %d entries", len(result))
}
})
}