mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 19:41:17 +00:00
332 lines
10 KiB
Go
332 lines
10 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/resources"
|
|
)
|
|
|
|
type stubResourceStateProvider struct {
|
|
snapshot models.StateSnapshot
|
|
}
|
|
|
|
func (s stubResourceStateProvider) GetState() models.StateSnapshot {
|
|
return s.snapshot
|
|
}
|
|
|
|
type stubTenantStateProvider struct {
|
|
snapshot models.StateSnapshot
|
|
}
|
|
|
|
func (s stubTenantStateProvider) GetStateForTenant(_ string) models.StateSnapshot {
|
|
return s.snapshot
|
|
}
|
|
|
|
func decodeResourcesResponse(t *testing.T, rec *httptest.ResponseRecorder) ResourcesResponse {
|
|
t.Helper()
|
|
var resp ResourcesResponse
|
|
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
return resp
|
|
}
|
|
|
|
func TestResourceHandlers_GetStoreStats(t *testing.T) {
|
|
handlers := NewResourceHandlers()
|
|
handlers.Store().Upsert(resources.Resource{ID: "vm-1", Type: resources.ResourceTypeVM})
|
|
handlers.getStoreForTenant("tenant-a").Upsert(resources.Resource{ID: "node-1", Type: resources.ResourceTypeNode})
|
|
|
|
stats := handlers.GetStoreStats()
|
|
if stats["default"].TotalResources != 1 {
|
|
t.Fatalf("expected default total 1, got %d", stats["default"].TotalResources)
|
|
}
|
|
if stats["tenant-a"].TotalResources != 1 {
|
|
t.Fatalf("expected tenant total 1, got %d", stats["tenant-a"].TotalResources)
|
|
}
|
|
}
|
|
|
|
func TestHandleGetResources_FiltersAndModes(t *testing.T) {
|
|
handlers := NewResourceHandlers()
|
|
handlers.Store().Upsert(resources.Resource{
|
|
ID: "vm-1",
|
|
Type: resources.ResourceTypeVM,
|
|
Status: resources.StatusRunning,
|
|
PlatformType: resources.PlatformProxmoxPVE,
|
|
ParentID: "node-1",
|
|
Alerts: []resources.ResourceAlert{{ID: "alert-1"}},
|
|
})
|
|
handlers.Store().Upsert(resources.Resource{
|
|
ID: "node-1",
|
|
Type: resources.ResourceTypeNode,
|
|
Status: resources.StatusOnline,
|
|
PlatformType: resources.PlatformProxmoxPVE,
|
|
})
|
|
handlers.Store().Upsert(resources.Resource{
|
|
ID: "ct-1",
|
|
Type: resources.ResourceTypeContainer,
|
|
Status: resources.StatusStopped,
|
|
PlatformType: resources.PlatformProxmoxPVE,
|
|
})
|
|
handlers.Store().Upsert(resources.Resource{
|
|
ID: "dh-1",
|
|
Type: resources.ResourceTypeDockerHost,
|
|
Status: resources.StatusOnline,
|
|
PlatformType: resources.PlatformDocker,
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/resources", nil)
|
|
rec := httptest.NewRecorder()
|
|
handlers.HandleGetResources(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
|
|
}
|
|
resp := decodeResourcesResponse(t, rec)
|
|
if resp.Count != 4 {
|
|
t.Fatalf("expected count 4, got %d", resp.Count)
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodGet, "/api/resources?type=vm,container", nil)
|
|
rec = httptest.NewRecorder()
|
|
handlers.HandleGetResources(rec, req)
|
|
resp = decodeResourcesResponse(t, rec)
|
|
if resp.Count != 2 {
|
|
t.Fatalf("expected type count 2, got %d", resp.Count)
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodGet, "/api/resources?platform=docker", nil)
|
|
rec = httptest.NewRecorder()
|
|
handlers.HandleGetResources(rec, req)
|
|
resp = decodeResourcesResponse(t, rec)
|
|
if resp.Count != 1 || resp.Resources[0].ID != "dh-1" {
|
|
t.Fatalf("expected docker host, got %#v", resp.Resources)
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodGet, "/api/resources?status=running", nil)
|
|
rec = httptest.NewRecorder()
|
|
handlers.HandleGetResources(rec, req)
|
|
resp = decodeResourcesResponse(t, rec)
|
|
if resp.Count != 1 || resp.Resources[0].ID != "vm-1" {
|
|
t.Fatalf("expected running vm, got %#v", resp.Resources)
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodGet, "/api/resources?parent=node-1", nil)
|
|
rec = httptest.NewRecorder()
|
|
handlers.HandleGetResources(rec, req)
|
|
resp = decodeResourcesResponse(t, rec)
|
|
if resp.Count != 1 || resp.Resources[0].ID != "vm-1" {
|
|
t.Fatalf("expected parent filter vm, got %#v", resp.Resources)
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodGet, "/api/resources?alerts=true", nil)
|
|
rec = httptest.NewRecorder()
|
|
handlers.HandleGetResources(rec, req)
|
|
resp = decodeResourcesResponse(t, rec)
|
|
if resp.Count != 1 || resp.Resources[0].ID != "vm-1" {
|
|
t.Fatalf("expected alerts filter vm, got %#v", resp.Resources)
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodGet, "/api/resources?infrastructure=true", nil)
|
|
rec = httptest.NewRecorder()
|
|
handlers.HandleGetResources(rec, req)
|
|
resp = decodeResourcesResponse(t, rec)
|
|
if resp.Count != 2 {
|
|
t.Fatalf("expected infrastructure count 2, got %d", resp.Count)
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodGet, "/api/resources?workloads=true", nil)
|
|
rec = httptest.NewRecorder()
|
|
handlers.HandleGetResources(rec, req)
|
|
resp = decodeResourcesResponse(t, rec)
|
|
if resp.Count != 2 {
|
|
t.Fatalf("expected workloads count 2, got %d", resp.Count)
|
|
}
|
|
}
|
|
|
|
func TestHandleGetResources_MethodNotAllowed(t *testing.T) {
|
|
handlers := NewResourceHandlers()
|
|
req := httptest.NewRequest(http.MethodPost, "/api/resources", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handlers.HandleGetResources(rec, req)
|
|
|
|
if rec.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestHandleGetResource_MethodNotAllowedAndMissingID(t *testing.T) {
|
|
handlers := NewResourceHandlers()
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/resources/vm-1", nil)
|
|
rec := httptest.NewRecorder()
|
|
handlers.HandleGetResource(rec, req)
|
|
if rec.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
|
|
}
|
|
|
|
req = httptest.NewRequest(http.MethodGet, "/api/resources/", nil)
|
|
rec = httptest.NewRecorder()
|
|
handlers.HandleGetResource(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestHandleGetResourceStats_MethodNotAllowed(t *testing.T) {
|
|
handlers := NewResourceHandlers()
|
|
req := httptest.NewRequest(http.MethodPost, "/api/resources/stats", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handlers.HandleGetResourceStats(rec, req)
|
|
|
|
if rec.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("expected status %d, got %d", http.StatusMethodNotAllowed, rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestHandleGetResources_StateProviderAndTenantProvider(t *testing.T) {
|
|
handlers := NewResourceHandlers()
|
|
|
|
snapshot := models.StateSnapshot{
|
|
Nodes: []models.Node{{ID: "node-1", Name: "node-1"}},
|
|
}
|
|
handlers.SetStateProvider(stubResourceStateProvider{snapshot: snapshot})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/resources", nil)
|
|
rec := httptest.NewRecorder()
|
|
handlers.HandleGetResources(rec, req)
|
|
resp := decodeResourcesResponse(t, rec)
|
|
if resp.Count != 1 {
|
|
t.Fatalf("expected count 1 from state provider, got %d", resp.Count)
|
|
}
|
|
|
|
tenantSnapshot := models.StateSnapshot{
|
|
Nodes: []models.Node{{ID: "node-tenant", Name: "node-tenant"}},
|
|
}
|
|
handlers.SetTenantStateProvider(stubTenantStateProvider{snapshot: tenantSnapshot})
|
|
req = httptest.NewRequest(http.MethodGet, "/api/resources", nil)
|
|
ctx := context.WithValue(req.Context(), OrgIDContextKey, "tenant-1")
|
|
req = req.WithContext(ctx)
|
|
rec = httptest.NewRecorder()
|
|
handlers.HandleGetResources(rec, req)
|
|
resp = decodeResourcesResponse(t, rec)
|
|
if resp.Count != 1 || resp.Resources[0].ID != "node-tenant" {
|
|
t.Fatalf("expected tenant resource, got %#v", resp.Resources)
|
|
}
|
|
}
|
|
|
|
func TestPopulateFromSnapshotForTenant(t *testing.T) {
|
|
handlers := NewResourceHandlers()
|
|
|
|
now := time.Now()
|
|
snapshot := models.StateSnapshot{
|
|
Nodes: []models.Node{{
|
|
ID: "node-1",
|
|
Name: "node-1",
|
|
CPU: 0.2,
|
|
Memory: models.Memory{Total: 100, Used: 50, Free: 50, Usage: 50},
|
|
Disk: models.Disk{Total: 100, Used: 10, Free: 90, Usage: 10},
|
|
}},
|
|
VMs: []models.VM{{
|
|
ID: "vm-1",
|
|
Name: "vm-1",
|
|
Status: "running",
|
|
Memory: models.Memory{Total: 100, Used: 10, Free: 90, Usage: 10},
|
|
Disk: models.Disk{Total: 100, Used: 10, Free: 90, Usage: 10},
|
|
}},
|
|
Containers: []models.Container{{
|
|
ID: "ct-1",
|
|
Name: "ct-1",
|
|
Status: "running",
|
|
Memory: models.Memory{Total: 100, Used: 10, Free: 90, Usage: 10},
|
|
Disk: models.Disk{Total: 100, Used: 10, Free: 90, Usage: 10},
|
|
}},
|
|
Hosts: []models.Host{{
|
|
ID: "host-1",
|
|
Hostname: "host-1",
|
|
Status: "online",
|
|
Memory: models.Memory{Total: 100, Used: 20, Free: 80, Usage: 20},
|
|
}},
|
|
DockerHosts: []models.DockerHost{{
|
|
ID: "dh-1",
|
|
Hostname: "dh-1",
|
|
Status: "online",
|
|
CPUUsage: 5,
|
|
Memory: models.Memory{Total: 100, Used: 20, Free: 80, Usage: 20},
|
|
TotalMemoryBytes: 100,
|
|
LastSeen: now,
|
|
IntervalSeconds: 60,
|
|
Containers: []models.DockerContainer{{
|
|
ID: "dc-1",
|
|
Name: "dc-1",
|
|
State: "running",
|
|
Status: "running",
|
|
CPUPercent: 1,
|
|
MemoryLimit: 100,
|
|
MemoryUsage: 20,
|
|
MemoryPercent: 20,
|
|
UptimeSeconds: 10,
|
|
CreatedAt: now,
|
|
}},
|
|
}},
|
|
PBSInstances: []models.PBSInstance{{
|
|
ID: "pbs-1",
|
|
Name: "pbs-1",
|
|
Host: "pbs-host",
|
|
Status: "online",
|
|
CPU: 2,
|
|
Memory: 10,
|
|
MemoryUsed: 10,
|
|
MemoryTotal: 100,
|
|
Uptime: 10,
|
|
ConnectionHealth: "ok",
|
|
LastSeen: now,
|
|
}},
|
|
Storage: []models.Storage{{
|
|
ID: "storage-1",
|
|
Name: "storage-1",
|
|
Instance: "inst-1",
|
|
Node: "node-1",
|
|
Status: "online",
|
|
Total: 100,
|
|
Used: 50,
|
|
Free: 50,
|
|
Usage: 50,
|
|
Content: "images",
|
|
Enabled: true,
|
|
Active: true,
|
|
}},
|
|
}
|
|
|
|
handlers.PopulateFromSnapshotForTenant("tenant-1", snapshot)
|
|
|
|
store := handlers.getStoreForTenant("tenant-1")
|
|
stats := store.GetStats()
|
|
if stats.TotalResources != 8 {
|
|
t.Fatalf("expected 8 resources, got %d", stats.TotalResources)
|
|
}
|
|
}
|
|
|
|
func TestParsePlatformTypesAndStatuses(t *testing.T) {
|
|
platforms := parsePlatformTypes(" docker , proxmox-pve , ")
|
|
if len(platforms) != 2 {
|
|
t.Fatalf("expected 2 platforms, got %d", len(platforms))
|
|
}
|
|
if platforms[0] != resources.PlatformDocker || platforms[1] != resources.PlatformProxmoxPVE {
|
|
t.Fatalf("unexpected platforms: %#v", platforms)
|
|
}
|
|
|
|
statuses := parseStatuses("running, stopped ,")
|
|
if len(statuses) != 2 {
|
|
t.Fatalf("expected 2 statuses, got %d", len(statuses))
|
|
}
|
|
if statuses[0] != resources.StatusRunning || statuses[1] != resources.StatusStopped {
|
|
t.Fatalf("unexpected statuses: %#v", statuses)
|
|
}
|
|
}
|