Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions pkg/api/handlers/console_persistence_handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package handlers

import (
"testing"

"github.com/kubestellar/console/pkg/apis/v1alpha1"
"github.com/stretchr/testify/assert"
)

func TestSetTerminalStatusHistory(t *testing.T) {
t.Run("adds history entry with correct revision", func(t *testing.T) {
wd := &v1alpha1.WorkloadDeployment{}
wd.Name = "test-deployment"
wd.Status.History = []v1alpha1.DeploymentHistoryEntry{
{Revision: 1, Phase: "Complete", Message: "First deployment"},
{Revision: 2, Phase: "Failed", Message: "Second deployment"},
}

h := &ConsolePersistenceHandlers{}
h.setTerminalStatus(wd, "Complete", "Third deployment", func(*v1alpha1.WorkloadDeployment) {})

assert.Len(t, wd.Status.History, 3)
assert.Equal(t, 3, wd.Status.History[2].Revision)
assert.Equal(t, "Complete", wd.Status.History[2].Phase)
assert.Equal(t, "Third deployment", wd.Status.History[2].Message)
})

t.Run("caps history at maxDeploymentHistory", func(t *testing.T) {
wd := &v1alpha1.WorkloadDeployment{}
wd.Name = "test-deployment"

// Add maxDeploymentHistory + 10 entries
for i := 1; i <= maxDeploymentHistory+10; i++ {
wd.Status.History = append(wd.Status.History, v1alpha1.DeploymentHistoryEntry{
Revision: i,
Phase: "Complete",
Message: "Deployment",
})
}

h := &ConsolePersistenceHandlers{}
h.setTerminalStatus(wd, "Complete", "Final deployment", func(*v1alpha1.WorkloadDeployment) {})

assert.Len(t, wd.Status.History, maxDeploymentHistory)
// The oldest entries should be dropped, newest entry should be last
// After adding entry #61, we trim to 50, keeping entries 12-61
assert.Equal(t, 12, wd.Status.History[0].Revision) // First kept entry (60 - 50 + 1 + 1 = 12)
assert.Equal(t, maxDeploymentHistory+11, wd.Status.History[len(wd.Status.History)-1].Revision)
})

t.Run("handles empty history", func(t *testing.T) {
wd := &v1alpha1.WorkloadDeployment{}
wd.Name = "test-deployment"

h := &ConsolePersistenceHandlers{}
h.setTerminalStatus(wd, "Failed", "Initial deployment failed", func(*v1alpha1.WorkloadDeployment) {})

assert.Len(t, wd.Status.History, 1)
assert.Equal(t, 1, wd.Status.History[0].Revision)
assert.Equal(t, "Failed", wd.Status.History[0].Phase)
})
}

func TestResolveTargetClustersLogic(t *testing.T) {
t.Run("explicit clusters only", func(t *testing.T) {
wd := &v1alpha1.WorkloadDeployment{}
wd.Spec.TargetClusters = []string{"cluster-1", "cluster-2", "cluster-3"}

// Test cluster deduplication logic
clusterSet := make(map[string]bool)
for _, c := range wd.Spec.TargetClusters {
clusterSet[c] = true
}

result := make([]string, 0, len(clusterSet))
for c := range clusterSet {
result = append(result, c)
}

assert.Len(t, result, 3)
assert.Contains(t, result, "cluster-1")
assert.Contains(t, result, "cluster-2")
assert.Contains(t, result, "cluster-3")
})

t.Run("duplicate clusters are deduplicated", func(t *testing.T) {
wd := &v1alpha1.WorkloadDeployment{}
wd.Spec.TargetClusters = []string{"cluster-1", "cluster-2", "cluster-1", "cluster-3", "cluster-2"}

clusterSet := make(map[string]bool)
for _, c := range wd.Spec.TargetClusters {
clusterSet[c] = true
}

result := make([]string, 0, len(clusterSet))
for c := range clusterSet {
result = append(result, c)
}

assert.Len(t, result, 3)
})

t.Run("empty target clusters returns empty result", func(t *testing.T) {
wd := &v1alpha1.WorkloadDeployment{}
wd.Spec.TargetClusters = []string{}

clusterSet := make(map[string]bool)
for _, c := range wd.Spec.TargetClusters {
clusterSet[c] = true
}

result := make([]string, 0, len(clusterSet))
for c := range clusterSet {
result = append(result, c)
}

assert.Len(t, result, 0)
})
}

func TestMaxDeploymentHistoryConstant(t *testing.T) {
// Verify the constant is set to a reasonable value
assert.Equal(t, 50, maxDeploymentHistory, "maxDeploymentHistory should be 50 to prevent etcd object-size issues")
}
78 changes: 78 additions & 0 deletions pkg/api/handlers/console_persistence_store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package handlers

import (
"context"
"testing"

"github.com/kubestellar/console/pkg/k8s"
"github.com/kubestellar/console/pkg/store"
"github.com/stretchr/testify/assert"
)

func TestCheckClusterHealth(t *testing.T) {
t.Run("returns unknown when k8s client is nil", func(t *testing.T) {
h := &ConsolePersistenceHandlers{k8sClient: nil}
ctx := context.Background()

health := h.checkClusterHealth(ctx, "test-cluster")
assert.Equal(t, store.ClusterHealthUnknown, health)
})

t.Run("returns unknown when cluster not found", func(t *testing.T) {
// This test verifies the logic path when a cluster is not in the list
// We can't easily test with a real k8s client, but we can test the handler structure
h := &ConsolePersistenceHandlers{}

// With nil client, should return unknown
health := h.checkClusterHealth(context.Background(), "non-existent-cluster")
assert.Equal(t, store.ClusterHealthUnknown, health)
})
}

func TestCheckClusterHealthLogic(t *testing.T) {
t.Run("healthy cluster logic", func(t *testing.T) {
// Test the cluster health determination logic
clusters := []k8s.ClusterInfo{
{Name: "healthy-cluster", Healthy: true},
{Name: "unhealthy-cluster", Healthy: false},
}

// Simulate the logic from checkClusterHealth
findHealth := func(clusterName string) store.ClusterHealth {
for _, cluster := range clusters {
if cluster.Name == clusterName {
if cluster.Healthy {
return store.ClusterHealthHealthy
}
return store.ClusterHealthUnreachable
}
}
return store.ClusterHealthUnknown
}

assert.Equal(t, store.ClusterHealthHealthy, findHealth("healthy-cluster"))
assert.Equal(t, store.ClusterHealthUnreachable, findHealth("unhealthy-cluster"))
assert.Equal(t, store.ClusterHealthUnknown, findHealth("non-existent"))
})
}

func TestStopWatcher(t *testing.T) {
t.Run("stop watcher when watcher is nil", func(t *testing.T) {
h := &ConsolePersistenceHandlers{}

// Should not panic
assert.NotPanics(t, func() {
h.StopWatcher()
})
})

t.Run("stop watcher sets watcher to nil", func(t *testing.T) {
h := &ConsolePersistenceHandlers{}
// Create a mock watcher (we can't easily create a real one without k8s)
// but we can verify the nil assignment logic
h.watcher = nil

h.StopWatcher()
assert.Nil(t, h.watcher)
})
}
112 changes: 112 additions & 0 deletions pkg/api/handlers/mcp/cluster_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package mcp

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestTryStartClusterHealthWarmup(t *testing.T) {
// Reset state before testing
listClustersWarmupMu.Lock()
listClustersWarmupInFlight = false
listClustersWarmupMu.Unlock()

t.Run("first caller becomes owner", func(t *testing.T) {
// Reset state
listClustersWarmupMu.Lock()
listClustersWarmupInFlight = false
listClustersWarmupMu.Unlock()

got := tryStartClusterHealthWarmup()
assert.True(t, got, "first caller should become owner")

// Verify flag is set
listClustersWarmupMu.Lock()
assert.True(t, listClustersWarmupInFlight)
listClustersWarmupMu.Unlock()

// Cleanup
finishClusterHealthWarmup()
})

t.Run("concurrent callers are rejected", func(t *testing.T) {
// Reset state
listClustersWarmupMu.Lock()
listClustersWarmupInFlight = false
listClustersWarmupMu.Unlock()

// First caller succeeds
got1 := tryStartClusterHealthWarmup()
assert.True(t, got1)

// Second caller is rejected
got2 := tryStartClusterHealthWarmup()
assert.False(t, got2, "concurrent caller should be rejected")

// Third caller is also rejected
got3 := tryStartClusterHealthWarmup()
assert.False(t, got3, "concurrent caller should be rejected")

// Cleanup
finishClusterHealthWarmup()
})

t.Run("finish allows next caller", func(t *testing.T) {
// Reset state
listClustersWarmupMu.Lock()
listClustersWarmupInFlight = false
listClustersWarmupMu.Unlock()

// First cycle
got1 := tryStartClusterHealthWarmup()
assert.True(t, got1)

finishClusterHealthWarmup()

// Verify flag is cleared
listClustersWarmupMu.Lock()
assert.False(t, listClustersWarmupInFlight)
listClustersWarmupMu.Unlock()

// Second cycle should succeed
got2 := tryStartClusterHealthWarmup()
assert.True(t, got2, "after finish, next caller should become owner")

// Cleanup
finishClusterHealthWarmup()
})
}

func TestFinishClusterHealthWarmup(t *testing.T) {
t.Run("clears in-flight flag", func(t *testing.T) {
// Set flag
listClustersWarmupMu.Lock()
listClustersWarmupInFlight = true
listClustersWarmupMu.Unlock()

finishClusterHealthWarmup()

// Verify cleared
listClustersWarmupMu.Lock()
assert.False(t, listClustersWarmupInFlight)
listClustersWarmupMu.Unlock()
})

t.Run("is safe to call when flag already cleared", func(t *testing.T) {
// Reset state
listClustersWarmupMu.Lock()
listClustersWarmupInFlight = false
listClustersWarmupMu.Unlock()

// Should not panic
assert.NotPanics(t, func() {
finishClusterHealthWarmup()
})

// Verify still cleared
listClustersWarmupMu.Lock()
assert.False(t, listClustersWarmupInFlight)
listClustersWarmupMu.Unlock()
})
}
Loading
Loading