Skip to content

Improve ban subcommand#123

Merged
jrosental merged 22 commits into
kubesaw:masterfrom
jrosental:improve-ban-subcommand
Oct 7, 2025
Merged

Improve ban subcommand#123
jrosental merged 22 commits into
kubesaw:masterfrom
jrosental:improve-ban-subcommand

Conversation

@jrosental

Copy link
Copy Markdown
Contributor

@MatousJobanek MatousJobanek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start 👍
Let me know if you need any help with the unit tests - see my last comment of the review for the hints.

Comment thread pkg/cmd/ban.go Outdated

const (
menuKey string = "menu.json"
configMapName string = "banning-reasons"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably name it ban-reason-config

Comment thread pkg/cmd/ban.go Outdated
Comment on lines +151 to +156
if len(args) == 2 {
// Traditional usage: both usersignup name and ban reason provided
banReason = args[1]
} else {
// Interactive mode: only usersignup name provided, need to get reason from ConfigMap menu
ctx.Printlnf("No ban reason provided. Checking for available reasons from ConfigMap...")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather use the following:

  • drop the second argument (the ban reason)
  • always try to get the ConfigMap
  • if ConfigMap is not available (or is empty) then ask user to provide the reason as an input in the interactive flow

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Comment thread pkg/cmd/ban.go Outdated
Comment on lines +52 to +55
if apierrors.IsNotFound(err) {
return nil, fmt.Errorf("failed to get ConfigMap: %w", err)
}
return nil, err

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simplify it only to

Suggested change
if apierrors.IsNotFound(err) {
return nil, fmt.Errorf("failed to get ConfigMap: %w", err)
}
return nil, err
return nil, err

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I just wanted to be more specific here.

Comment thread pkg/cmd/ban.go Outdated
configMap := &corev1.ConfigMap{}
err = cl.Get(context.TODO(), types.NamespacedName{
Name: configMapName,
Namespace: "toolchain-host-operator",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Namespace: "toolchain-host-operator",
Namespace: cfg.OperatorNamespace,

so it's not hard-coded, but loaded from the ksctl.yaml file

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

Comment thread pkg/cmd/ban.go Outdated
if menuJSON, exists := configMap.Data[menuKey]; exists && menuJSON != "" {
//var menus []Menu
if err := json.Unmarshal([]byte(menuJSON), &menus); err != nil {
return nil, fmt.Errorf("ConfigMap doesn't contain %s key: %w", menuKey, err)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the error returned is not because the ConfigMap wouldn't contain the key, but because the value doesn't contain the correct JSON format.
The error that it doesn't contain the key should be returned somewhere else

Comment thread pkg/cmd/ban_test.go Outdated
Comment on lines +300 to +351
t.Run("single workload menu item", func(t *testing.T) {
// This test demonstrates the structure but cannot test interactivity

// given
menu := []cmd.Menu{
{
Kind: "workload",
Description: "Select workload type",
Options: []string{"container", "vm"},
},
}

// Verify menu structure is correct for processing
assert.Len(t, menu, 1)
assert.Equal(t, "workload", menu[0].Kind)
assert.Equal(t, "Select workload type", menu[0].Description)
assert.Len(t, menu[0].Options, 2)
assert.Contains(t, menu[0].Options, "container")
})

t.Run("multiple menu items structure", func(t *testing.T) {
// given
menu := []cmd.Menu{
{
Kind: "workload",
Description: "Select workload type",
Options: []string{"container", "vm"},
},
{
Kind: "behavior",
Description: "Select behavior classification",
Options: []string{"malicious", "suspicious", "policy-violation"},
},
{
Kind: "detection",
Description: "Select detection mechanism",
Options: []string{"automated", "manual", "user-report"},
},
}

// Verify menu structure supports all BanInfo fields
kindMap := make(map[string]bool)
for _, item := range menu {
kindMap[item.Kind] = true
assert.NotEmpty(t, item.Description)
assert.NotEmpty(t, item.Options)
}

assert.True(t, kindMap["workload"])
assert.True(t, kindMap["behavior"])
assert.True(t, kindMap["detection"])
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct me if I missed something, but it looks like that you don't verify any actual logic in these two tests.

Comment thread pkg/cmd/ban_test.go Outdated
Comment on lines +354 to +633
func TestBanMenuMappingLogic(t *testing.T) {
// This test verifies the mapping logic that converts menu selections to BanInfo

t.Run("verify BanInfo field mapping", func(t *testing.T) {
// Test data that simulates what would be collected from the interactive menu
testCases := []struct {
name string
kind string
answer string
expected func(*cmd.BanInfo) string
}{
{
name: "workload mapping",
kind: "workload",
answer: "compute-intensive",
expected: func(info *cmd.BanInfo) string {
return info.WorkloadType
},
},
{
name: "behavior mapping",
kind: "behavior",
answer: "malicious",
expected: func(info *cmd.BanInfo) string {
return info.BehaviorClassification
},
},
{
name: "detection mapping",
kind: "detection",
answer: "automated",
expected: func(info *cmd.BanInfo) string {
return info.DetectionMechanism
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// This demonstrates the expected mapping behavior
banInfo := &cmd.BanInfo{}

// Simulate the switch statement logic from banMenu
switch tc.kind {
case "workload":
banInfo.WorkloadType = tc.answer
case "behavior":
banInfo.BehaviorClassification = tc.answer
case "detection":
banInfo.DetectionMechanism = tc.answer
}

assert.Equal(t, tc.expected(banInfo), tc.answer)
})
}
})
}

func TestMenuStruct(t *testing.T) {
t.Run("JSON unmarshaling works correctly", func(t *testing.T) {
// given
jsonData := `[
{
"kind": "workload",
"description": "Select workload type",
"options": ["container", "vm"]
},
{
"kind": "behavior",
"description": "Select behavior classification",
"options": ["malicious", "suspicious"]
}
]`

// when
var menus []cmd.Menu
err := json.Unmarshal([]byte(jsonData), &menus)

// then
require.NoError(t, err)
assert.Len(t, menus, 2)

// Verify first menu
assert.Equal(t, "workload", menus[0].Kind)
assert.Equal(t, "Select workload type", menus[0].Description)
assert.Len(t, menus[0].Options, 2)
assert.Contains(t, menus[0].Options, "container")
assert.Contains(t, menus[0].Options, "vm")

// Verify second menu
assert.Equal(t, "behavior", menus[1].Kind)
assert.Equal(t, "Select behavior classification", menus[1].Description)
assert.Len(t, menus[1].Options, 2)
assert.Contains(t, menus[1].Options, "malicious")
assert.Contains(t, menus[1].Options, "suspicious")
})

t.Run("empty JSON array unmarshals correctly", func(t *testing.T) {
// given
jsonData := `[]`

// when
var menus []cmd.Menu
err := json.Unmarshal([]byte(jsonData), &menus)

// then
require.NoError(t, err)
assert.Empty(t, menus)
})

t.Run("malformed JSON returns error", func(t *testing.T) {
// given
jsonData := `[{invalid json`

// when
var menus []cmd.Menu
err := json.Unmarshal([]byte(jsonData), &menus)

// then
require.Error(t, err)
})
}

func TestBanInfoStruct(t *testing.T) {
t.Run("JSON marshaling works correctly", func(t *testing.T) {
// given
banInfo := &cmd.BanInfo{
WorkloadType: "container",
BehaviorClassification: "malicious",
DetectionMechanism: "automated",
}

// when
jsonData, err := json.Marshal(banInfo)

// then
require.NoError(t, err)
assert.Contains(t, string(jsonData), "container")
assert.Contains(t, string(jsonData), "malicious")
assert.Contains(t, string(jsonData), "automated")

// Verify it can be unmarshaled back
var unmarshaled cmd.BanInfo
err = json.Unmarshal(jsonData, &unmarshaled)
require.NoError(t, err)
assert.Equal(t, banInfo.WorkloadType, unmarshaled.WorkloadType)
assert.Equal(t, banInfo.BehaviorClassification, unmarshaled.BehaviorClassification)
assert.Equal(t, banInfo.DetectionMechanism, unmarshaled.DetectionMechanism)
})

t.Run("empty BanInfo marshals to empty fields", func(t *testing.T) {
// given
banInfo := &cmd.BanInfo{}

// when
jsonData, err := json.Marshal(banInfo)

// then
require.NoError(t, err)
assert.Contains(t, string(jsonData), `"workloadType":""`)
assert.Contains(t, string(jsonData), `"behaviorClassification":""`)
assert.Contains(t, string(jsonData), `"detectionMechanism":""`)
})
}

// TestBanMenuInteractiveLogic tests the interactive menu logic in BanMenu function (lines 76-110)
func TestBanMenuLogic(t *testing.T) {
t.Run("BanMenu with menu content creates proper data structures", func(t *testing.T) {
// This test verifies the logic of creating huh.Option structures and the mapping
// We can't test the actual interaction, but we can test the data preparation

// given
menu := []cmd.Menu{
{
Kind: "workload",
Description: "Select workload type",
Options: []string{"container", "vm"},
},
{
Kind: "behavior",
Description: "Select behavior classification",
Options: []string{"malicious", "suspicious"},
},
}

// Verify the menu structure that would be processed in lines 77-95
for _, item := range menu {
// Simulate the options creation logic from lines 79-82
options := make([]map[string]string, len(item.Options))
for i, opt := range item.Options {
options[i] = map[string]string{"Key": opt, "Value": opt}
}

// Verify options are created correctly
assert.Len(t, options, len(item.Options))
for i, opt := range item.Options {
assert.Equal(t, opt, options[i]["Key"])
assert.Equal(t, opt, options[i]["Value"])
}
}
})

t.Run("BanMenu mapping logic from answers to BanInfo", func(t *testing.T) {
// Test the switch statement logic that maps answers to BanInfo fields

testCases := []struct {
name string
answers map[string]string
expectedInfo *cmd.BanInfo
}{
{
name: "all fields mapped correctly",
answers: map[string]string{
"workload": "container",
"behavior": "malicious",
"detection": "automated",
},
expectedInfo: &cmd.BanInfo{
WorkloadType: "container",
BehaviorClassification: "malicious",
DetectionMechanism: "automated",
},
},
{
name: "partial mapping - only workload",
answers: map[string]string{
"workload": "vm",
},
expectedInfo: &cmd.BanInfo{
WorkloadType: "vm",
BehaviorClassification: "",
DetectionMechanism: "",
},
},
{
name: "unknown kind ignored",
answers: map[string]string{
"workload": "container",
"unknown": "should-be-ignored",
},
expectedInfo: &cmd.BanInfo{
WorkloadType: "container",
BehaviorClassification: "",
DetectionMechanism: "",
},
},
{
name: "empty answers",
answers: map[string]string{},
expectedInfo: &cmd.BanInfo{
WorkloadType: "",
BehaviorClassification: "",
DetectionMechanism: "",
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Simulate the logic from lines 102-112
banInfo := &cmd.BanInfo{}

for kind, answer := range tc.answers {
switch kind {
case "workload":
banInfo.WorkloadType = answer
case "behavior":
banInfo.BehaviorClassification = answer
case "detection":
banInfo.DetectionMechanism = answer
}
}

assert.Equal(t, tc.expectedInfo.WorkloadType, banInfo.WorkloadType)
assert.Equal(t, tc.expectedInfo.BehaviorClassification, banInfo.BehaviorClassification)
assert.Equal(t, tc.expectedInfo.DetectionMechanism, banInfo.DetectionMechanism)
})
}
})
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same for these test cases, I don't see any "band command" logic that would be tested here.
We don't need to test the logic of json.Marshal per se - we expect that it works.
Also, we should not simulate the actual logic from the command - we should rather call the logic to test that it really works.
In other words, unit-tests should always test the "production" logic.

Comment thread pkg/cmd/ban_test.go Outdated

// TestBanInteractiveModeWithValidConfigMap tests the complete interactive flow
func TestBanWithValidConfigMap(t *testing.T) {
t.Run("Ban function interactive mode output messages (line 167)", func(t *testing.T) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test name looks a bit weird.

Comment thread pkg/cmd/ban.go Outdated
options[i] = huh.Option[string]{Key: opt, Value: opt}
}

form := huh.NewSelect[string]().

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

according to the huh README, it looks like it should be defined as part of the Form & Group:
https://github.com/charmbracelet/huh?tab=readme-ov-file#tutorial

Comment thread pkg/cmd/ban_test.go Outdated
Comment on lines +812 to +819
// This test will fail at the interactive part, but we can verify initial processing
// We expect it to get to the interactive menu and fail there

// when
err := cmd.Ban(ctx, userSignup.Name)

// then
// Should fail at the interactive part (huh.Select.Run()), but we can verify:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that you still have issues with testing the interactive mode. It's a bit tricky but there is a way to do that.
The problematic line is when it tries to "Run" the form:

form.Run()

it returns an error.
The solution would be moving this "run" call into a parameter of the Ban command - something like this:

type runFormFunc func(form *huh.Form) error 

func Ban(ctx *clicontext.CommandContext, runForm runFormFunc, args ...string) error {

so it can be used:

RunE: func(cmd *cobra.Command, args []string) error {
	term := ioutils.NewTerminal(cmd.InOrStdin, cmd.OutOrStdout)
	ctx := clicontext.NewCommandContext(term, client.DefaultNewClient)
	showForm := func(form *huh.Form) error {
		if err := form.Run(); err != nil {
			return fmt.Errorf("failed to show interactive menu: %w", err)
		}
		return nil
	}
	return Ban(ctx, showForm, args...)
},

but in unit tests you can do something else. There are a bunch of other methods you can call on the form:

form.Init() // to init the form
form.View() // to return the form as the string (for example to be printed out in case of debugging)

but what is more important, you can select between the options and fields via form.Update

form.Update(tea.KeyMsg{Type: tea.KeyDown}) // moves the selection one option down
form.Update(huh.NextField()) // goes to the next selection field

not sure if there is a better way of testing the huh forms. @xcoulon do you know?

@MatousJobanek MatousJobanek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some zombies 👻 (commented lines of code), can you please clean them up?

Comment thread pkg/cmd/ban.go Outdated
Comment on lines +139 to +141
if len(args) == 0 {
return fmt.Errorf("UserSignup name is required")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is actually not needed - the validation is already done by Cobra when you defined

Args: cobra.ExactArgs(1),
Suggested change
if len(args) == 0 {
return fmt.Errorf("UserSignup name is required")
}

Comment thread pkg/cmd/ban.go Outdated
cfgMapContent, err := getValuesFromConfigMap(ctx)

if err != nil || len(cfgMapContent) == 0 {
fmt.Printf("failed to load reasons from ConfigMap: %s", err)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fmt.Printf("failed to load reasons from ConfigMap: %s", err)
if err != nil {
ctx.Printlnf("failed to load reasons from ConfigMap %q: %s", configMapName, err)
} else {
ctx.Printlnf("the provided ConfigMap %q is empty", configMapName)
}

Comment thread pkg/cmd/ban.go Outdated
//err := runForm(form)
err := runForm(form)
if err != nil {
return fmt.Errorf("banning option could not be obtained: %w", err)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("banning option could not be obtained: %w", err)
return fmt.Errorf("ban reason could not be obtained: %w", err)

Comment thread pkg/cmd/ban.go Outdated
Comment on lines +96 to +98
fmt.Printf("\nYour selection:\n")
for kind, optionSelected := range answers {
fmt.Printf("- %s:\t%s\n", kind, optionSelected)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use the ctx *clicontext.CommandContext for printing out messages as we do it at other places

Comment thread pkg/cmd/ban_test.go Outdated
Comment on lines +299 to +362
func TestMenuStruct(t *testing.T) {
t.Run("JSON unmarshaling works correctly", func(t *testing.T) {
// given
jsonData := `[
{
"kind": "workload",
"description": "Select workload type",
"options": ["container", "vm"]
},
{
"kind": "behavior",
"description": "Select behavior classification",
"options": ["malicious", "suspicious"]
}
]`

// when
var menus []cmd.Menu
err := json.Unmarshal([]byte(jsonData), &menus)

// then
require.NoError(t, err)
assert.Len(t, menus, 2)

// Verify first menu
assert.Equal(t, "workload", menus[0].Kind)
assert.Equal(t, "Select workload type", menus[0].Description)
assert.Len(t, menus[0].Options, 2)
assert.Contains(t, menus[0].Options, "container")
assert.Contains(t, menus[0].Options, "vm")

// Verify second menu
assert.Equal(t, "behavior", menus[1].Kind)
assert.Equal(t, "Select behavior classification", menus[1].Description)
assert.Len(t, menus[1].Options, 2)
assert.Contains(t, menus[1].Options, "malicious")
assert.Contains(t, menus[1].Options, "suspicious")
})

t.Run("empty JSON array unmarshals correctly", func(t *testing.T) {
// given
jsonData := `[]`

// when
var menus []cmd.Menu
err := json.Unmarshal([]byte(jsonData), &menus)

// then
require.NoError(t, err)
assert.Empty(t, menus)
})

t.Run("malformed JSON returns error", func(t *testing.T) {
// given
jsonData := `[{invalid json`

// when
var menus []cmd.Menu
err := json.Unmarshal([]byte(jsonData), &menus)

// then
require.Error(t, err)
})
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as I mentioned in my previous review - these tests don't verify anything from the "ban command" logic, let's drop them.

Comment thread pkg/cmd/ban_test.go Outdated
Comment on lines +369 to +377
emptyConfigMap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "ban-reason-config",
Namespace: test.HostOperatorNs,
},
Data: map[string]string{
"menu.json": "[]", // Empty array
},
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the creation is used in the same way in the other tests, you could extract that to a helper function

func newBanReasonConfigMap(key, value string) *corev1.ConfigMap {
	return &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "ban-reason-config",
			Namespace: test.HostOperatorNs,
		},
		Data: map[string]string{
			key: value,
		},
	}
}

and then use it:

emptyConfigMap := newBanReasonConfigMap("Menu.json", "[]") // Empty array

and

configMapWithoutMenu := newBanReasonConfigMap("other-key", "some-value") // No Menu.json key

etc...

Comment thread pkg/cmd/ban_test.go Outdated

func TestBanCmdInteractiveMode(t *testing.T) {
t.Run("interactive mode with ConfigMap present", func(t *testing.T) {
t.Skip("Skipping interactive test - requires actual terminal interaction")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why skipping?

Comment thread pkg/cmd/ban_test.go Outdated

// then
require.NoError(t, err)
assert.Contains(t, term.Output(), "Checking for available reasons from ConfigMap...")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with my proposed changes in the other comment, you can check the actual reason why the select mode wasn't used

Suggested change
assert.Contains(t, term.Output(), "Checking for available reasons from ConfigMap...")
assert.Contains(t, term.Output(), "the provided ConfigMap \"ban-reason-config\" is empty")

@MatousJobanek MatousJobanek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. let's clean it up from all the zombies.
There is actually another thing to be updated - we need to add "read ConfigMap" permissions to the "ban-user" role here:

- kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: ban-user
labels:
provider: ksctl
rules:
- apiGroups:
- toolchain.dev.openshift.com
resources:
- "bannedusers"
verbs:
- "get"
- "list"
- "create"
- "delete"
- apiGroups:
- toolchain.dev.openshift.com
resources:
- "usersignups"
- "masteruserrecords"
verbs:
- "get"
- "list"

This template is used to generate the permissions based on ksctl-admins.yaml file as we have it in sandbox-sre

Comment thread pkg/cmd/ban.go Outdated
)

if err := runForm(form); err != nil {
return nil, err //fmt.Errorf("failed to show interactive menu: %w", err)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's clean up all these zombies 👻

Suggested change
return nil, err //fmt.Errorf("failed to show interactive menu: %w", err)
return nil, err

Comment thread pkg/cmd/ban.go Outdated

}

//return answers, nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//return answers, nil

Comment thread pkg/cmd/ban.go Outdated

var menus []Menu
if menuJSON, exists := configMap.Data[menuKey]; exists && menuJSON != "" {
//var menus []Menu

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//var menus []Menu

Comment thread pkg/cmd/ban.go Outdated

ctx.Printlnf("\nYour selection:\n")
for kind, optionSelected := range answers {
fmt.Printf("- %s:\t%s\n", kind, optionSelected)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fmt.Printf("- %s:\t%s\n", kind, optionSelected)
ctx.Printf("- %s:\t%s\n", kind, optionSelected)

Comment thread pkg/cmd/ban_test.go Outdated
Comment on lines +315 to +323
/* emptyConfigMap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "ban-reason-config",
Namespace: test.HostOperatorNs,
},
Data: map[string]string{
"menu.json": "[]", // Empty array
},
}*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙃

Suggested change
/* emptyConfigMap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "ban-reason-config",
Namespace: test.HostOperatorNs,
},
Data: map[string]string{
"menu.json": "[]", // Empty array
},
}*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and in other test-cases too

Comment thread pkg/cmd/ban_test.go Outdated
err := cmd.Ban(ctx, func(form *huh.Form) error {
form.Init()
form.Update(banReasonInput)
// form.View()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👻

Suggested change
// form.View()

@MatousJobanek MatousJobanek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 🥇 Great job 🥇 🚀

@jrosental jrosental merged commit 8c2a275 into kubesaw:master Oct 7, 2025
9 checks passed
@codecov

codecov Bot commented Oct 7, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.13043% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.74%. Comparing base (66afa66) to head (afec370).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
pkg/cmd/ban.go 77.98% 18 Missing and 6 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #123      +/-   ##
==========================================
+ Coverage   70.54%   70.74%   +0.20%     
==========================================
  Files          56       56              
  Lines        4013     4116     +103     
==========================================
+ Hits         2831     2912      +81     
- Misses        958      974      +16     
- Partials      224      230       +6     
Files with missing lines Coverage Δ
pkg/test/banneduser.go 100.00% <100.00%> (ø)
pkg/cmd/ban.go 75.90% <77.98%> (+3.17%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants