Files
hanzoai_agent/control-plane/internal/server/server_routes_test.go
zeekay 9556032ab1 feat: add cloud instance provisioning with billing integration
Multi-platform cloud bot provisioning (Linux K8s pods, Windows/macOS
EC2), billing authorization via bootnode API, usage metering, and
real-time monitoring. Includes REST + UI APIs, SSE events, dedicated
host pooling for macOS, and per-tier compute quotas.
2026-02-13 15:33:35 -08:00

625 lines
24 KiB
Go

package server
import (
"context"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/hanzoai/agents/control-plane/internal/config"
"github.com/hanzoai/agents/control-plane/internal/events"
"github.com/hanzoai/agents/control-plane/internal/services"
"github.com/hanzoai/agents/control-plane/internal/storage"
"github.com/hanzoai/agents/control-plane/pkg/types"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
)
// stubStorage implements storage.StorageProvider with minimal functionality for testing
type stubStorage struct {
eventBus *events.ExecutionEventBus
}
func newStubStorage() *stubStorage {
return &stubStorage{
eventBus: events.NewExecutionEventBus(),
}
}
// Required methods for ExecuteHandler
func (s *stubStorage) GetAgent(ctx context.Context, id string) (*types.AgentNode, error) {
return nil, nil
}
func (s *stubStorage) CreateExecutionRecord(ctx context.Context, execution *types.Execution) error {
return nil
}
func (s *stubStorage) GetExecutionRecord(ctx context.Context, executionID string) (*types.Execution, error) {
return nil, nil
}
func (s *stubStorage) UpdateExecutionRecord(ctx context.Context, executionID string, update func(*types.Execution) (*types.Execution, error)) (*types.Execution, error) {
return nil, nil
}
func (s *stubStorage) QueryExecutionRecords(ctx context.Context, filter types.ExecutionFilter) ([]*types.Execution, error) {
return nil, nil
}
func (s *stubStorage) RegisterExecutionWebhook(ctx context.Context, webhook *types.ExecutionWebhook) error {
return nil
}
func (s *stubStorage) StoreWorkflowExecution(ctx context.Context, execution *types.WorkflowExecution) error {
return nil
}
func (s *stubStorage) UpdateWorkflowExecution(ctx context.Context, executionID string, updateFunc func(*types.WorkflowExecution) (*types.WorkflowExecution, error)) error {
return nil
}
func (s *stubStorage) GetWorkflowExecution(ctx context.Context, executionID string) (*types.WorkflowExecution, error) {
return nil, nil
}
func (s *stubStorage) GetExecutionEventBus() *events.ExecutionEventBus {
return s.eventBus
}
// Stub implementations for remaining StorageProvider methods
func (s *stubStorage) Initialize(ctx context.Context, config storage.StorageConfig) error { return nil }
func (s *stubStorage) Close(ctx context.Context) error { return nil }
func (s *stubStorage) HealthCheck(ctx context.Context) error { return nil }
func (s *stubStorage) StoreExecution(ctx context.Context, execution *types.AgentExecution) error {
return nil
}
func (s *stubStorage) GetExecution(ctx context.Context, id int64) (*types.AgentExecution, error) {
return nil, nil
}
func (s *stubStorage) QueryExecutions(ctx context.Context, filters types.ExecutionFilters) ([]*types.AgentExecution, error) {
return nil, nil
}
func (s *stubStorage) QueryWorkflowExecutions(ctx context.Context, filters types.WorkflowExecutionFilters) ([]*types.WorkflowExecution, error) {
return nil, nil
}
func (s *stubStorage) QueryRunSummaries(ctx context.Context, filter types.ExecutionFilter) ([]*storage.RunSummaryAggregation, int, error) {
return nil, 0, nil
}
func (s *stubStorage) GetExecutionWebhook(ctx context.Context, executionID string) (*types.ExecutionWebhook, error) {
return nil, nil
}
func (s *stubStorage) ListDueExecutionWebhooks(ctx context.Context, limit int) ([]*types.ExecutionWebhook, error) {
return nil, nil
}
func (s *stubStorage) TryMarkExecutionWebhookInFlight(ctx context.Context, executionID string, now time.Time) (bool, error) {
return false, nil
}
func (s *stubStorage) UpdateExecutionWebhookState(ctx context.Context, executionID string, update types.ExecutionWebhookStateUpdate) error {
return nil
}
func (s *stubStorage) HasExecutionWebhook(ctx context.Context, executionID string) (bool, error) {
return false, nil
}
func (s *stubStorage) ListExecutionWebhooksRegistered(ctx context.Context, executionIDs []string) (map[string]bool, error) {
return nil, nil
}
func (s *stubStorage) StoreExecutionWebhookEvent(ctx context.Context, event *types.ExecutionWebhookEvent) error {
return nil
}
func (s *stubStorage) ListExecutionWebhookEvents(ctx context.Context, executionID string) ([]*types.ExecutionWebhookEvent, error) {
return nil, nil
}
func (s *stubStorage) ListExecutionWebhookEventsBatch(ctx context.Context, executionIDs []string) (map[string][]*types.ExecutionWebhookEvent, error) {
return nil, nil
}
func (s *stubStorage) StoreWorkflowExecutionEvent(ctx context.Context, event *types.WorkflowExecutionEvent) error {
return nil
}
func (s *stubStorage) ListWorkflowExecutionEvents(ctx context.Context, executionID string, afterSeq *int64, limit int) ([]*types.WorkflowExecutionEvent, error) {
return nil, nil
}
func (s *stubStorage) CleanupOldExecutions(ctx context.Context, retentionPeriod time.Duration, batchSize int) (int, error) {
return 0, nil
}
func (s *stubStorage) MarkStaleExecutions(ctx context.Context, staleAfter time.Duration, limit int) (int, error) {
return 0, nil
}
func (s *stubStorage) CleanupWorkflow(ctx context.Context, workflowID string, dryRun bool) (*types.WorkflowCleanupResult, error) {
return &types.WorkflowCleanupResult{
Success: true,
WorkflowID: workflowID,
DryRun: dryRun,
DeletedRecords: map[string]int{"total": 0},
}, nil
}
func (s *stubStorage) QueryWorkflowDAG(ctx context.Context, rootWorkflowID string) ([]*types.WorkflowExecution, error) {
return nil, nil
}
func (s *stubStorage) CreateOrUpdateWorkflow(ctx context.Context, workflow *types.Workflow) error {
return nil
}
func (s *stubStorage) GetWorkflow(ctx context.Context, workflowID string) (*types.Workflow, error) {
return nil, nil
}
func (s *stubStorage) QueryWorkflows(ctx context.Context, filters types.WorkflowFilters) ([]*types.Workflow, error) {
return nil, nil
}
func (s *stubStorage) CreateOrUpdateSession(ctx context.Context, session *types.Session) error {
return nil
}
func (s *stubStorage) GetSession(ctx context.Context, sessionID string) (*types.Session, error) {
return nil, nil
}
func (s *stubStorage) QuerySessions(ctx context.Context, filters types.SessionFilters) ([]*types.Session, error) {
return nil, nil
}
// Memory operations
func (s *stubStorage) SetMemory(ctx context.Context, memory *types.Memory) error { return nil }
func (s *stubStorage) GetMemory(ctx context.Context, scope, scopeID, key string) (*types.Memory, error) {
return nil, nil
}
func (s *stubStorage) DeleteMemory(ctx context.Context, scope, scopeID, key string) error { return nil }
func (s *stubStorage) ListMemory(ctx context.Context, scope, scopeID string) ([]*types.Memory, error) {
return nil, nil
}
func (s *stubStorage) SetVector(ctx context.Context, record *types.VectorRecord) error { return nil }
func (s *stubStorage) GetVector(ctx context.Context, scope, scopeID, key string) (*types.VectorRecord, error) {
return nil, nil
}
func (s *stubStorage) DeleteVector(ctx context.Context, scope, scopeID, key string) error { return nil }
func (s *stubStorage) DeleteVectorsByPrefix(ctx context.Context, scope, scopeID, prefix string) (int, error) {
return 0, nil
}
func (s *stubStorage) SimilaritySearch(ctx context.Context, scope, scopeID string, queryEmbedding []float32, topK int, filters map[string]interface{}) ([]*types.VectorSearchResult, error) {
return nil, nil
}
// Event operations
func (s *stubStorage) StoreEvent(ctx context.Context, event *types.MemoryChangeEvent) error {
return nil
}
func (s *stubStorage) GetEventHistory(ctx context.Context, filter types.EventFilter) ([]*types.MemoryChangeEvent, error) {
return nil, nil
}
// Distributed Lock operations
func (s *stubStorage) AcquireLock(ctx context.Context, key string, timeout time.Duration) (*types.DistributedLock, error) {
return nil, nil
}
func (s *stubStorage) ReleaseLock(ctx context.Context, lockID string) error { return nil }
func (s *stubStorage) RenewLock(ctx context.Context, lockID string) (*types.DistributedLock, error) {
return nil, nil
}
func (s *stubStorage) GetLockStatus(ctx context.Context, key string) (*types.DistributedLock, error) {
return nil, nil
}
// Agent registry
func (s *stubStorage) RegisterAgent(ctx context.Context, agent *types.AgentNode) error { return nil }
func (s *stubStorage) ListAgents(ctx context.Context, filters types.AgentFilters) ([]*types.AgentNode, error) {
return nil, nil
}
func (s *stubStorage) UpdateAgentHealth(ctx context.Context, id string, status types.HealthStatus) error {
return nil
}
func (s *stubStorage) UpdateAgentHealthAtomic(ctx context.Context, id string, status types.HealthStatus, expectedLastHeartbeat *time.Time) error {
return nil
}
func (s *stubStorage) UpdateAgentHeartbeat(ctx context.Context, id string, heartbeatTime time.Time) error {
return nil
}
func (s *stubStorage) UpdateAgentLifecycleStatus(ctx context.Context, id string, status types.AgentLifecycleStatus) error {
return nil
}
// Configuration
func (s *stubStorage) SetConfig(ctx context.Context, key string, value interface{}) error { return nil }
func (s *stubStorage) GetConfig(ctx context.Context, key string) (interface{}, error) {
return nil, nil
}
// Reasoner Performance and History
func (s *stubStorage) GetReasonerPerformanceMetrics(ctx context.Context, reasonerID string) (*types.ReasonerPerformanceMetrics, error) {
return nil, nil
}
func (s *stubStorage) GetReasonerExecutionHistory(ctx context.Context, reasonerID string, page, limit int) (*types.ReasonerExecutionHistory, error) {
return nil, nil
}
// Agent Configuration Management
func (s *stubStorage) StoreAgentConfiguration(ctx context.Context, config *types.AgentConfiguration) error {
return nil
}
func (s *stubStorage) GetAgentConfiguration(ctx context.Context, agentID, packageID string) (*types.AgentConfiguration, error) {
return nil, nil
}
func (s *stubStorage) QueryAgentConfigurations(ctx context.Context, filters types.ConfigurationFilters) ([]*types.AgentConfiguration, error) {
return nil, nil
}
func (s *stubStorage) UpdateAgentConfiguration(ctx context.Context, config *types.AgentConfiguration) error {
return nil
}
func (s *stubStorage) DeleteAgentConfiguration(ctx context.Context, agentID, packageID string) error {
return nil
}
func (s *stubStorage) ValidateAgentConfiguration(ctx context.Context, agentID, packageID string, config map[string]interface{}) (*types.ConfigurationValidationResult, error) {
return nil, nil
}
// Agent Package Management
func (s *stubStorage) StoreAgentPackage(ctx context.Context, pkg *types.AgentPackage) error {
return nil
}
func (s *stubStorage) GetAgentPackage(ctx context.Context, packageID string) (*types.AgentPackage, error) {
return nil, nil
}
func (s *stubStorage) QueryAgentPackages(ctx context.Context, filters types.PackageFilters) ([]*types.AgentPackage, error) {
return nil, nil
}
func (s *stubStorage) UpdateAgentPackage(ctx context.Context, pkg *types.AgentPackage) error {
return nil
}
func (s *stubStorage) DeleteAgentPackage(ctx context.Context, packageID string) error { return nil }
// Real-time features
func (s *stubStorage) SubscribeToMemoryChanges(ctx context.Context, scope, scopeID string) (<-chan types.MemoryChangeEvent, error) {
return nil, nil
}
func (s *stubStorage) PublishMemoryChange(ctx context.Context, event types.MemoryChangeEvent) error {
return nil
}
func (s *stubStorage) GetWorkflowExecutionEventBus() *events.EventBus[*types.WorkflowExecutionEvent] {
return nil
}
// DID Registry operations
func (s *stubStorage) StoreDID(ctx context.Context, did string, didDocument, publicKey, privateKeyRef, derivationPath string) error {
return nil
}
func (s *stubStorage) GetDID(ctx context.Context, did string) (*types.DIDRegistryEntry, error) {
return nil, nil
}
func (s *stubStorage) ListDIDs(ctx context.Context) ([]*types.DIDRegistryEntry, error) {
return nil, nil
}
// HanzoAgents Server DID operations
func (s *stubStorage) StoreHanzoAgentsServerDID(ctx context.Context, hanzoAgentsServerID, rootDID string, masterSeed []byte, createdAt, lastKeyRotation time.Time) error {
return nil
}
func (s *stubStorage) GetHanzoAgentsServerDID(ctx context.Context, hanzoAgentsServerID string) (*types.HanzoAgentsServerDIDInfo, error) {
return nil, nil
}
func (s *stubStorage) ListHanzoAgentsServerDIDs(ctx context.Context) ([]*types.HanzoAgentsServerDIDInfo, error) {
return nil, nil
}
// Agent DID operations
func (s *stubStorage) StoreAgentDID(ctx context.Context, agentID, agentDID, hanzoAgentsServerDID, publicKeyJWK string, derivationIndex int) error {
return nil
}
func (s *stubStorage) GetAgentDID(ctx context.Context, agentID string) (*types.AgentDIDInfo, error) {
return nil, nil
}
func (s *stubStorage) ListAgentDIDs(ctx context.Context) ([]*types.AgentDIDInfo, error) {
return nil, nil
}
// Component DID operations
func (s *stubStorage) StoreComponentDID(ctx context.Context, componentID, componentDID, agentDID, componentType, componentName string, derivationIndex int) error {
return nil
}
func (s *stubStorage) GetComponentDID(ctx context.Context, componentID string) (*types.ComponentDIDInfo, error) {
return nil, nil
}
func (s *stubStorage) ListComponentDIDs(ctx context.Context, agentDID string) ([]*types.ComponentDIDInfo, error) {
return nil, nil
}
// Multi-step DID operations
func (s *stubStorage) StoreAgentDIDWithComponents(ctx context.Context, agentID, agentDID, hanzoAgentsServerDID, publicKeyJWK string, derivationIndex int, components []storage.ComponentDIDRequest) error {
return nil
}
// Execution VC operations
func (s *stubStorage) StoreExecutionVC(ctx context.Context, vcID, executionID, workflowID, sessionID, issuerDID, targetDID, callerDID, inputHash, outputHash, status string, vcDocument []byte, signature string, storageURI string, documentSizeBytes int64) error {
return nil
}
func (s *stubStorage) GetExecutionVC(ctx context.Context, vcID string) (*types.ExecutionVCInfo, error) {
return nil, nil
}
func (s *stubStorage) ListExecutionVCs(ctx context.Context, filters types.VCFilters) ([]*types.ExecutionVCInfo, error) {
return nil, nil
}
func (s *stubStorage) ListWorkflowVCStatusSummaries(ctx context.Context, workflowIDs []string) ([]*types.WorkflowVCStatusAggregation, error) {
return nil, nil
}
// Workflow VC operations
func (s *stubStorage) StoreWorkflowVC(ctx context.Context, workflowVCID, workflowID, sessionID string, componentVCIDs []string, status string, startTime, endTime *time.Time, totalSteps, completedSteps int, storageURI string, documentSizeBytes int64) error {
return nil
}
func (s *stubStorage) GetWorkflowVC(ctx context.Context, workflowVCID string) (*types.WorkflowVCInfo, error) {
return nil, nil
}
func (s *stubStorage) ListWorkflowVCs(ctx context.Context, workflowID string) ([]*types.WorkflowVCInfo, error) {
return nil, nil
}
func (s *stubStorage) CountExecutionVCs(ctx context.Context, filters types.VCFilters) (int, error) {
return 0, nil
}
// Observability webhook operations
func (s *stubStorage) GetObservabilityWebhook(ctx context.Context) (*types.ObservabilityWebhookConfig, error) {
return nil, nil
}
func (s *stubStorage) SetObservabilityWebhook(ctx context.Context, config *types.ObservabilityWebhookConfig) error {
return nil
}
func (s *stubStorage) DeleteObservabilityWebhook(ctx context.Context) error { return nil }
// Dead Letter Queue operations
func (s *stubStorage) AddToDeadLetterQueue(ctx context.Context, event *types.ObservabilityEvent, errorMessage string, retryCount int) error {
return nil
}
func (s *stubStorage) GetDeadLetterQueueCount(ctx context.Context) (int64, error) { return 0, nil }
func (s *stubStorage) GetDeadLetterQueue(ctx context.Context, limit, offset int) ([]types.ObservabilityDeadLetterEntry, error) {
return nil, nil
}
func (s *stubStorage) DeleteFromDeadLetterQueue(ctx context.Context, ids []int64) error { return nil }
func (s *stubStorage) ClearDeadLetterQueue(ctx context.Context) error { return nil }
// Cloud instance stubs
func (s *stubStorage) CreateCloudInstance(ctx context.Context, instance *types.CloudInstance) error {
return nil
}
func (s *stubStorage) GetCloudInstance(ctx context.Context, id string) (*types.CloudInstance, error) {
return nil, nil
}
func (s *stubStorage) UpdateCloudInstance(ctx context.Context, instance *types.CloudInstance) error {
return nil
}
func (s *stubStorage) ListCloudInstances(ctx context.Context, filters types.InstanceFilters) ([]*types.CloudInstance, error) {
return nil, nil
}
func (s *stubStorage) DeleteCloudInstance(ctx context.Context, id string) error { return nil }
func (s *stubStorage) GetCloudInstanceByAgentNodeID(ctx context.Context, agentNodeID string) (*types.CloudInstance, error) {
return nil, nil
}
func (s *stubStorage) CountCloudInstancesByTeam(ctx context.Context, teamID string) (int, error) {
return 0, nil
}
// Dedicated host stubs
func (s *stubStorage) CreateDedicatedHost(ctx context.Context, host *types.DedicatedHost) error {
return nil
}
func (s *stubStorage) GetDedicatedHost(ctx context.Context, id string) (*types.DedicatedHost, error) {
return nil, nil
}
func (s *stubStorage) UpdateDedicatedHost(ctx context.Context, host *types.DedicatedHost) error {
return nil
}
func (s *stubStorage) ListDedicatedHosts(ctx context.Context) ([]*types.DedicatedHost, error) {
return nil, nil
}
func (s *stubStorage) GetAvailableDedicatedHost(ctx context.Context) (*types.DedicatedHost, error) {
return nil, nil
}
// stubPayloadStore implements services.PayloadStore
type stubPayloadStore struct{}
func (s *stubPayloadStore) SaveFromReader(ctx context.Context, r io.Reader) (*services.PayloadRecord, error) {
return nil, nil
}
func (s *stubPayloadStore) SaveBytes(ctx context.Context, data []byte) (*services.PayloadRecord, error) {
return nil, nil
}
func (s *stubPayloadStore) Open(ctx context.Context, uri string) (io.ReadCloser, error) {
return nil, nil
}
func (s *stubPayloadStore) Remove(ctx context.Context, uri string) error {
return nil
}
// stubWebhookDispatcher implements services.WebhookDispatcher
type stubWebhookDispatcher struct{}
func (s *stubWebhookDispatcher) Start(ctx context.Context) error {
return nil
}
func (s *stubWebhookDispatcher) Stop(ctx context.Context) error {
return nil
}
func (s *stubWebhookDispatcher) Notify(ctx context.Context, executionID string) error {
return nil
}
func TestSetupRoutesRegistersMetricsAndUI(t *testing.T) {
t.Parallel()
gin.SetMode(gin.TestMode)
srv := &HanzoAgentsServer{
Router: gin.New(),
storage: newStubStorage(),
payloadStore: &stubPayloadStore{},
webhookDispatcher: &stubWebhookDispatcher{},
config: &config.Config{
UI: config.UIConfig{Enabled: true, Mode: "embedded"},
API: config.APIConfig{},
},
}
srv.setupRoutes()
t.Run("metrics endpoint", func(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, "/metrics", nil)
req.Header.Set("Origin", "http://localhost:5173")
w := httptest.NewRecorder()
srv.Router.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
require.Equal(t, "http://localhost:5173", w.Header().Get("Access-Control-Allow-Origin"))
})
t.Run("root redirect", func(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()
srv.Router.ServeHTTP(w, req)
require.Equal(t, http.StatusMovedPermanently, w.Code)
require.Equal(t, "/ui/", w.Header().Get("Location"))
})
}
func TestSetupRoutesRegistersWorkflowCleanupUIRoute(t *testing.T) {
t.Parallel()
gin.SetMode(gin.TestMode)
srv := &HanzoAgentsServer{
Router: gin.New(),
storage: newStubStorage(),
payloadStore: &stubPayloadStore{},
webhookDispatcher: &stubWebhookDispatcher{},
config: &config.Config{
UI: config.UIConfig{Enabled: true, Mode: "embedded"},
API: config.APIConfig{},
},
}
srv.setupRoutes()
req, _ := http.NewRequest(http.MethodDelete, "/api/ui/v1/workflows/run_test_123/cleanup?confirm=true", nil)
w := httptest.NewRecorder()
srv.Router.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), `"success":true`)
require.Contains(t, w.Body.String(), `"workflow_id":"run_test_123"`)
}
func TestSetupRoutesRegistersHealthEndpoint(t *testing.T) {
t.Parallel()
gin.SetMode(gin.TestMode)
t.Run("health endpoint returns healthy status", func(t *testing.T) {
srv := &HanzoAgentsServer{
Router: gin.New(),
storage: newStubStorage(),
payloadStore: &stubPayloadStore{},
webhookDispatcher: &stubWebhookDispatcher{},
config: &config.Config{
UI: config.UIConfig{Enabled: false},
API: config.APIConfig{},
},
storageHealthOverride: func(context.Context) gin.H { return gin.H{"status": "healthy"} },
}
srv.setupRoutes()
req, _ := http.NewRequest(http.MethodGet, "/health", nil)
w := httptest.NewRecorder()
srv.Router.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), "healthy")
})
t.Run("health endpoint accessible without API key", func(t *testing.T) {
srv := &HanzoAgentsServer{
Router: gin.New(),
storage: newStubStorage(),
payloadStore: &stubPayloadStore{},
webhookDispatcher: &stubWebhookDispatcher{},
config: &config.Config{
UI: config.UIConfig{Enabled: false},
API: config.APIConfig{
Auth: config.AuthConfig{
APIKey: "super-secret-key",
},
},
},
storageHealthOverride: func(context.Context) gin.H { return gin.H{"status": "healthy"} },
}
srv.setupRoutes()
// Request without API key should still succeed for /health
req, _ := http.NewRequest(http.MethodGet, "/health", nil)
w := httptest.NewRecorder()
srv.Router.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
})
t.Run("health endpoint returns CORS headers", func(t *testing.T) {
srv := &HanzoAgentsServer{
Router: gin.New(),
storage: newStubStorage(),
payloadStore: &stubPayloadStore{},
webhookDispatcher: &stubWebhookDispatcher{},
config: &config.Config{
UI: config.UIConfig{Enabled: false},
API: config.APIConfig{},
},
storageHealthOverride: func(context.Context) gin.H { return gin.H{"status": "healthy"} },
}
srv.setupRoutes()
req, _ := http.NewRequest(http.MethodGet, "/health", nil)
req.Header.Set("Origin", "http://localhost:3000")
w := httptest.NewRecorder()
srv.Router.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
require.Equal(t, "http://localhost:3000", w.Header().Get("Access-Control-Allow-Origin"))
})
}
//nolint:unused // Reserved for future test cases
type stubHealthMonitor struct {
*services.HealthMonitor
}
func TestUnregisterAgentFromMonitoringResponses(t *testing.T) {
t.Parallel()
gin.SetMode(gin.TestMode)
srv := &HanzoAgentsServer{}
t.Run("missing node id returns 400", func(t *testing.T) {
req, _ := http.NewRequest(http.MethodDelete, "/internal/nodes//monitor", nil)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Params = append(c.Params, gin.Param{Key: "node_id", Value: ""})
c.Request = req
srv.unregisterAgentFromMonitoring(c)
require.Equal(t, http.StatusBadRequest, w.Code)
})
t.Run("health monitor unavailable", func(t *testing.T) {
req, _ := http.NewRequest(http.MethodDelete, "/internal/nodes/node-1/monitor", nil)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Params = append(c.Params, gin.Param{Key: "node_id", Value: "node-1"})
c.Request = req
srv.unregisterAgentFromMonitoring(c)
require.Equal(t, http.StatusServiceUnavailable, w.Code)
})
t.Run("successful unregister", func(t *testing.T) {
hm := services.NewHealthMonitor(nil, services.HealthMonitorConfig{}, nil, nil, nil, nil)
req, _ := http.NewRequest(http.MethodDelete, "/internal/nodes/node-42/monitor", nil)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Params = append(c.Params, gin.Param{Key: "node_id", Value: "node-42"})
c.Request = req
srv.healthMonitor = hm
srv.unregisterAgentFromMonitoring(c)
require.Equal(t, http.StatusOK, w.Code)
})
}