-
Notifications
You must be signed in to change notification settings - Fork 254
ROSAENG-61173 | test: complete delete cluster command coverage #3372
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,8 @@ import ( | |
| cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" | ||
| . "github.com/openshift-online/ocm-sdk-go/testing" | ||
|
|
||
| "github.com/openshift/rosa/pkg/arguments" | ||
| "github.com/openshift/rosa/pkg/interactive" | ||
| "github.com/openshift/rosa/pkg/test" | ||
| ) | ||
|
|
||
|
|
@@ -21,6 +23,258 @@ var _ = Describe("Delete cluster", func() { | |
| BeforeEach(func() { | ||
| t = test.NewTestRuntime() | ||
| clusterId = test.MockClusterID | ||
| args.bestEffort = false | ||
| args.watch = false | ||
| interactive.SetEnabled(false) | ||
| arguments.DisableRegionDeprecationWarning = false | ||
| }) | ||
|
|
||
| Context("runWithRuntime", func() { | ||
| var argv []string | ||
|
|
||
| BeforeEach(func() { | ||
| argv = []string{} | ||
| args.bestEffort = false | ||
| args.watch = false | ||
| confirmDelete = func(string, ...interface{}) bool { return true } | ||
| runUninstallLogs = func(string) {} | ||
| interactive.SetEnabled(false) | ||
| arguments.DisableRegionDeprecationWarning = false | ||
| }) | ||
|
|
||
| It("runs the non-STS happy path and prints the uninstall log hint", func() { | ||
| clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) { | ||
| c.State(cmv1.ClusterStateReady) | ||
| c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS())) | ||
| }) | ||
| t.SetCluster(clusterId, clusterReady) | ||
|
|
||
| statusBody := fmt.Sprintf(`{ | ||
| "kind": "ClusterStatus", | ||
| "id": "%s", | ||
| "state": "ready" | ||
| }`, clusterId) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody)) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON( | ||
| http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady}))) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, "")) | ||
|
|
||
| stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(stderr).To(BeEmpty()) | ||
| Expect(stdout).To(ContainSubstring("will start uninstalling")) | ||
| Expect(stdout).To(ContainSubstring("rosa logs uninstall -c")) | ||
| Expect(stdout).To(ContainSubstring("--watch")) | ||
| }) | ||
|
|
||
| It("returns cleanly when deletion is not confirmed", func() { | ||
| clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) { | ||
| c.State(cmv1.ClusterStateReady) | ||
| c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS())) | ||
| }) | ||
| t.SetCluster(clusterId, clusterReady) | ||
| confirmDelete = func(string, ...interface{}) bool { return false } | ||
|
|
||
| stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(stderr).To(BeEmpty()) | ||
| Expect(stdout).To(BeEmpty()) | ||
| }) | ||
|
|
||
| It("prints the best-effort warning and passes the flag through", func() { | ||
| clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) { | ||
| c.State(cmv1.ClusterStateReady) | ||
| c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS())) | ||
| }) | ||
| t.SetCluster(clusterId, clusterReady) | ||
| args.bestEffort = true | ||
|
|
||
| statusBody := fmt.Sprintf(`{ | ||
| "kind": "ClusterStatus", | ||
| "id": "%s", | ||
| "state": "ready" | ||
| }`, clusterId) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody)) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON( | ||
| http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady}))) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, "")) | ||
|
|
||
| stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(stderr).To(ContainSubstring("best effort")) | ||
| Expect(stderr).To(ContainSubstring("certain resources may be left behind")) | ||
| Expect(stdout).To(ContainSubstring("will start uninstalling")) | ||
| }) | ||
|
Comment on lines
+84
to
+107
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Assert that This test only verifies warning output and a successful response; the mock handler accepts the request regardless of the flag. Inspect the request in the handler or use a spy so the test fails unless As per path instructions, “Flag weak tests that only restate implementation or changes that weaken existing assertions.” 🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| It("prints STS cleanup guidance for clusters with operator roles", func() { | ||
| clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) { | ||
| c.State(cmv1.ClusterStateReady) | ||
| c.AWS(cmv1.NewAWS().STS( | ||
| cmv1.NewSTS(). | ||
| RoleARN("arn:aws:iam::123456789012:role/Installer"). | ||
| OIDCEndpointURL("https://oidc.example.com"). | ||
| OperatorRolePrefix("my-prefix"). | ||
| OperatorIAMRoles( | ||
| cmv1.NewOperatorIAMRole(). | ||
| Name("ebs-cloud-credentials"). | ||
| Namespace("openshift-cluster-csi-drivers"). | ||
| RoleARN("arn:aws:iam::123456789012:role/op-role"), | ||
| ), | ||
| )) | ||
| }) | ||
| t.SetCluster(clusterId, clusterReady) | ||
|
|
||
| statusBody := fmt.Sprintf(`{ | ||
| "kind": "ClusterStatus", | ||
| "id": "%s", | ||
| "state": "ready" | ||
| }`, clusterId) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody)) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON( | ||
| http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady}))) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, "")) | ||
|
|
||
| stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(stderr).To(BeEmpty()) | ||
| Expect(stdout).To(ContainSubstring("Operator IAM Roles:")) | ||
| Expect(stdout).To(ContainSubstring("arn:aws:iam::123456789012:role/op-role")) | ||
| Expect(stdout).To(ContainSubstring("OIDC Provider : https://oidc.example.com")) | ||
| Expect(stdout).To(ContainSubstring("rosa delete operator-roles -c")) | ||
| Expect(stdout).To(ContainSubstring("rosa delete oidc-provider -c")) | ||
| }) | ||
|
|
||
| It("prints STS cleanup guidance without operator role output when none remain", func() { | ||
| clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) { | ||
| c.State(cmv1.ClusterStateReady) | ||
| c.AWS(cmv1.NewAWS().STS( | ||
| cmv1.NewSTS(). | ||
| RoleARN("arn:aws:iam::123456789012:role/Installer"). | ||
| OIDCEndpointURL("https://oidc-no-roles.example.com"), | ||
| )) | ||
| }) | ||
| t.SetCluster(clusterId, clusterReady) | ||
|
|
||
| statusBody := fmt.Sprintf(`{ | ||
| "kind": "ClusterStatus", | ||
| "id": "%s", | ||
| "state": "ready" | ||
| }`, clusterId) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody)) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON( | ||
| http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady}))) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, "")) | ||
|
|
||
| stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(stderr).To(BeEmpty()) | ||
| Expect(stdout).NotTo(ContainSubstring("Operator IAM Roles:")) | ||
| Expect(stdout).To(ContainSubstring("OIDC Provider : https://oidc-no-roles.example.com")) | ||
| Expect(stdout).To(ContainSubstring("rosa delete operator-roles -c")) | ||
| Expect(stdout).To(ContainSubstring("rosa delete oidc-provider -c")) | ||
| }) | ||
|
|
||
| It("runs uninstall logs when watch is enabled and restores the deprecation warning flag", func() { | ||
| clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) { | ||
| c.State(cmv1.ClusterStateReady) | ||
| c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS())) | ||
| }) | ||
| t.SetCluster(clusterId, clusterReady) | ||
| args.watch = true | ||
|
|
||
| statusBody := fmt.Sprintf(`{ | ||
| "kind": "ClusterStatus", | ||
| "id": "%s", | ||
| "state": "ready" | ||
| }`, clusterId) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody)) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON( | ||
| http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady}))) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, "")) | ||
|
|
||
| var watchedClusterKey string | ||
| var sawDisabledWarning bool | ||
| runUninstallLogs = func(clusterKey string) { | ||
| watchedClusterKey = clusterKey | ||
| sawDisabledWarning = arguments.DisableRegionDeprecationWarning | ||
| } | ||
|
|
||
| stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(stderr).To(BeEmpty()) | ||
| Expect(stdout).To(ContainSubstring("will start uninstalling")) | ||
| Expect(watchedClusterKey).To(Equal(clusterId)) | ||
| Expect(sawDisabledWarning).To(BeTrue()) | ||
| Expect(arguments.DisableRegionDeprecationWarning).To(BeFalse()) | ||
| Expect(stdout).NotTo(ContainSubstring("rosa logs uninstall -c")) | ||
| }) | ||
|
|
||
| It("returns the already-uninstalling path through the command wrapper", func() { | ||
| clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) { | ||
| c.State(cmv1.ClusterStateReady) | ||
| c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS())) | ||
| }) | ||
| t.SetCluster(clusterId, clusterReady) | ||
|
|
||
| statusBody := fmt.Sprintf(`{ | ||
| "kind": "ClusterStatus", | ||
| "id": "%s", | ||
| "state": "uninstalling" | ||
| }`, clusterId) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody)) | ||
|
|
||
| stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(stderr).To(BeEmpty()) | ||
| Expect(stdout).To(ContainSubstring("already uninstalling")) | ||
| Expect(stdout).To(ContainSubstring("rosa logs uninstall -c")) | ||
| }) | ||
|
|
||
| It("returns an error from GetClusterState through the command wrapper", func() { | ||
| clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) { | ||
| c.State(cmv1.ClusterStateReady) | ||
| c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS())) | ||
| }) | ||
| t.SetCluster(clusterId, clusterReady) | ||
|
|
||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusInternalServerError, "")) | ||
|
|
||
| stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv) | ||
| Expect(err).To(HaveOccurred()) | ||
| Expect(stderr).To(BeEmpty()) | ||
| Expect(stdout).To(BeEmpty()) | ||
| Expect(err.Error()).To(ContainSubstring("expected response content type")) | ||
| }) | ||
|
|
||
| It("returns a delete error through the command wrapper", func() { | ||
| clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) { | ||
| c.State(cmv1.ClusterStateReady) | ||
| c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS())) | ||
| }) | ||
| t.SetCluster(clusterId, clusterReady) | ||
|
|
||
| statusBody := fmt.Sprintf(`{ | ||
| "kind": "ClusterStatus", | ||
| "id": "%s", | ||
| "state": "ready" | ||
| }`, clusterId) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody)) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON( | ||
| http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady}))) | ||
| t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusForbidden, `{ | ||
| "kind": "Error", | ||
| "id": "403", | ||
| "href": "/api/clusters_mgmt/v1/errors/403", | ||
| "code": "CLUSTERS-MGMT-403", | ||
| "reason": "forbidden" | ||
| }`)) | ||
|
|
||
| stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv) | ||
| Expect(err).To(HaveOccurred()) | ||
| Expect(stderr).To(BeEmpty()) | ||
| Expect(stdout).To(BeEmpty()) | ||
| Expect(err.Error()).To(ContainSubstring("forbidden")) | ||
| }) | ||
| }) | ||
|
|
||
| Context("handleClusterDelete", func() { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Wrap deletion errors with operation context.
Returning the raw SDK error makes the command failure harder to diagnose. Preserve it with
%wwhile identifying the failed cluster operation.Proposed fix
err := handleClusterDelete(r, cluster, clusterKey, args.bestEffort) if err != nil { - return err + return fmt.Errorf("failed to delete cluster %q: %w", clusterKey, err) }As per coding guidelines, “Wrap returned errors with context using
%w; do not drop the original error.”📝 Committable suggestion
🤖 Prompt for AI Agents
Source: Coding guidelines