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
49 changes: 49 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Go Test and Coverage

on:
push:
branches: [ "master", "main" ]
pull_request:
branches: [ "master", "main" ]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'

- name: Install dependencies
run: go mod download

- name: Run tests
run: go test -v ./...

- name: Generate coverage
run: go test -coverprofile=coverage.out ./...

- name: Show coverage
run: go tool cover -func=coverage.out

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'

- name: Run go vet
run: go vet ./...

- name: Check formatting
run: gofmt -l . | grep -q . && exit 1 || exit 0
114 changes: 114 additions & 0 deletions cmd/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package cmd

import (
"testing"

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

func TestMapFromSlice(t *testing.T) {
tests := []struct {
name string
input []string
expected map[string]string
}{
{
name: "empty slice",
input: []string{},
expected: map[string]string{},
},
{
name: "single element",
input: []string{"test"},
expected: map[string]string{"test": ""},
},
{
name: "multiple elements",
input: []string{"step1", "step2", "step3"},
expected: map[string]string{"step1": "", "step2": "", "step3": ""},
},
{
name: "duplicate elements",
input: []string{"step1", "step1", "step2"},
expected: map[string]string{"step1": "", "step2": ""},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := mapFromSlice(tt.input)
assert.Equal(t, tt.expected, result)
})
}
}

func TestGlobalVariables(t *testing.T) {
// Test that global variables are properly initialized
assert.Equal(t, "", profile)
assert.Equal(t, "", outputFormat)
assert.Equal(t, "", Config)
assert.Equal(t, []string{}, skipSteps)
}

func TestGlobalVariableTypes(t *testing.T) {
// Test that global variables have correct types
assert.IsType(t, "", profile)
assert.IsType(t, []string{}, skipSteps)
assert.IsType(t, "", outputFormat)
assert.IsType(t, "", Config)
}

func TestMapFromSliceNil(t *testing.T) {
// Test with nil input
result := mapFromSlice(nil)
assert.NotNil(t, result)
assert.Equal(t, 0, len(result))
}

func TestMapFromSliceWithSpecialCharacters(t *testing.T) {
input := []string{"step-1", "step_2", "step.3", "step@4"}
expected := map[string]string{
"step-1": "",
"step_2": "",
"step.3": "",
"step@4": "",
}

result := mapFromSlice(input)
assert.Equal(t, expected, result)
}

func TestMapFromSliceWithEmptyStrings(t *testing.T) {
input := []string{"", "step1", "", "step2"}
expected := map[string]string{
"": "",
"step1": "",
"step2": "",
}

result := mapFromSlice(input)
assert.Equal(t, expected, result)
}

func TestMapFromSliceReturnType(t *testing.T) {
result := mapFromSlice([]string{"test"})
assert.IsType(t, map[string]string{}, result)
}

func TestMapFromSliceLargeInput(t *testing.T) {
// Test with a large number of elements
input := make([]string, 1000)
for i := 0; i < 1000; i++ {
input[i] = "step" + string(rune(i))
}

result := mapFromSlice(input)
assert.Equal(t, 1000, len(result))

// Check that all keys exist and have empty string values
for _, step := range input {
value, exists := result[step]
assert.True(t, exists)
assert.Equal(t, "", value)
}
}
226 changes: 226 additions & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package cmd

import (
"bytes"
"os"
"testing"

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

func TestRootCmd(t *testing.T) {
tests := []struct {
name string
args []string
wantErr bool
wantOutput string
}{
{
name: "help flag",
args: []string{"--help"},
wantErr: false,
wantOutput: "kubeslice-cli - a simple CLI for KubeSlice Operations",
},
{
name: "version flag",
args: []string{"--version"},
wantErr: false,
wantOutput: "kubeslice-cli version 0.6.0",
},
{
name: "no args shows help",
args: []string{},
wantErr: false,
wantOutput: "kubeslice-cli - a simple CLI for KubeSlice Operations",
},
{
name: "config flag with value",
args: []string{"--config", "/path/to/config.yaml"},
wantErr: false,
},
{
name: "config short flag",
args: []string{"-c", "/path/to/config.yaml"},
wantErr: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Reset global variables
Config = ""

// Create a new root command for each test
cmd := &cobra.Command{
Use: "kubeslice-cli",
Version: version,
Short: "kubeslice-cli - a simple CLI for KubeSlice Operations",
Long: `kubeslice-cli - a simple CLI for KubeSlice Operations

Use kubeslice-cli to install/uninstall required workloads to run KubeSlice Controller and KubeSlice Worker.
Additional example applications can also be installed in demo profiles to showcase the
KubeSlice functionality`,
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
}

cmd.PersistentFlags().StringVarP(&Config, "config", "c", "", `<path-to-topology-configuration-yaml-file>
The yaml file with topology configuration.
Refer: https://github.com/kubeslice/kubeslice-cli/blob/master/samples/template.yaml`)

var output bytes.Buffer
cmd.SetOut(&output)
cmd.SetErr(&output)
cmd.SetArgs(tt.args)

err := cmd.Execute()

if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}

if tt.wantOutput != "" {
outputStr := output.String()
assert.Contains(t, outputStr, tt.wantOutput)
}
})
}
}

func TestExecute(t *testing.T) {
// Save original args and restore after test
originalArgs := os.Args
defer func() { os.Args = originalArgs }()

tests := []struct {
name string
args []string
wantExit bool
}{
{
name: "help command",
args: []string{"kubeslice-cli", "--help"},
wantExit: false,
},
{
name: "version command",
args: []string{"kubeslice-cli", "--version"},
wantExit: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Reset global variables
Config = ""

// Create new root command to avoid state pollution
testRootCmd := &cobra.Command{
Use: "kubeslice-cli",
Version: version,
Short: "kubeslice-cli - a simple CLI for KubeSlice Operations",
Long: `kubeslice-cli - a simple CLI for KubeSlice Operations

Use kubeslice-cli to install/uninstall required workloads to run KubeSlice Controller and KubeSlice Worker.
Additional example applications can also be installed in demo profiles to showcase the
KubeSlice functionality`,
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
}

testRootCmd.PersistentFlags().StringVarP(&Config, "config", "c", "", `<path-to-topology-configuration-yaml-file>
The yaml file with topology configuration.
Refer: https://github.com/kubeslice/kubeslice-cli/blob/master/samples/template.yaml`)

var output bytes.Buffer
testRootCmd.SetOut(&output)
testRootCmd.SetErr(&output)
testRootCmd.SetArgs(tt.args[1:]) // Skip program name

err := testRootCmd.Execute()
assert.NoError(t, err)
})
}
}

func TestRootCmdVersion(t *testing.T) {
assert.Equal(t, "0.6.0", version)
}

func TestRootCmdGlobalVariable(t *testing.T) {
// Test that RootCmd is properly exported
assert.NotNil(t, RootCmd)
assert.Equal(t, "kubeslice-cli", RootCmd.Use)
assert.Equal(t, version, RootCmd.Version)
}

func TestConfigFlag(t *testing.T) {
// Reset global variable
Config = ""

cmd := &cobra.Command{
Use: "test",
Run: func(cmd *cobra.Command, args []string) {},
}

cmd.PersistentFlags().StringVarP(&Config, "config", "c", "", "config file path")
cmd.SetArgs([]string{"--config", "/test/path.yaml"})

err := cmd.Execute()
assert.NoError(t, err)
assert.Equal(t, "/test/path.yaml", Config)
}

func TestConfigFlagShort(t *testing.T) {
// Reset global variable
Config = ""

cmd := &cobra.Command{
Use: "test",
Run: func(cmd *cobra.Command, args []string) {},
}

cmd.PersistentFlags().StringVarP(&Config, "config", "c", "", "config file path")
cmd.SetArgs([]string{"-c", "/test/path.yaml"})

err := cmd.Execute()
assert.NoError(t, err)
assert.Equal(t, "/test/path.yaml", Config)
}

func TestRootCmdLongDescription(t *testing.T) {
expectedLong := `kubeslice-cli - a simple CLI for KubeSlice Operations

Use kubeslice-cli to install/uninstall required workloads to run KubeSlice Controller and KubeSlice Worker.
Additional example applications can also be installed in demo profiles to showcase the
KubeSlice functionality`

assert.Equal(t, expectedLong, RootCmd.Long)
}

func TestRootCmdShortDescription(t *testing.T) {
expectedShort := "kubeslice-cli - a simple CLI for KubeSlice Operations"
assert.Equal(t, expectedShort, RootCmd.Short)
}

func TestRootCmdRunFunction(t *testing.T) {
// Test that the run function shows help
var output bytes.Buffer
cmd := &cobra.Command{
Use: "test",
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
}
cmd.SetOut(&output)

cmd.Run(cmd, []string{})

// Should contain usage information
assert.Contains(t, output.String(), "Usage:")
}
Loading