Skip to content

🌱 (cli; alpha commands): Add unit tests for alpha commands [WiP] #4931

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ require (
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/h2non/gock v1.2.0 // indirect
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
Expand All @@ -27,6 +31,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
Expand Down
44 changes: 44 additions & 0 deletions pkg/cli/alpha/command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Copy link
Member

Choose a reason for hiding this comment

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

Just more one nit: Could we aquash all commits we should have 1 commit per PR

Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package alpha

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

// Figuring out ways to test run these tests similar to existing.
// Currently unable to run without this on VSCode. Will remove once done
func TestCommand(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "alpha")
}

var _ = Describe("NewScaffoldCommand", func() {

When("NewScaffoldCommand", func() {
It("Testing the NewScaffoldCommand", func() {
cmd := NewScaffoldCommand()
Copy link
Member

Choose a reason for hiding this comment

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

The alpha update command internal code it might be nice if we could cover something
However, if you find it too complicated, I suggest skipping pkg/cli/alpha and proceeding to the following directory. ;-)
We have a lot inside that we can add more to, and it is not alpha/experimental staff.

Copy link
Author

Choose a reason for hiding this comment

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

Sure, I am working on the update internal code.

Copy link
Member

Choose a reason for hiding this comment

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

Just a tip:

You can validate the code locally with:

  • make lint -> for linter check
  • you can run make lint-fix : to fix automaticly issues ( not all is fixed but is very helpful )

Copy link
Member

Choose a reason for hiding this comment

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

When you finish this one please remove WIP from the title so that we are aware of and we can review/get merged get done ;-)

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi @cmallikarjunah, I'm currently working on alpha internal update package, are you working on the same or is it different?. I wanted to clarify so that we dont duplicate the work :-)

https://github.com/kubernetes-sigs/kubebuilder/tree/master/pkg/cli/alpha/internal/update

Copy link
Author

@cmallikarjunah cmallikarjunah Jul 18, 2025

Choose a reason for hiding this comment

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

Ya it's the same @mayuka-c.
Was working on functions of prepare.go yesterday. Planning to continue.
There was no WiP or status for it so I picked it upon Camila's suggestion.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, no worries, you can continue

Expect(cmd).NotTo(BeNil())
Expect(cmd.Use).NotTo(Equal(""))
Expect(cmd.Use).To(ContainSubstring("generate"))
Expect(cmd.Short).NotTo(Equal(""))
Expect(cmd.Short).To(ContainSubstring("Re-scaffold a Kubebuilder project from its PROJECT file"))
Expect(cmd.Example).NotTo(Equal(""))
Expect(cmd.Example).To(ContainSubstring("kubebuilder alpha generate"))
})
})
})
2 changes: 1 addition & 1 deletion pkg/cli/alpha/internal/update/prepare.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (opts *Update) defineFromVersion(config store.Store) (string, error) {

func (opts *Update) defineToVersion() string {
if len(opts.ToVersion) != 0 {
if !strings.HasPrefix(opts.FromVersion, "v") {
if !strings.HasPrefix(opts.ToVersion, "v") {
Copy link
Member

Choose a reason for hiding this comment

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

Could you please rebase with master?
So that we have no this change it is fixed and merged now 👍

Copy link
Author

Choose a reason for hiding this comment

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

Sure Camila, will do

Copy link
Author

Choose a reason for hiding this comment

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

@camilamacedo86 I have rebased. But the code in master still checks prefix for opts.FromVersion in function defineToVersion(). Can you re-check and confirm if this change is not needed?

return "v" + opts.ToVersion
}
return opts.ToVersion
Expand Down
186 changes: 186 additions & 0 deletions pkg/cli/alpha/internal/update/prepare_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
Copyright 2025 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package update

import (
"os"
"path/filepath"
"testing"

"github.com/h2non/gock"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"sigs.k8s.io/kubebuilder/v4/pkg/cli/alpha/internal/common"
"sigs.k8s.io/kubebuilder/v4/pkg/config"
"sigs.k8s.io/kubebuilder/v4/pkg/config/store/yaml"
v3 "sigs.k8s.io/kubebuilder/v4/pkg/config/v3"
)

func TestCommand(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "update")
}

var _ = Describe("Prepare for internal update", func() {
var (
tmpDir string
workDir string
projectFile string
)

BeforeEach(func() {
tmpDir, _ = os.MkdirTemp("", "kubebuilder-prepare-test")
workDir, _ = os.Getwd()
os.Chdir(tmpDir)
projectFile = filepath.Join(tmpDir, yaml.DefaultPath)

config.Register(config.Version{Number: 3}, func() config.Config {
return &v3.Cfg{Version: config.Version{Number: 3}, CliVersion: "1.0.0"}
})

gock.New("https://api.github.com").
Get("/repos/kubernetes-sigs/kubebuilder/releases/latest").
Reply(200).
JSON(map[string]string{
"tag_name": "v1.1.0",
})
})

AfterEach(func() {
os.Chdir(workDir)
os.RemoveAll(tmpDir)
defer gock.Off()
})

Context("Prepare", func() {
DescribeTable("should succeed for valid options",
func(options *Update) {

const version = `version: "3"`
Expect(os.WriteFile(projectFile, []byte(version), 0o644)).To(Succeed())

result := options.Prepare()
Expect(result).To(BeNil())
Expect(options.Prepare()).To(Succeed())
Expect(options.FromVersion).To(Equal("v1.0.0"))
Expect(options.ToVersion).To(Equal("v1.1.0"))
},
Entry("options", &Update{FromVersion: "v1.0.0", ToVersion: "v1.1.0", FromBranch: "test"}),
Entry("options", &Update{FromVersion: "1.0.0", ToVersion: "1.1.0", FromBranch: "test"}),
Entry("options", &Update{FromVersion: "v1.0.0", ToVersion: "v1.1.0"}),
Entry("options", &Update{FromVersion: "1.0.0"}),
Entry("options", &Update{ToVersion: "1.1.0"}),
Entry("options", &Update{}),
Copy link
Member

@camilamacedo86 camilamacedo86 Jul 20, 2025

Choose a reason for hiding this comment

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

Can we reduce the number of test cases here?

Do we really need all of them to cover the intended scenarios?
It seems like we’re primarily validating support for:

  • FromVersion and ToVersion with and without the v prefix
  • Presence or absence of FromBranch

We’re not actually validating SemVer parsing here, so duplicating every combination may be unnecessary.
Maybe we could simplify this to focus on meaningful permutations only?

Copy link
Author

Choose a reason for hiding this comment

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

Got it. Will remove those.

)

DescribeTable("Should fail to prepare if project path is undetermined",
func(options *Update) {
result := options.Prepare()
Expect(result).NotTo(BeNil())
Expect(result.Error()).Should(ContainSubstring("failed to determine project path"))
},
Entry("options", &Update{FromVersion: "v1.0.0", ToVersion: "v1.1.0", FromBranch: "test"}),
)

DescribeTable("Should fail if PROJECT config could not be loaded",
func(options *Update) {
const version = ""
Expect(os.WriteFile(projectFile, []byte(version), 0o644)).To(Succeed())

result := options.Prepare()
Expect(result).NotTo(BeNil())
Expect(result.Error()).Should(ContainSubstring("failed to load PROJECT config"))
},
Entry("options", &Update{FromVersion: "v1.0.0", ToVersion: "v1.1.0", FromBranch: "test"}),
)

DescribeTable("Should fail if FromVersion cannot be determined",
func(options *Update) {

config.Register(config.Version{Number: 3}, func() config.Config {
return &v3.Cfg{Version: config.Version{Number: 3}}
})

const version = `version: "3"`
Expect(os.WriteFile(projectFile, []byte(version), 0o644)).To(Succeed())

// TODO: Update test. err is Nil as default branch in Prepare() is being hardcoded to "main".
// Hence, the defineFromVersion condition will never fail.
// result := options.Prepare()
// Expect(result).NotTo(BeNil())
// Expect(result.Error()).Should(ContainSubstring("failed to determine the version"))
Expect(options.FromVersion).To(BeEquivalentTo(""))
},
Entry("options", &Update{}),
)
})

Context("DefineFromVersion", func() {

DescribeTable("Should succeed when branch or CliVersion in Project config is present",
func(options *Update) {

const version = `version: "3"`
Expect(os.WriteFile(projectFile, []byte(version), 0o644)).To(Succeed())

config, err := common.LoadProjectConfig(tmpDir)
Expect(err).NotTo(HaveOccurred())
fromVersion, err := options.defineFromVersion(config)
Expect(err).To(BeNil())
Expect(fromVersion).To(BeEquivalentTo("v1.0.0"))
},
Entry("options", &Update{FromVersion: ""}),
Entry("options", &Update{FromBranch: "test"}),
Entry("options", &Update{FromVersion: "1.0.0", FromBranch: "test"}),
)
DescribeTable("Should fail when branch and CliVersion in Project config are absent",
func(options *Update) {

config.Register(config.Version{Number: 3}, func() config.Config {
return &v3.Cfg{Version: config.Version{Number: 3}}
})

const version = `version: "3"`
Expect(os.WriteFile(projectFile, []byte(version), 0o644)).To(Succeed())

config, err := common.LoadProjectConfig(tmpDir)
Expect(err).NotTo(HaveOccurred())
fromVersion, err := options.defineFromVersion(config)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("no version specified in PROJECT file"))
Expect(fromVersion).To(Equal(""))
},
Entry("options", &Update{FromVersion: ""}),
Entry("options", &Update{FromVersion: "v1.0.0"}),
)
})

Context("DefineToVersion", func() {

DescribeTable("Should succeed.",
func(options *Update) {
toVersion := options.defineToVersion()
Expect(toVersion).To(BeEquivalentTo("v1.1.0"))
},
Entry("options", &Update{ToVersion: "1.1.0"}),
Entry("options", &Update{ToVersion: "v1.1.0"}),
Entry("options", &Update{}),
)

})

})
33 changes: 33 additions & 0 deletions pkg/cli/alpha/update_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package alpha

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("NewUpdateCommand", func() {

When("NewUpdateCommand", func() {
It("Testing the NewUpdateCommand", func() {
cmd := NewUpdateCommand()
Expect(cmd).NotTo(BeNil())
Expect(cmd.Use).NotTo(Equal(""))
Expect(cmd.Use).To(ContainSubstring("update"))
Expect(cmd.Short).NotTo(Equal(""))
Expect(cmd.Short).To(ContainSubstring("Update a Kubebuilder project to a newer version"))
})
})
})