mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-29 03:50:18 +00:00
Add tests for AI intelligence, Docker/K8s agents, log redaction, and general router helper functions.
234 lines
6.8 KiB
Go
234 lines
6.8 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/monitoring"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/updates"
|
|
"github.com/rcourtman/pulse-go-rewrite/pkg/metrics"
|
|
)
|
|
|
|
func TestGetTenantMonitor_Default(t *testing.T) {
|
|
defaultMonitor, _, _ := newTestMonitor(t)
|
|
router := &Router{monitor: defaultMonitor}
|
|
|
|
if got := router.getTenantMonitor(context.Background()); got != defaultMonitor {
|
|
t.Fatalf("expected default monitor to be returned")
|
|
}
|
|
}
|
|
|
|
func TestGetTenantMonitor_WithTenant(t *testing.T) {
|
|
defaultMonitor, _, _ := newTestMonitor(t)
|
|
tenantMonitor, _, _ := newTestMonitor(t)
|
|
|
|
mtm := &monitoring.MultiTenantMonitor{}
|
|
setUnexportedField(t, mtm, "monitors", map[string]*monitoring.Monitor{
|
|
"tenant-1": tenantMonitor,
|
|
})
|
|
|
|
router := &Router{monitor: defaultMonitor, mtMonitor: mtm}
|
|
ctx := context.WithValue(context.Background(), OrgIDContextKey, "tenant-1")
|
|
|
|
if got := router.getTenantMonitor(ctx); got != tenantMonitor {
|
|
t.Fatalf("expected tenant monitor to be returned")
|
|
}
|
|
}
|
|
|
|
func TestGetTenantMonitor_FallbackOnError(t *testing.T) {
|
|
defaultMonitor, _, _ := newTestMonitor(t)
|
|
mtp := config.NewMultiTenantPersistence(t.TempDir())
|
|
mtm := monitoring.NewMultiTenantMonitor(&config.Config{}, mtp, nil)
|
|
defer mtm.Stop()
|
|
|
|
router := &Router{monitor: defaultMonitor, mtMonitor: mtm}
|
|
ctx := context.WithValue(context.Background(), OrgIDContextKey, "../bad")
|
|
|
|
if got := router.getTenantMonitor(ctx); got != defaultMonitor {
|
|
t.Fatalf("expected fallback to default monitor")
|
|
}
|
|
}
|
|
|
|
func TestHandleVersion_MethodNotAllowed(t *testing.T) {
|
|
router := &Router{updateManager: updates.NewManager(&config.Config{})}
|
|
req := httptest.NewRequest(http.MethodPost, "/api/version", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
router.handleVersion(rec, req)
|
|
|
|
if rec.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func TestHandleVersion_Success(t *testing.T) {
|
|
router := &Router{updateManager: updates.NewManager(&config.Config{})}
|
|
req := httptest.NewRequest(http.MethodGet, "/api/version", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
router.handleVersion(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
|
|
}
|
|
var payload map[string]interface{}
|
|
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if payload["version"] == "" {
|
|
t.Fatalf("expected version in response, got %#v", payload)
|
|
}
|
|
}
|
|
|
|
func TestHandleMetricsHistory_MethodNotAllowed(t *testing.T) {
|
|
router := &Router{}
|
|
req := httptest.NewRequest(http.MethodPost, "/api/metrics/history", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
router.handleMetricsHistory(rec, req)
|
|
|
|
if rec.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func TestHandleMetricsHistory_MissingParams(t *testing.T) {
|
|
monitor, _, _ := newTestMonitor(t)
|
|
router := &Router{monitor: monitor}
|
|
req := httptest.NewRequest(http.MethodGet, "/api/metrics/history", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
router.handleMetricsHistory(rec, req)
|
|
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusBadRequest)
|
|
}
|
|
}
|
|
|
|
func TestHandleMetricsHistory_LicenseRequired(t *testing.T) {
|
|
monitor, _, _ := newTestMonitor(t)
|
|
mtp := config.NewMultiTenantPersistence(t.TempDir())
|
|
if _, err := mtp.GetPersistence("default"); err != nil {
|
|
t.Fatalf("failed to init persistence: %v", err)
|
|
}
|
|
|
|
router := &Router{
|
|
monitor: monitor,
|
|
licenseHandlers: NewLicenseHandlers(mtp),
|
|
}
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/metrics-store/history?resourceType=vm&resourceId=vm-1&range=30d", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
router.handleMetricsHistory(rec, req)
|
|
|
|
if rec.Code != http.StatusPaymentRequired {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusPaymentRequired)
|
|
}
|
|
}
|
|
|
|
func TestHandleMetricsHistory_UsesStore(t *testing.T) {
|
|
monitor, _, _ := newTestMonitor(t)
|
|
store, err := metrics.NewStore(metrics.DefaultConfig(t.TempDir()))
|
|
if err != nil {
|
|
t.Fatalf("metrics.NewStore error: %v", err)
|
|
}
|
|
defer store.Close()
|
|
|
|
store.WriteBatchSync([]metrics.WriteMetric{{
|
|
ResourceType: "vm",
|
|
ResourceID: "vm-1",
|
|
MetricType: "cpu",
|
|
Value: 42.0,
|
|
Timestamp: time.Now(),
|
|
Tier: metrics.TierRaw,
|
|
}})
|
|
|
|
setUnexportedField(t, monitor, "metricsStore", store)
|
|
router := &Router{monitor: monitor}
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/metrics-store/history?resourceType=vm&resourceId=vm-1&metric=cpu&range=1h", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
router.handleMetricsHistory(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
|
|
}
|
|
var payload map[string]interface{}
|
|
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if payload["source"] != "store" {
|
|
t.Fatalf("expected source store, got %#v", payload["source"])
|
|
}
|
|
}
|
|
|
|
func TestHandleMetricsHistory_UsesStoreAllMetrics(t *testing.T) {
|
|
monitor, _, _ := newTestMonitor(t)
|
|
store, err := metrics.NewStore(metrics.DefaultConfig(t.TempDir()))
|
|
if err != nil {
|
|
t.Fatalf("metrics.NewStore error: %v", err)
|
|
}
|
|
defer store.Close()
|
|
|
|
now := time.Now()
|
|
store.WriteBatchSync([]metrics.WriteMetric{
|
|
{
|
|
ResourceType: "vm",
|
|
ResourceID: "vm-1",
|
|
MetricType: "cpu",
|
|
Value: 50.0,
|
|
Timestamp: now,
|
|
Tier: metrics.TierRaw,
|
|
},
|
|
{
|
|
ResourceType: "vm",
|
|
ResourceID: "vm-1",
|
|
MetricType: "memory",
|
|
Value: 70.0,
|
|
Timestamp: now,
|
|
Tier: metrics.TierRaw,
|
|
},
|
|
})
|
|
|
|
setUnexportedField(t, monitor, "metricsStore", store)
|
|
router := &Router{monitor: monitor}
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/metrics-store/history?resourceType=vm&resourceId=vm-1&range=1h", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
router.handleMetricsHistory(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
|
|
}
|
|
var payload map[string]interface{}
|
|
if err := json.NewDecoder(rec.Body).Decode(&payload); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if payload["source"] != "store" {
|
|
t.Fatalf("expected source store, got %#v", payload["source"])
|
|
}
|
|
metricsMap, ok := payload["metrics"].(map[string]interface{})
|
|
if !ok || metricsMap["cpu"] == nil {
|
|
t.Fatalf("expected cpu metrics in response, got %#v", payload["metrics"])
|
|
}
|
|
}
|
|
|
|
func TestHandleCharts_MethodNotAllowed(t *testing.T) {
|
|
router := &Router{}
|
|
req := httptest.NewRequest(http.MethodPost, "/api/charts", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
router.handleCharts(rec, req)
|
|
|
|
if rec.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusMethodNotAllowed)
|
|
}
|
|
}
|