Skip to content

Commit 8c7074e

Browse files
authored
Merge pull request #3631 from dashanji/fix-ginkgo-test
✨ (go/v4): Add tests for the webhooks and controllers which are created so that suite_test are no longer ignored
2 parents c8a7cc5 + fc064f5 commit 8c7074e

File tree

46 files changed

+2896
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2896
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright 2023 The Kubernetes authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1
18+
19+
import (
20+
. "github.com/onsi/ginkgo/v2"
21+
)
22+
23+
var _ = Describe("CronJob Webhook", func() {
24+
25+
Context("When creating CronJob under Defaulting Webhook", func() {
26+
It("Should fill in the default value if a required field is empty", func() {
27+
28+
// TODO(user): Add your logic here
29+
30+
})
31+
})
32+
33+
Context("When creating CronJob under Validating Webhook", func() {
34+
It("Should deny if a required field is empty", func() {
35+
36+
// TODO(user): Add your logic here
37+
38+
})
39+
40+
It("Should admit if all required fields are provided", func() {
41+
42+
// TODO(user): Add your logic here
43+
44+
})
45+
})
46+
47+
})

pkg/plugins/golang/deploy-image/v1alpha1/scaffolds/internal/templates/controllers/controller-test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ func (f *ControllerTest) SetTemplateDefaults() error {
6464
f.PackageName = "controllers"
6565
}
6666

67+
f.IfExistsAction = machinery.OverwriteFile
68+
6769
log.Println("creating import for %", f.Resource.Path)
6870
f.TemplateBody = controllerTestTemplate
6971

pkg/plugins/golang/v4/scaffolds/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ func (s *apiScaffolder) Scaffold() error {
104104
if err := scaffold.Execute(
105105
&controllers.SuiteTest{Force: s.force, K8SVersion: EnvtestK8SVersion},
106106
&controllers.Controller{ControllerRuntimeVersion: ControllerRuntimeVersion, Force: s.force},
107+
&controllers.ControllerTest{Force: s.force, DoAPI: doAPI},
107108
); err != nil {
108109
return fmt.Errorf("error scaffolding controller: %v", err)
109110
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
Copyright 2022 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package api
18+
19+
import (
20+
"fmt"
21+
"path/filepath"
22+
"strings"
23+
24+
log "github.com/sirupsen/logrus"
25+
26+
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
27+
)
28+
29+
var _ machinery.Template = &WebhookTest{}
30+
31+
// WebhookTest scaffolds the file that sets up the webhook unit tests
32+
type WebhookTest struct { // nolint:maligned
33+
machinery.TemplateMixin
34+
machinery.MultiGroupMixin
35+
machinery.BoilerplateMixin
36+
machinery.ResourceMixin
37+
38+
Force bool
39+
}
40+
41+
// SetTemplateDefaults implements file.Template
42+
func (f *WebhookTest) SetTemplateDefaults() error {
43+
if f.Path == "" {
44+
if f.MultiGroup && f.Resource.Group != "" {
45+
f.Path = filepath.Join("api", "%[group]", "%[version]", "%[kind]_webhook_test.go")
46+
} else {
47+
f.Path = filepath.Join("api", "%[version]", "%[kind]_webhook_test.go")
48+
}
49+
}
50+
f.Path = f.Resource.Replacer().Replace(f.Path)
51+
log.Println(f.Path)
52+
53+
webhookTestTemplate := webhookTestTemplate
54+
templates := make([]string, 0)
55+
if f.Resource.HasDefaultingWebhook() {
56+
templates = append(templates, defaultWebhookTestTemplate)
57+
}
58+
if f.Resource.HasValidationWebhook() {
59+
templates = append(templates, validateWebhookTestTemplate)
60+
}
61+
if f.Resource.HasConversionWebhook() {
62+
templates = append(templates, conversionWebhookTestTemplate)
63+
}
64+
f.TemplateBody = fmt.Sprintf(webhookTestTemplate, strings.Join(templates, "\n"))
65+
66+
if f.Force {
67+
f.IfExistsAction = machinery.OverwriteFile
68+
}
69+
70+
return nil
71+
}
72+
73+
const webhookTestTemplate = `{{ .Boilerplate }}
74+
75+
package {{ .Resource.Version }}
76+
77+
import (
78+
. "github.com/onsi/ginkgo/v2"
79+
)
80+
81+
var _ = Describe("{{ .Resource.Kind }} Webhook", func() {
82+
%s
83+
})
84+
`
85+
86+
const conversionWebhookTestTemplate = `
87+
Context("When creating {{ .Resource.Kind }} under Conversion Webhook", func() {
88+
It("Should get the converted version of {{ .Resource.Kind }}" , func() {
89+
90+
// TODO(user): Add your logic here
91+
92+
})
93+
})
94+
`
95+
96+
const validateWebhookTestTemplate = `
97+
Context("When creating {{ .Resource.Kind }} under Validating Webhook", func() {
98+
It("Should deny if a required field is empty", func() {
99+
100+
// TODO(user): Add your logic here
101+
102+
})
103+
104+
It("Should admit if all required fields are provided", func() {
105+
106+
// TODO(user): Add your logic here
107+
108+
})
109+
})
110+
`
111+
112+
const defaultWebhookTestTemplate = `
113+
Context("When creating {{ .Resource.Kind }} under Defaulting Webhook", func() {
114+
It("Should fill in the default value if a required field is empty", func() {
115+
116+
// TODO(user): Add your logic here
117+
118+
})
119+
})
120+
`
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
Copyright 2022 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package controllers
18+
19+
import (
20+
"path/filepath"
21+
22+
log "github.com/sirupsen/logrus"
23+
24+
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
25+
)
26+
27+
var _ machinery.Template = &ControllerTest{}
28+
29+
// ControllerTest scaffolds the file that sets up the controller unit tests
30+
// nolint:maligned
31+
type ControllerTest struct {
32+
machinery.TemplateMixin
33+
machinery.MultiGroupMixin
34+
machinery.BoilerplateMixin
35+
machinery.ResourceMixin
36+
37+
Force bool
38+
39+
DoAPI bool
40+
}
41+
42+
// SetTemplateDefaults implements file.Template
43+
func (f *ControllerTest) SetTemplateDefaults() error {
44+
if f.Path == "" {
45+
if f.MultiGroup && f.Resource.Group != "" {
46+
f.Path = filepath.Join("internal", "controller", "%[group]", "%[kind]_controller_test.go")
47+
} else {
48+
f.Path = filepath.Join("internal", "controller", "%[kind]_controller_test.go")
49+
}
50+
}
51+
52+
f.Path = f.Resource.Replacer().Replace(f.Path)
53+
log.Println(f.Path)
54+
55+
f.TemplateBody = controllerTestTemplate
56+
57+
if f.Force {
58+
f.IfExistsAction = machinery.OverwriteFile
59+
}
60+
61+
return nil
62+
}
63+
64+
const controllerTestTemplate = `{{ .Boilerplate }}
65+
66+
{{if and .MultiGroup .Resource.Group }}
67+
package {{ .Resource.PackageName }}
68+
{{else}}
69+
package controller
70+
{{end}}
71+
72+
import (
73+
{{ if .DoAPI -}}
74+
"context"
75+
{{- end }}
76+
. "github.com/onsi/ginkgo/v2"
77+
{{ if .DoAPI -}}
78+
79+
. "github.com/onsi/gomega"
80+
"k8s.io/apimachinery/pkg/api/errors"
81+
"k8s.io/apimachinery/pkg/types"
82+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
83+
84+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
85+
{{ if not (isEmptyStr .Resource.Path) -}}
86+
{{ .Resource.ImportAlias }} "{{ .Resource.Path }}"
87+
{{- end }}
88+
{{- end }}
89+
)
90+
91+
var _ = Describe("{{ .Resource.Kind }} Controller", func() {
92+
Context("When reconciling a resource", func() {
93+
{{ if .DoAPI -}}
94+
const resourceName = "test-resource"
95+
96+
ctx := context.Background()
97+
98+
typeNamespacedName := types.NamespacedName{
99+
Name: resourceName,
100+
Namespace: "default", // TODO(user):Modify as needed
101+
}
102+
{{ lower .Resource.Kind }} := &{{ .Resource.ImportAlias }}.{{ .Resource.Kind }}{}
103+
104+
BeforeEach(func() {
105+
By("creating the custom resource for the Kind {{ .Resource.Kind }}")
106+
err := k8sClient.Get(ctx, typeNamespacedName, {{ lower .Resource.Kind }})
107+
if err != nil && errors.IsNotFound(err) {
108+
resource := &{{ .Resource.ImportAlias }}.{{ .Resource.Kind }}{
109+
ObjectMeta: metav1.ObjectMeta{
110+
Name: resourceName,
111+
Namespace: "default",
112+
},
113+
// TODO(user): Specify other spec details if needed.
114+
}
115+
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
116+
}
117+
})
118+
119+
AfterEach(func() {
120+
// TODO(user): Cleanup logic after each test, like removing the resource instance.
121+
resource := &{{ .Resource.ImportAlias }}.{{ .Resource.Kind }}{}
122+
err := k8sClient.Get(ctx, typeNamespacedName, resource)
123+
Expect(err).NotTo(HaveOccurred())
124+
125+
By("Cleanup the specific resource instance {{ .Resource.Kind }}")
126+
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
127+
})
128+
{{- end }}
129+
It("should successfully reconcile the resource", func() {
130+
{{ if .DoAPI -}}
131+
By("Reconciling the created resource")
132+
controllerReconciler := &{{ .Resource.Kind }}Reconciler{
133+
Client: k8sClient,
134+
Scheme: k8sClient.Scheme(),
135+
}
136+
137+
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
138+
NamespacedName: typeNamespacedName,
139+
})
140+
Expect(err).NotTo(HaveOccurred())
141+
{{- end }}
142+
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
143+
// Example: If you expect a certain status condition after reconciliation, verify it here.
144+
})
145+
})
146+
})
147+
`

pkg/plugins/golang/v4/scaffolds/webhook.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ func (s *webhookScaffolder) Scaffold() error {
8787
if err := scaffold.Execute(
8888
&api.Webhook{Force: s.force},
8989
&templates.MainUpdater{WireWebhook: true},
90+
&api.WebhookTest{Force: s.force},
9091
); err != nil {
9192
return err
9293
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright 2023 The Kubernetes authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1
18+
19+
import (
20+
. "github.com/onsi/ginkgo/v2"
21+
)
22+
23+
var _ = Describe("Captain Webhook", func() {
24+
25+
Context("When creating Captain under Defaulting Webhook", func() {
26+
It("Should fill in the default value if a required field is empty", func() {
27+
28+
// TODO(user): Add your logic here
29+
30+
})
31+
})
32+
33+
Context("When creating Captain under Validating Webhook", func() {
34+
It("Should deny if a required field is empty", func() {
35+
36+
// TODO(user): Add your logic here
37+
38+
})
39+
40+
It("Should admit if all required fields are provided", func() {
41+
42+
// TODO(user): Add your logic here
43+
44+
})
45+
})
46+
47+
})

0 commit comments

Comments
 (0)