mirror of
https://github.com/lukaszraczylo/kportal.git
synced 2026-06-05 23:03:40 +00:00
a297ba7073
When user starts kportal for the first time, and there is no config file, kportal should create an empty config file with default values and empty forwarding rules, so that user can easily edit the config file and add their own rules.
374 lines
9.2 KiB
Go
374 lines
9.2 KiB
Go
package forward
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nvm/kportal/internal/config"
|
|
"github.com/nvm/kportal/internal/events"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestNewManager tests manager creation
|
|
func TestNewManager(t *testing.T) {
|
|
t.Run("creates manager with default settings", func(t *testing.T) {
|
|
// Skip if no kubeconfig available (CI environment)
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
assert.NotNil(t, manager.workers)
|
|
assert.NotNil(t, manager.portChecker)
|
|
assert.NotNil(t, manager.healthChecker)
|
|
assert.NotNil(t, manager.watchdog)
|
|
assert.NotNil(t, manager.eventBus)
|
|
assert.False(t, manager.verbose)
|
|
})
|
|
|
|
t.Run("creates manager in verbose mode", func(t *testing.T) {
|
|
manager, err := NewManager(true)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
assert.True(t, manager.verbose)
|
|
})
|
|
}
|
|
|
|
// TestManager_SetStatusUI tests setting the status UI
|
|
func TestManager_SetStatusUI(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
mockUI := &MockStatusUpdater{}
|
|
manager.SetStatusUI(mockUI)
|
|
|
|
assert.Equal(t, mockUI, manager.statusUI)
|
|
}
|
|
|
|
// TestManager_GetEventBus tests getting the event bus
|
|
func TestManager_GetEventBus(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
bus := manager.GetEventBus()
|
|
assert.NotNil(t, bus)
|
|
}
|
|
|
|
// TestManager_GetWorkerCount tests worker count tracking
|
|
func TestManager_GetWorkerCount(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
assert.Equal(t, 0, manager.GetWorkerCount())
|
|
}
|
|
|
|
// TestManager_GetActiveForwards tests getting active forwards
|
|
func TestManager_GetActiveForwards(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
forwards := manager.GetActiveForwards()
|
|
assert.Empty(t, forwards)
|
|
}
|
|
|
|
// TestManager_GetWorker tests getting a worker by ID
|
|
func TestManager_GetWorker(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
// Non-existent worker
|
|
worker := manager.GetWorker("non-existent")
|
|
assert.Nil(t, worker)
|
|
}
|
|
|
|
// TestManager_Start_NilConfig tests starting with nil config
|
|
func TestManager_Start_NilConfig(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
err = manager.Start(nil)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "configuration is nil")
|
|
}
|
|
|
|
// TestManager_Start_EmptyForwards tests starting with empty forwards
|
|
func TestManager_Start_EmptyForwards(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
cfg := &config.Config{}
|
|
err = manager.Start(cfg)
|
|
// Empty config is now valid - allows users to add forwards via TUI
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// TestManager_Reload_NilConfig tests reloading with nil config
|
|
func TestManager_Reload_NilConfig(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
err = manager.Reload(nil)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "new configuration is nil")
|
|
}
|
|
|
|
// TestManager_EnableForward_NoConfig tests enabling without config
|
|
func TestManager_EnableForward_NoConfig(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
err = manager.EnableForward("some-id")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "no configuration available")
|
|
}
|
|
|
|
// TestManager_DisableForward_NotFound tests disabling non-existent forward
|
|
func TestManager_DisableForward_NotFound(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
err = manager.DisableForward("non-existent")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "worker not found")
|
|
}
|
|
|
|
// TestManager_extractPorts tests port extraction
|
|
func TestManager_extractPorts(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
forwards := []config.Forward{
|
|
{LocalPort: 8080},
|
|
{LocalPort: 5432},
|
|
{LocalPort: 3000},
|
|
}
|
|
|
|
ports := manager.extractPorts(forwards)
|
|
assert.Equal(t, []int{8080, 5432, 3000}, ports)
|
|
}
|
|
|
|
// TestManager_getResourceForPort tests finding resource by port
|
|
func TestManager_getResourceForPort(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
forwards := []config.Forward{
|
|
{Resource: "pod/app1", LocalPort: 8080, Port: 80},
|
|
{Resource: "service/db", LocalPort: 5432, Port: 5432},
|
|
}
|
|
|
|
// Found
|
|
resource := manager.getResourceForPort(forwards, 8080)
|
|
assert.Contains(t, resource, "app1")
|
|
|
|
// Not found
|
|
resource = manager.getResourceForPort(forwards, 9999)
|
|
assert.Equal(t, "unknown", resource)
|
|
}
|
|
|
|
// MockStatusUpdater is a mock implementation of StatusUpdater
|
|
type MockStatusUpdater struct {
|
|
updates []StatusUpdate
|
|
adds []ForwardAdd
|
|
removes []string
|
|
errorSets []ErrorSet
|
|
}
|
|
|
|
type StatusUpdate struct {
|
|
ID string
|
|
Status string
|
|
}
|
|
|
|
type ForwardAdd struct {
|
|
ID string
|
|
Fwd *config.Forward
|
|
}
|
|
|
|
type ErrorSet struct {
|
|
ID string
|
|
Msg string
|
|
}
|
|
|
|
func (m *MockStatusUpdater) UpdateStatus(id string, status string) {
|
|
m.updates = append(m.updates, StatusUpdate{ID: id, Status: status})
|
|
}
|
|
|
|
func (m *MockStatusUpdater) AddForward(id string, fwd *config.Forward) {
|
|
m.adds = append(m.adds, ForwardAdd{ID: id, Fwd: fwd})
|
|
}
|
|
|
|
func (m *MockStatusUpdater) Remove(id string) {
|
|
m.removes = append(m.removes, id)
|
|
}
|
|
|
|
func (m *MockStatusUpdater) SetError(id, msg string) {
|
|
m.errorSets = append(m.errorSets, ErrorSet{ID: id, Msg: msg})
|
|
}
|
|
|
|
// TestConfigureHealthChecker tests health checker configuration
|
|
func TestConfigureHealthChecker(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
tests := []struct {
|
|
name string
|
|
method string
|
|
}{
|
|
{"tcp-dial method", "tcp-dial"},
|
|
{"data-transfer method", "data-transfer"},
|
|
{"unknown method defaults to data-transfer", "unknown"},
|
|
{"empty method defaults to data-transfer", ""},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
cfg := &config.Config{
|
|
HealthCheck: &config.HealthCheckSpec{
|
|
Method: tt.method,
|
|
},
|
|
}
|
|
|
|
// Should not panic
|
|
manager.configureHealthChecker(cfg)
|
|
assert.NotNil(t, manager.healthChecker)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestManager_Stop tests graceful shutdown
|
|
func TestManager_Stop(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
|
|
// Stop should not panic even with no workers
|
|
done := make(chan bool)
|
|
go func() {
|
|
manager.Stop()
|
|
done <- true
|
|
}()
|
|
|
|
select {
|
|
case <-done:
|
|
// Success
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("Stop timed out")
|
|
}
|
|
}
|
|
|
|
// TestManager_Reload_EmptyToEmpty tests reloading from empty to empty config
|
|
func TestManager_Reload_EmptyToEmpty(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
cfg := &config.Config{}
|
|
err = manager.Reload(cfg)
|
|
// Should handle gracefully (stop all workers if any)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// TestPortConflict tests the PortConflict struct
|
|
func TestPortConflict(t *testing.T) {
|
|
conflict := PortConflict{
|
|
Port: 8080,
|
|
Resource: "dev/default/pod/app:8080",
|
|
UsedBy: "nginx (PID 1234)",
|
|
}
|
|
|
|
assert.Equal(t, 8080, conflict.Port)
|
|
assert.Equal(t, "dev/default/pod/app:8080", conflict.Resource)
|
|
assert.Equal(t, "nginx (PID 1234)", conflict.UsedBy)
|
|
}
|
|
|
|
// TestStatusUpdater_Interface tests that MockStatusUpdater implements StatusUpdater
|
|
func TestStatusUpdater_Interface(t *testing.T) {
|
|
var _ StatusUpdater = (*MockStatusUpdater)(nil)
|
|
}
|
|
|
|
// TestManager_WorkersMap tests workers map operations
|
|
func TestManager_WorkersMap(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
// Initial state
|
|
assert.Empty(t, manager.workers)
|
|
|
|
// Verify concurrent-safe access patterns
|
|
manager.workersMu.RLock()
|
|
count := len(manager.workers)
|
|
manager.workersMu.RUnlock()
|
|
assert.Equal(t, 0, count)
|
|
}
|
|
|
|
// TestManager_EventBusIntegration tests event bus wiring
|
|
func TestManager_EventBusIntegration(t *testing.T) {
|
|
manager, err := NewManager(false)
|
|
if err != nil {
|
|
t.Skip("Skipping test - no kubeconfig available")
|
|
}
|
|
defer manager.Stop()
|
|
|
|
// Event bus should be wired to health checker and watchdog
|
|
assert.NotNil(t, manager.eventBus)
|
|
|
|
// Get event bus
|
|
bus := manager.GetEventBus()
|
|
require.NotNil(t, bus)
|
|
|
|
// SubscribeAll should work (no return value in this API)
|
|
bus.SubscribeAll(func(event events.Event) {
|
|
// Handler
|
|
})
|
|
}
|