package resources import ( "testing" "time" "github.com/rcourtman/pulse-go-rewrite/internal/models" ) func TestStorePopulateFromSnapshot(t *testing.T) { store := NewStore() // Create a minimal snapshot with test data snapshot := models.StateSnapshot{ Nodes: []models.Node{ { ID: "node-1", Name: "pve-node-1", Instance: "https://pve1.local:8006", Status: "online", CPU: 0.455, // Proxmox API returns 0-1 ratio Memory: models.Memory{ Total: 16000000000, Used: 8000000000, Free: 8000000000, Usage: 50.0, }, Uptime: 86400, LastSeen: time.Now(), }, }, VMs: []models.VM{ { ID: "vm-100", VMID: 100, Name: "test-vm", Node: "pve-node-1", Instance: "https://pve1.local:8006", Status: "running", CPU: 0.25, // Proxmox API returns 0-1 ratio Memory: models.Memory{ Total: 4000000000, Used: 2000000000, Free: 2000000000, Usage: 50.0, }, }, }, Containers: []models.Container{ { ID: "ct-200", VMID: 200, Name: "test-container", Node: "pve-node-1", Instance: "https://pve1.local:8006", Status: "running", }, }, Hosts: []models.Host{ { ID: "host-1", Hostname: "my-host", Status: "online", }, }, } // Populate the store store.PopulateFromSnapshot(snapshot) // Verify resources were created all := store.GetAll() if len(all) == 0 { t.Fatal("Expected resources to be populated, got 0") } t.Logf("Populated %d resources", len(all)) // Check for each type nodes := store.Query().OfType(ResourceTypeNode).Execute() if len(nodes) != 1 { t.Errorf("Expected 1 node, got %d", len(nodes)) } else { t.Logf("Node: id=%s, name=%s, cpu=%.1f%%", nodes[0].ID, nodes[0].Name, nodes[0].CPU.Current) } vms := store.Query().OfType(ResourceTypeVM).Execute() if len(vms) != 1 { t.Errorf("Expected 1 VM, got %d", len(vms)) } else { t.Logf("VM: id=%s, name=%s, status=%s", vms[0].ID, vms[0].Name, vms[0].Status) } containers := store.Query().OfType(ResourceTypeContainer).Execute() if len(containers) != 1 { t.Errorf("Expected 1 container, got %d", len(containers)) } else { t.Logf("Container: id=%s, name=%s", containers[0].ID, containers[0].Name) } hosts := store.Query().OfType(ResourceTypeHost).Execute() if len(hosts) != 1 { t.Errorf("Expected 1 host, got %d", len(hosts)) } else { t.Logf("Host: id=%s, hostname=%s", hosts[0].ID, hosts[0].Identity.Hostname) } // Test summary t.Logf("SUCCESS: PopulateFromSnapshot works correctly!") t.Logf("Total resources: %d (1 node + 1 VM + 1 container + 1 host)", len(all)) } // TestPopulateFromSnapshotRemovesStaleResources verifies that resources not present // in subsequent snapshots are removed from the store (fixing the "ghost LXCs" bug). func TestPopulateFromSnapshotRemovesStaleResources(t *testing.T) { store := NewStore() // First snapshot with 2 containers snapshot1 := models.StateSnapshot{ Containers: []models.Container{ { ID: "ct-100", VMID: 100, Name: "container-to-keep", Node: "pve-node-1", Instance: "pve1", Status: "running", }, { ID: "ct-200", VMID: 200, Name: "container-to-remove", Node: "pve-node-1", Instance: "pve1", Status: "running", }, }, } // Populate with first snapshot store.PopulateFromSnapshot(snapshot1) // Verify both containers exist containers := store.Query().OfType(ResourceTypeContainer).Execute() if len(containers) != 2 { t.Fatalf("Expected 2 containers after first snapshot, got %d", len(containers)) } t.Logf("After first snapshot: %d containers", len(containers)) // Second snapshot with only 1 container (the other was "deleted" from Proxmox) snapshot2 := models.StateSnapshot{ Containers: []models.Container{ { ID: "ct-100", VMID: 100, Name: "container-to-keep", Node: "pve-node-1", Instance: "pve1", Status: "running", }, // container-to-remove is NOT included - simulating it was deleted }, } // Populate with second snapshot store.PopulateFromSnapshot(snapshot2) // Verify only 1 container remains containers = store.Query().OfType(ResourceTypeContainer).Execute() if len(containers) != 1 { t.Fatalf("Expected 1 container after second snapshot (removed container should be gone), got %d", len(containers)) } // Verify the correct container remains if containers[0].ID != "ct-100" { t.Errorf("Expected container-to-keep (ct-100) to remain, got %s", containers[0].ID) } // Verify the removed container is gone _, found := store.Get("ct-200") if found { t.Error("container-to-remove (ct-200) should have been removed from the store") } t.Logf("SUCCESS: Removed resources are correctly cleaned up!") t.Logf("After second snapshot: %d container(s) - 'container-to-remove' was properly removed", len(containers)) }