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
7 changes: 5 additions & 2 deletions pkg/internal/enterprise.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ kubeslice:
type: %s
`

var runCommandCustomIO = util.RunCommandCustomIO
var getNodeIPFunc = getNodeIP

func InstallKubeSliceUI(ApplicationConfiguration *ConfigurationSpecs) {
util.Printf("\nInstalling KubeSlice Manager...")
if ApplicationConfiguration.Configuration.HelmChartConfiguration.UIChart.ChartName == "" {
Expand Down Expand Up @@ -112,7 +115,7 @@ func GetUIEndpoint(cc *Cluster, profile string) string {
ep := ""

var outB, errB bytes.Buffer
err := util.RunCommandCustomIO("kubectl", &outB, &errB, true, "--context="+cc.ContextName, "--kubeconfig="+cc.KubeConfigPath, "get", "services", "kubeslice-ui-proxy", "-n", KUBESLICE_CONTROLLER_NAMESPACE, "-o", "jsonpath='{.spec}'")
err := runCommandCustomIO("kubectl", &outB, &errB, true, "--context="+cc.ContextName, "--kubeconfig="+cc.KubeConfigPath, "get", "services", "kubeslice-ui-proxy", "-n", KUBESLICE_CONTROLLER_NAMESPACE, "-o", "jsonpath='{.spec}'")
if err == nil {
jsonMap := make(map[string]interface{})
err = json.Unmarshal(outB.Bytes()[1:len(outB.Bytes())-1], &jsonMap)
Expand All @@ -129,7 +132,7 @@ func GetUIEndpoint(cc *Cluster, profile string) string {
portMap := port.(map[string]interface{})
if portMap["name"] == "http" { // Assuming that http is the name of the port that you want to use
nodePort := int(portMap["nodePort"].(float64))
nodeIP, err := getNodeIP(cc)
nodeIP, err := getNodeIPFunc(cc)
if err == nil {
ep = fmt.Sprintf("https://%s:%d", strings.Trim(nodeIP, "'"), nodePort)
} else {
Expand Down
129 changes: 129 additions & 0 deletions pkg/internal/enterprise_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package internal

import (
"errors"
"io"
"testing"

"github.com/kubeslice/kubeslice-cli/util"
)

// Mock getNodeIP to return dummy IP
func mockGetNodeIP(_ *Cluster) (string, error) {
return "192.168.1.100", nil
}

func TestGetUIEndpoint_NodePort(t *testing.T) {
runCalled := false

// Mock kubectl output for NodePort
nodePortJSON := `{
"type": "NodePort",
"ports": [{
"name": "http",
"nodePort": 30080
}]
}`

runCommandCustomIO = func(name string, stdout, stderr io.Writer, _ bool, args ...string) error {
runCalled = true
stdout.Write([]byte("'" + nodePortJSON + "'")) // wrap in quotes to simulate jsonpath output
return nil
}
getNodeIPFunc = mockGetNodeIP
defer func() {
runCommandCustomIO = util.RunCommandCustomIO
getNodeIPFunc = getNodeIP
}()

cluster := &Cluster{
ContextName: "mock-context",
KubeConfigPath: "/fake/config",
}
endpoint := GetUIEndpoint(cluster, "some-profile")

expected := "https://192.168.1.100:30080"
if endpoint != expected {
t.Errorf("Expected endpoint %q, got %q", expected, endpoint)
}
if !runCalled {
t.Error("Expected RunCommandCustomIO to be called")
}
}

// Mocks a LoadBalancer service and checks the endpoint.
func TestGetUIEndpoint_LoadBalancer(t *testing.T) {
runCalled := false

// Mock output for LoadBalancer
loadBalancerJSON := `{
"type": "LoadBalancer",
"externalIPs": ["1.2.3.4"],
"ports": [{
"name": "http",
"port": 443
}]
}`

runCommandCustomIO = func(name string, stdout, stderr io.Writer, _ bool, args ...string) error {
runCalled = true
stdout.Write([]byte("'" + loadBalancerJSON + "'"))
return nil
}
defer func() {
runCommandCustomIO = util.RunCommandCustomIO
}()

cluster := &Cluster{
ContextName: "mock-context",
KubeConfigPath: "/fake/config",
}
endpoint := GetUIEndpoint(cluster, "some-profile")

expected := "https://1.2.3.4:443"
if endpoint != expected {
t.Errorf("Expected endpoint %q, got %q", expected, endpoint)
}
if !runCalled {
t.Error("Expected RunCommandCustomIO to be called")
}
}

// Mocks invalid JSON output and checks that the function returns an empty string
func TestGetUIEndpoint_InvalidJSON(t *testing.T) {
runCommandCustomIO = func(name string, stdout, stderr io.Writer, _ bool, args ...string) error {
stdout.Write([]byte("'not-a-json'"))
return nil
}
defer func() {
runCommandCustomIO = util.RunCommandCustomIO
}()

cluster := &Cluster{
ContextName: "mock-context",
KubeConfigPath: "/fake/config",
}
endpoint := GetUIEndpoint(cluster, "profile")
if endpoint != "" {
t.Errorf("Expected empty endpoint on invalid JSON, got %q", endpoint)
}
}

// Mocks a command failure and checks that the function returns an empty string
func TestGetUIEndpoint_CommandFailure(t *testing.T) {
runCommandCustomIO = func(name string, stdout, stderr io.Writer, _ bool, args ...string) error {
return errors.New("kubectl failed")
}
defer func() {
runCommandCustomIO = util.RunCommandCustomIO
}()

cluster := &Cluster{
ContextName: "mock-context",
KubeConfigPath: "/fake/config",
}
endpoint := GetUIEndpoint(cluster, "profile")
if endpoint != "" {
t.Errorf("Expected empty endpoint on command failure, got %q", endpoint)
}
}
4 changes: 3 additions & 1 deletion pkg/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package pkg

import "github.com/kubeslice/kubeslice-cli/pkg/internal"

var getUIEndpointFunc = internal.GetUIEndpoint

func GetUIEndpoint() {
internal.GetUIEndpoint(CliOptions.Cluster, ApplicationConfiguration.Configuration.ClusterConfiguration.Profile)
getUIEndpointFunc(CliOptions.Cluster, ApplicationConfiguration.Configuration.ClusterConfiguration.Profile)
}
49 changes: 49 additions & 0 deletions pkg/ui_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package pkg

import (
"testing"

"github.com/kubeslice/kubeslice-cli/pkg/internal"
)

func TestGetUIEndpoint_WithMock(t *testing.T) {
// Initialize CliOptions using the helper
cliParams := CliParams{
ObjectType: "project",
ObjectName: "mock-cluster",
Namespace: "test-namespace",
FileName: "test-file.yaml",
Config: "",
OutputFormat: "json",
}
SetCliOptions(cliParams)

// Initialize ApplicationConfiguration and nested fields
ApplicationConfiguration = &internal.ConfigurationSpecs{}
ApplicationConfiguration.Configuration = internal.Configuration{}
ApplicationConfiguration.Configuration.ClusterConfiguration = internal.ClusterConfiguration{}
ApplicationConfiguration.Configuration.ClusterConfiguration.Profile = "mock-profile"

called := false
mockFunc := func(c *internal.Cluster, profile string) string {
called = true
if c.Name != "mock-cluster" || profile != "mock-profile" {
t.Errorf("Unexpected values: cluster=%v, profile=%v", c.Name, profile)
}
return "https://mock-endpoint"
}

// Inject mock
getUIEndpointFunc = mockFunc
defer func() { getUIEndpointFunc = internal.GetUIEndpoint }() // Restore after test

// Inject mock config
CliOptions.Cluster = &internal.Cluster{Name: "mock-cluster"}
ApplicationConfiguration.Configuration.ClusterConfiguration.Profile = "mock-profile"

GetUIEndpoint()

if !called {
t.Error("Expected mock GetUIEndpoint to be called")
}
}