Pulse/pkg/proxmox/cluster_client_test.go

112 lines
3.4 KiB
Go

package proxmox
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"sync/atomic"
"testing"
"time"
)
func TestClusterClientHandlesRateLimitWithoutMarkingUnhealthy(t *testing.T) {
var requestCount int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api2/json/nodes":
current := atomic.AddInt32(&requestCount, 1)
if current == 1 {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusTooManyRequests)
fmt.Fprint(w, `{"error":"rate limited"}`)
return
}
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `{"data":[{"node":"node1","status":"online","cpu":0,"maxcpu":1,"mem":0,"maxmem":1,"disk":0,"maxdisk":1,"uptime":1,"level":"normal"}]}`)
default:
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `{"data":{}}`)
}
}))
defer server.Close()
cfg := ClientConfig{
Host: server.URL,
TokenName: "pulse@pve!token",
TokenValue: "sometokenvalue",
VerifySSL: false,
Timeout: 2 * time.Second,
}
cc := NewClusterClient("test-cluster", cfg, []string{server.URL})
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
nodes, err := cc.GetNodes(ctx)
if err != nil {
t.Fatalf("expected GetNodes to succeed after retry, got error: %v", err)
}
if len(nodes) != 1 {
t.Fatalf("expected 1 node after retry, got %d", len(nodes))
}
health := cc.GetHealthStatus()
if healthy, ok := health[server.URL]; !ok || !healthy {
t.Fatalf("expected endpoint %s to remain healthy, got health map: %+v", server.URL, health)
}
if atomic.LoadInt32(&requestCount) < 2 {
t.Fatalf("expected at least 2 requests to backend, got %d", requestCount)
}
}
func TestClusterClientIgnoresGuestAgentTimeoutForHealth(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api2/json/nodes":
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `{"data":[{"node":"test","status":"online","cpu":0,"maxcpu":1,"mem":0,"maxmem":1,"disk":0,"maxdisk":1,"uptime":1,"level":"normal"}]}`)
case "/api2/json/nodes/test/qemu/100/agent/get-fsinfo":
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, `{"data":null,"message":"VM 100 qmp command 'guest-get-fsinfo' failed - got timeout\n"}`)
default:
w.WriteHeader(http.StatusNotFound)
}
}))
defer server.Close()
cfg := ClientConfig{
Host: server.URL,
TokenName: "pulse@pve!token",
TokenValue: "sometokenvalue",
VerifySSL: false,
Timeout: 2 * time.Second,
}
cc := NewClusterClient("test-cluster", cfg, []string{server.URL})
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_, err := cc.GetVMFSInfo(ctx, "test", 100)
if err == nil {
t.Fatalf("expected VM guest agent timeout error, got nil")
}
health := cc.GetHealthStatusWithErrors()
endpointHealth, ok := health[server.URL]
if !ok {
t.Fatalf("expected health entry for endpoint %s", server.URL)
}
if !endpointHealth.Healthy {
t.Fatalf("expected endpoint to remain healthy after VM-specific guest agent error, got %+v", endpointHealth)
}
if endpointHealth.LastError != "" {
t.Fatalf("expected last error to remain empty for VM-specific failures, got %q", endpointHealth.LastError)
}
}