package tools import ( "context" "encoding/json" "testing" "time" "github.com/rcourtman/pulse-go-rewrite/internal/models" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestExecuteGetCephStatus(t *testing.T) { ctx := context.Background() exec := NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: models.StateSnapshot{}}}) result, err := exec.executeGetCephStatus(ctx, map[string]interface{}{}) require.NoError(t, err) assert.Equal(t, "No Ceph clusters found. Ceph may not be configured or data is not yet available.", result.Content[0].Text) state := models.StateSnapshot{ CephClusters: []models.CephCluster{ { Name: "alpha", Health: "HEALTH_OK", HealthMessage: "ok", NumOSDs: 3, NumOSDsUp: 2, NumOSDsIn: 3, NumMons: 1, }, }, } exec = NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err = exec.executeGetCephStatus(ctx, map[string]interface{}{ "cluster": "beta", }) require.NoError(t, err) assert.Equal(t, "Ceph cluster 'beta' not found.", result.Content[0].Text) result, err = exec.executeGetCephStatus(ctx, map[string]interface{}{}) require.NoError(t, err) assert.Contains(t, result.Content[0].Text, "alpha") assert.Contains(t, result.Content[0].Text, "HEALTH_OK") } func TestExecuteGetReplication(t *testing.T) { ctx := context.Background() exec := NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: models.StateSnapshot{}}}) result, err := exec.executeGetReplication(ctx, map[string]interface{}{}) require.NoError(t, err) assert.Equal(t, "No replication jobs found. Replication may not be configured.", result.Content[0].Text) now := time.Now() state := models.StateSnapshot{ ReplicationJobs: []models.ReplicationJob{ { ID: "rep1", GuestID: 101, GuestName: "vm101", TargetNode: "node2", Status: "ok", LastSyncTime: &now, LastSyncDurationHuman: "5s", }, }, } exec = NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err = exec.executeGetReplication(ctx, map[string]interface{}{ "vm_id": "999", }) require.NoError(t, err) assert.Equal(t, "No replication jobs found for VM 999.", result.Content[0].Text) result, err = exec.executeGetReplication(ctx, map[string]interface{}{ "vm_id": "101", }) require.NoError(t, err) assert.Contains(t, result.Content[0].Text, "rep1") } func TestExecuteListSnapshots(t *testing.T) { ctx := context.Background() now := time.Now() state := models.StateSnapshot{ VMs: []models.VM{ {VMID: 100, Name: "vm100"}, }, PVEBackups: models.PVEBackups{ GuestSnapshots: []models.GuestSnapshot{ { ID: "snap1", VMID: 100, Type: "vm", Node: "node1", Instance: "pve1", Name: "before-upgrade", Time: now, VMState: true, }, }, }, } exec := NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err := exec.executeListSnapshots(ctx, map[string]interface{}{ "guest_id": "100", "instance": "pve1", }) require.NoError(t, err) var resp SnapshotsResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) require.Len(t, resp.Snapshots, 1) assert.Equal(t, "snap1", resp.Snapshots[0].ID) assert.Equal(t, "vm100", resp.Snapshots[0].VMName) } func TestExecuteListPBSJobs(t *testing.T) { ctx := context.Background() exec := NewPulseToolExecutor(ExecutorConfig{}) result, err := exec.executeListPBSJobs(ctx, map[string]interface{}{}) require.NoError(t, err) assert.Equal(t, "Backup provider not available.", result.Content[0].Text) backupProvider := &mockBackupProvider{} backupProvider.On("GetPBSInstances").Return([]models.PBSInstance{ { ID: "pbs1", BackupJobs: []models.PBSBackupJob{ {ID: "job1", Store: "store1", Status: "ok", VMID: "101"}, }, }, }) exec = NewPulseToolExecutor(ExecutorConfig{BackupProvider: backupProvider}) result, err = exec.executeListPBSJobs(ctx, map[string]interface{}{ "job_type": "backup", }) require.NoError(t, err) var resp PBSJobsResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) require.Len(t, resp.Jobs, 1) assert.Equal(t, "job1", resp.Jobs[0].ID) } func TestExecuteGetConnectionHealth(t *testing.T) { ctx := context.Background() exec := NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: models.StateSnapshot{}}}) result, err := exec.executeGetConnectionHealth(ctx, map[string]interface{}{}) require.NoError(t, err) assert.Equal(t, "No connection health data available.", result.Content[0].Text) state := models.StateSnapshot{ ConnectionHealth: map[string]bool{ "pve1": true, "pve2": false, }, } exec = NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err = exec.executeGetConnectionHealth(ctx, map[string]interface{}{}) require.NoError(t, err) var resp ConnectionHealthResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) assert.Equal(t, 2, resp.Total) assert.Equal(t, 1, resp.Connected) assert.Equal(t, 1, resp.Disconnected) } func TestExecuteGetNetworkStats(t *testing.T) { ctx := context.Background() speed := int64(1000) state := models.StateSnapshot{ Hosts: []models.Host{ { Hostname: "host1", NetworkInterfaces: []models.HostNetworkInterface{ {Name: "eth0", MAC: "aa", Addresses: []string{"10.0.0.1"}, RXBytes: 1, TXBytes: 2, SpeedMbps: &speed}, }, }, }, DockerHosts: []models.DockerHost{ { Hostname: "dock1", NetworkInterfaces: []models.HostNetworkInterface{ {Name: "eth0", RXBytes: 3, TXBytes: 4}, }, }, }, } exec := NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err := exec.executeGetNetworkStats(ctx, map[string]interface{}{}) require.NoError(t, err) var resp NetworkStatsResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) assert.Len(t, resp.Hosts, 2) result, err = exec.executeGetNetworkStats(ctx, map[string]interface{}{ "host": "missing", }) require.NoError(t, err) assert.Equal(t, "No network statistics available for host 'missing'.", result.Content[0].Text) } func TestExecuteGetDiskIOStats(t *testing.T) { ctx := context.Background() state := models.StateSnapshot{ Hosts: []models.Host{ { Hostname: "host1", DiskIO: []models.DiskIO{ {Device: "sda", ReadBytes: 10, WriteBytes: 20}, }, }, }, } exec := NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err := exec.executeGetDiskIOStats(ctx, map[string]interface{}{}) require.NoError(t, err) var resp DiskIOStatsResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) assert.Len(t, resp.Hosts, 1) } func TestExecuteListPhysicalDisks(t *testing.T) { ctx := context.Background() now := time.Now() state := models.StateSnapshot{ PhysicalDisks: []models.PhysicalDisk{ { ID: "disk1", Node: "node1", Instance: "pve1", DevPath: "/dev/sda", Model: "model", Serial: "serial", WWN: "wwn", Type: "sata", Size: 1, Health: "PASSED", Wearout: 10, Temperature: 30, RPM: 7200, Used: "used", LastChecked: now, }, }, } exec := NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err := exec.executeListPhysicalDisks(ctx, map[string]interface{}{ "type": "sata", }) require.NoError(t, err) var resp PhysicalDisksResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) require.Len(t, resp.Disks, 1) assert.Equal(t, "disk1", resp.Disks[0].ID) } func TestExecuteGetResourceDisks(t *testing.T) { ctx := context.Background() state := models.StateSnapshot{ VMs: []models.VM{ { ID: "vm1", VMID: 101, Name: "vm1", Instance: "pve1", Disks: []models.Disk{ {Device: "vda", Usage: 85}, }, }, }, Containers: []models.Container{ { ID: "ct1", VMID: 201, Name: "ct1", Instance: "pve1", Disks: []models.Disk{ {Device: "vda", Usage: 50}, }, }, }, } exec := NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err := exec.executeGetResourceDisks(ctx, map[string]interface{}{ "min_usage": 80.0, }) require.NoError(t, err) var resp ResourceDisksResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) require.Len(t, resp.Resources, 1) assert.Equal(t, "vm1", resp.Resources[0].ID) } func TestExecuteListBackupTasks(t *testing.T) { ctx := context.Background() exec := NewPulseToolExecutor(ExecutorConfig{}) result, err := exec.executeListBackupTasks(ctx, map[string]interface{}{}) require.NoError(t, err) assert.Equal(t, "State provider not available.", result.Content[0].Text) now := time.Now() state := models.StateSnapshot{ VMs: []models.VM{ {VMID: 101, Name: "vm101"}, }, Containers: []models.Container{ {VMID: 201, Name: "ct201"}, }, PVEBackups: models.PVEBackups{ BackupTasks: []models.BackupTask{ {ID: "task1", VMID: 101, Node: "node1", Instance: "pve1", Status: "OK", StartTime: now}, {ID: "task2", VMID: 201, Node: "node2", Instance: "pve1", Status: "FAIL", StartTime: now}, }, }, } exec = NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err = exec.executeListBackupTasks(ctx, map[string]interface{}{ "guest_id": "101", "status": "ok", }) require.NoError(t, err) var resp BackupTasksListResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) require.Len(t, resp.Tasks, 1) assert.Equal(t, "task1", resp.Tasks[0].ID) } func TestExecuteGetSwarmStatus(t *testing.T) { ctx := context.Background() exec := NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: models.StateSnapshot{}}}) result, err := exec.executeGetSwarmStatus(ctx, map[string]interface{}{}) require.NoError(t, err) assert.True(t, result.IsError) assert.Contains(t, result.Content[0].Text, "host is required") state := models.StateSnapshot{ DockerHosts: []models.DockerHost{ {ID: "h1", Hostname: "dock1"}, }, } exec = NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err = exec.executeGetSwarmStatus(ctx, map[string]interface{}{ "host": "missing", }) require.NoError(t, err) assert.Equal(t, "Docker host 'missing' not found.", result.Content[0].Text) result, err = exec.executeGetSwarmStatus(ctx, map[string]interface{}{ "host": "dock1", }) require.NoError(t, err) assert.Equal(t, "Docker host 'dock1' is not part of a Swarm cluster.", result.Content[0].Text) state.DockerHosts[0].Swarm = &models.DockerSwarmInfo{ NodeID: "node-1", NodeRole: "manager", LocalState: "active", ClusterID: "cluster-1", ClusterName: "swarm-1", } exec = NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err = exec.executeGetSwarmStatus(ctx, map[string]interface{}{ "host": "dock1", }) require.NoError(t, err) var resp SwarmStatusResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) assert.Equal(t, "dock1", resp.Host) assert.Equal(t, "node-1", resp.Status.NodeID) } func TestExecuteListDockerServices(t *testing.T) { ctx := context.Background() exec := NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: models.StateSnapshot{}}}) result, err := exec.executeListDockerServices(ctx, map[string]interface{}{}) require.NoError(t, err) assert.True(t, result.IsError) assert.Contains(t, result.Content[0].Text, "host is required") state := models.StateSnapshot{ DockerHosts: []models.DockerHost{ {ID: "h1", Hostname: "dock1"}, }, } exec = NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err = exec.executeListDockerServices(ctx, map[string]interface{}{ "host": "dock1", }) require.NoError(t, err) assert.Equal(t, "No Docker services found on host 'dock1'. The host may not be a Swarm manager.", result.Content[0].Text) state.DockerHosts[0].Services = []models.DockerService{ {ID: "svc1", Name: "web", Stack: "stack1", DesiredTasks: 1, RunningTasks: 1}, {ID: "svc2", Name: "db", Stack: "stack2", DesiredTasks: 1, RunningTasks: 1}, } exec = NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err = exec.executeListDockerServices(ctx, map[string]interface{}{ "host": "dock1", "stack": "stack1", }) require.NoError(t, err) var resp DockerServicesResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) require.Len(t, resp.Services, 1) assert.Equal(t, "svc1", resp.Services[0].ID) } func TestExecuteListDockerTasks(t *testing.T) { ctx := context.Background() exec := NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: models.StateSnapshot{}}}) result, err := exec.executeListDockerTasks(ctx, map[string]interface{}{}) require.NoError(t, err) assert.True(t, result.IsError) assert.Contains(t, result.Content[0].Text, "host is required") state := models.StateSnapshot{ DockerHosts: []models.DockerHost{ {ID: "h1", Hostname: "dock1"}, }, } exec = NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err = exec.executeListDockerTasks(ctx, map[string]interface{}{ "host": "dock1", }) require.NoError(t, err) assert.Equal(t, "No Docker tasks found on host 'dock1'. The host may not be a Swarm manager.", result.Content[0].Text) state.DockerHosts[0].Tasks = []models.DockerTask{ {ID: "task1", ServiceID: "svc1", ServiceName: "web", DesiredState: "running", CurrentState: "running"}, {ID: "task2", ServiceID: "svc2", ServiceName: "db", DesiredState: "running", CurrentState: "running"}, } exec = NewPulseToolExecutor(ExecutorConfig{StateProvider: &mockStateProvider{state: state}}) result, err = exec.executeListDockerTasks(ctx, map[string]interface{}{ "host": "dock1", "service": "web", }) require.NoError(t, err) var resp DockerTasksResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) require.Len(t, resp.Tasks, 1) assert.Equal(t, "task1", resp.Tasks[0].ID) } func TestExecuteGetHostRAIDStatus(t *testing.T) { ctx := context.Background() exec := NewPulseToolExecutor(ExecutorConfig{}) result, err := exec.executeGetHostRAIDStatus(ctx, map[string]interface{}{}) require.NoError(t, err) assert.Equal(t, "Disk health provider not available.", result.Content[0].Text) diskProvider := &mockDiskHealthProvider{} diskProvider.On("GetHosts").Return([]models.Host{ { ID: "host1", Hostname: "node1", RAID: []models.HostRAIDArray{ { Device: "/dev/md0", Level: "raid1", State: "clean", TotalDevices: 2, ActiveDevices: 2, WorkingDevices: 2, Devices: []models.HostRAIDDevice{ {Device: "/dev/sda", State: "active", Slot: 0}, }, }, }, }, }) exec = NewPulseToolExecutor(ExecutorConfig{DiskHealthProvider: diskProvider}) result, err = exec.executeGetHostRAIDStatus(ctx, map[string]interface{}{ "host": "missing", }) require.NoError(t, err) assert.Equal(t, "No RAID arrays found for host 'missing'.", result.Content[0].Text) result, err = exec.executeGetHostRAIDStatus(ctx, map[string]interface{}{}) require.NoError(t, err) var resp HostRAIDStatusResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) require.Len(t, resp.Hosts, 1) assert.Equal(t, "node1", resp.Hosts[0].Hostname) } func TestExecuteGetHostCephDetails(t *testing.T) { ctx := context.Background() exec := NewPulseToolExecutor(ExecutorConfig{}) result, err := exec.executeGetHostCephDetails(ctx, map[string]interface{}{}) require.NoError(t, err) assert.Equal(t, "Disk health provider not available.", result.Content[0].Text) diskProvider := &mockDiskHealthProvider{} diskProvider.On("GetHosts").Return([]models.Host{ { ID: "host1", Hostname: "node1", Ceph: &models.HostCephCluster{ FSID: "fsid1", Health: models.HostCephHealth{ Status: "HEALTH_OK", Checks: map[string]models.HostCephCheck{ "CHECK_1": {Severity: "HEALTH_OK"}, }, Summary: []models.HostCephHealthSummary{ {Severity: "HEALTH_OK", Message: "ok"}, }, }, MonMap: models.HostCephMonitorMap{ NumMons: 1, Monitors: []models.HostCephMonitor{ {Name: "mon1", Rank: 0, Addr: "1.2.3.4", Status: "leader"}, }, }, MgrMap: models.HostCephManagerMap{ Available: true, NumMgrs: 1, ActiveMgr: "mgr1", Standbys: 0, }, OSDMap: models.HostCephOSDMap{NumOSDs: 2, NumUp: 2, NumIn: 2}, PGMap: models.HostCephPGMap{NumPGs: 1, BytesTotal: 10, BytesUsed: 5, BytesAvailable: 5, UsagePercent: 50}, Pools: []models.HostCephPool{ {ID: 1, Name: "rbd", PercentUsed: 10}, }, CollectedAt: time.Now(), }, }, }) exec = NewPulseToolExecutor(ExecutorConfig{DiskHealthProvider: diskProvider}) result, err = exec.executeGetHostCephDetails(ctx, map[string]interface{}{ "host": "missing", }) require.NoError(t, err) assert.Equal(t, "No Ceph data found for host 'missing'.", result.Content[0].Text) result, err = exec.executeGetHostCephDetails(ctx, map[string]interface{}{}) require.NoError(t, err) var resp HostCephDetailsResponse require.NoError(t, json.Unmarshal([]byte(result.Content[0].Text), &resp)) require.Len(t, resp.Hosts, 1) assert.Equal(t, "node1", resp.Hosts[0].Hostname) }