Files
Pulse/internal/api/resources_v2_test.go
2026-02-05 17:57:58 +00:00

194 lines
5.9 KiB
Go

package api
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/config"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
unified "github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
)
type resourceV2StateProvider struct {
snapshot models.StateSnapshot
}
func (s resourceV2StateProvider) GetState() models.StateSnapshot {
return s.snapshot
}
func TestResourceV2ListMergesLinkedHost(t *testing.T) {
now := time.Now().UTC()
node := models.Node{
ID: "instance-pve1",
Name: "pve1",
Instance: "instance",
Host: "https://pve1:8006",
Status: "online",
CPU: 0.15,
Memory: models.Memory{Total: 1024, Used: 512, Free: 512, Usage: 0.5},
Disk: models.Disk{Total: 2048, Used: 1024, Free: 1024, Usage: 0.5},
LastSeen: now,
LinkedHostAgentID: "host-1",
}
host := models.Host{
ID: "host-1",
Hostname: "pve1",
Status: "online",
Memory: models.Memory{Total: 2048, Used: 1024, Free: 1024, Usage: 0.5},
LastSeen: now,
LinkedNodeID: node.ID,
}
snapshot := models.StateSnapshot{
Nodes: []models.Node{node},
Hosts: []models.Host{host},
}
cfg := &config.Config{DataPath: t.TempDir()}
h := NewResourceV2Handlers(cfg)
h.SetStateProvider(resourceV2StateProvider{snapshot: snapshot})
rec := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/api/v2/resources?type=host", nil)
h.HandleListResources(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, body=%s", rec.Code, rec.Body.String())
}
var resp ResourcesV2Response
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
t.Fatalf("decode response: %v", err)
}
if len(resp.Data) != 1 {
t.Fatalf("expected 1 resource, got %d", len(resp.Data))
}
resource := resp.Data[0]
if !containsSource(resource.Sources, unified.SourceProxmox) || !containsSource(resource.Sources, unified.SourceAgent) {
t.Fatalf("expected merged sources, got %+v", resource.Sources)
}
}
func TestResourceV2GetResource(t *testing.T) {
now := time.Now().UTC()
host := models.Host{
ID: "host-1",
Hostname: "pve1",
Status: "online",
Memory: models.Memory{Total: 2048, Used: 1024, Free: 1024, Usage: 0.5},
LastSeen: now,
}
snapshot := models.StateSnapshot{Hosts: []models.Host{host}}
cfg := &config.Config{DataPath: t.TempDir()}
h := NewResourceV2Handlers(cfg)
h.SetStateProvider(resourceV2StateProvider{snapshot: snapshot})
listRec := httptest.NewRecorder()
listReq := httptest.NewRequest(http.MethodGet, "/api/v2/resources?type=host", nil)
h.HandleListResources(listRec, listReq)
var listResp ResourcesV2Response
if err := json.NewDecoder(listRec.Body).Decode(&listResp); err != nil {
t.Fatalf("decode list response: %v", err)
}
if len(listResp.Data) != 1 {
t.Fatalf("expected 1 resource, got %d", len(listResp.Data))
}
resourceID := listResp.Data[0].ID
getRec := httptest.NewRecorder()
getReq := httptest.NewRequest(http.MethodGet, "/api/v2/resources/"+resourceID, nil)
h.HandleGetResource(getRec, getReq)
if getRec.Code != http.StatusOK {
t.Fatalf("status = %d, body=%s", getRec.Code, getRec.Body.String())
}
var resource unified.Resource
if err := json.NewDecoder(getRec.Body).Decode(&resource); err != nil {
t.Fatalf("decode resource: %v", err)
}
if resource.ID != resourceID {
t.Fatalf("resource id = %q, want %q", resource.ID, resourceID)
}
}
func containsSource(sources []unified.DataSource, target unified.DataSource) bool {
for _, source := range sources {
if source == target {
return true
}
}
return false
}
func TestResourceV2LinkMergesResources(t *testing.T) {
now := time.Now().UTC()
host := models.Host{
ID: "host-1",
Hostname: "alpha",
Status: "online",
Memory: models.Memory{Total: 2048, Used: 1024, Free: 1024, Usage: 0.5},
LastSeen: now,
}
dockerHost := models.DockerHost{
ID: "docker-1",
Hostname: "beta",
Status: "online",
CPUs: 4,
Memory: models.Memory{Total: 4096, Used: 1024, Free: 3072, Usage: 0.25},
LastSeen: now,
}
snapshot := models.StateSnapshot{Hosts: []models.Host{host}, DockerHosts: []models.DockerHost{dockerHost}}
cfg := &config.Config{DataPath: t.TempDir()}
h := NewResourceV2Handlers(cfg)
h.SetStateProvider(resourceV2StateProvider{snapshot: snapshot})
listRec := httptest.NewRecorder()
listReq := httptest.NewRequest(http.MethodGet, "/api/v2/resources?type=host", nil)
h.HandleListResources(listRec, listReq)
var listResp ResourcesV2Response
if err := json.NewDecoder(listRec.Body).Decode(&listResp); err != nil {
t.Fatalf("decode list response: %v", err)
}
if len(listResp.Data) != 2 {
t.Fatalf("expected 2 resources before link, got %d", len(listResp.Data))
}
primaryID := listResp.Data[0].ID
secondaryID := listResp.Data[1].ID
linkPayload := map[string]string{"targetId": secondaryID, "reason": "manual merge"}
payloadBytes, _ := json.Marshal(linkPayload)
linkRec := httptest.NewRecorder()
linkReq := httptest.NewRequest(http.MethodPost, "/api/v2/resources/"+primaryID+"/link", bytes.NewReader(payloadBytes))
h.HandleLink(linkRec, linkReq)
if linkRec.Code != http.StatusOK {
t.Fatalf("link status = %d, body=%s", linkRec.Code, linkRec.Body.String())
}
listRec2 := httptest.NewRecorder()
listReq2 := httptest.NewRequest(http.MethodGet, "/api/v2/resources?type=host", nil)
h.HandleListResources(listRec2, listReq2)
var listResp2 ResourcesV2Response
if err := json.NewDecoder(listRec2.Body).Decode(&listResp2); err != nil {
t.Fatalf("decode list response: %v", err)
}
if len(listResp2.Data) != 1 {
t.Fatalf("expected 1 resource after link, got %d", len(listResp2.Data))
}
resource := listResp2.Data[0]
if !containsSource(resource.Sources, unified.SourceAgent) || !containsSource(resource.Sources, unified.SourceDocker) {
t.Fatalf("expected merged sources, got %+v", resource.Sources)
}
}