Skip to content

Commit 8a07599

Browse files
author
Jelle Dijkstra
committed
Validation test setup
1 parent 6335cfe commit 8a07599

10 files changed

+163
-477
lines changed

internal/webhook/v3/atom_webhook_test.go

Lines changed: 161 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,14 @@ SOFTWARE.
2525
package v3
2626

2727
import (
28-
"errors"
28+
"context"
29+
"fmt"
30+
v1 "github.com/pdok/smooth-operator/api/v1"
31+
"github.com/pdok/smooth-operator/model"
32+
apierrors "k8s.io/apimachinery/pkg/api/errors"
33+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34+
"k8s.io/apimachinery/pkg/runtime/schema"
35+
"k8s.io/apimachinery/pkg/util/validation/field"
2936
"os"
3037

3138
. "github.com/onsi/ginkgo/v2" //nolint:revive // ginkgo bdd
@@ -37,9 +44,11 @@ import (
3744

3845
var _ = Describe("Atom Webhook", func() {
3946
var (
40-
obj *pdoknlv3.Atom
41-
oldObj *pdoknlv3.Atom
42-
validator AtomCustomValidator
47+
obj *pdoknlv3.Atom
48+
oldObj *pdoknlv3.Atom
49+
validator AtomCustomValidator
50+
labelsPath *field.Path
51+
servicePath *field.Path
4352
)
4453

4554
BeforeEach(func() {
@@ -53,6 +62,8 @@ var _ = Describe("Atom Webhook", func() {
5362
Expect(obj).NotTo(BeNil(), "Expected obj to be initialized")
5463
// TODO (user): Add any setup logic common to all tests
5564

65+
labelsPath = field.NewPath("metadata").Child("labels")
66+
servicePath = field.NewPath("spec").Child("service")
5667
})
5768

5869
AfterEach(func() {
@@ -65,7 +76,11 @@ var _ = Describe("Atom Webhook", func() {
6576
})
6677

6778
It("Should deny creation if no labels are available", func() {
68-
testCreate(validator, "invalid/no-labels.yaml", errors.New("Atom.pdok.nl \"asis-readonly-prod\" is invalid: metadata.labels: Required value: can't be empty"))
79+
testCreate(validator, "invalid/no-labels.yaml", func(_ *pdoknlv3.Atom) field.ErrorList {
80+
return field.ErrorList{
81+
field.Required(labelsPath, "can't be empty"),
82+
}
83+
})
6984
})
7085

7186
It("Should create atom with ingressRouteUrls that contains the service baseUrl", func() {
@@ -76,96 +91,205 @@ var _ = Describe("Atom Webhook", func() {
7691
testCreate(
7792
validator,
7893
"invalid/ingress-route-urls-missing-baseurl.yaml",
79-
errors.New("Atom.pdok.nl \"ingress-route-urls\" is invalid: spec.ingressRouteUrls: Invalid value: \"[{http://test.com/path}]\": must contain baseURL: http://localhost:32788/rvo/wetlands/atom"),
94+
func(atom *pdoknlv3.Atom) field.ErrorList {
95+
return field.ErrorList{
96+
field.Invalid(field.NewPath("spec").Child("ingressRouteUrls"), fmt.Sprint(atom.Spec.IngressRouteURLs), "must contain baseURL: "+atom.Spec.Service.BaseURL.String()),
97+
}
98+
},
99+
)
100+
})
101+
102+
It("Should deny creation if spec.service.ownerReference is not found", func() {
103+
testCreate(
104+
validator,
105+
"invalid/unknown-ownerref.yaml",
106+
func(atom *pdoknlv3.Atom) field.ErrorList {
107+
return field.ErrorList{
108+
field.NotFound(servicePath.Child("ownerInfoRef"), atom.Spec.Service.OwnerInfoRef),
109+
}
110+
},
111+
)
112+
})
113+
114+
It("Should deny creation if spec.service.ownerReference does not contain Atom info", func() {
115+
// Create the OwnerInfo
116+
o := v1.OwnerInfo{
117+
ObjectMeta: metav1.ObjectMeta{
118+
Name: "random",
119+
Namespace: "services",
120+
},
121+
}
122+
123+
err := validator.Client.Create(context.TODO(), &o)
124+
Expect(err).To(Not(HaveOccurred()))
125+
126+
testCreate(
127+
validator,
128+
"invalid/unknown-ownerref.yaml",
129+
func(atom *pdoknlv3.Atom) field.ErrorList {
130+
return field.ErrorList{
131+
field.Required(servicePath.Child("ownerInfoRef"), "spec.Atom missing in random"),
132+
}
133+
},
80134
)
81135
})
82136

83137
It("Should create and update atom without errors or warnings", func() {
84-
testUpdate(validator, "valid/minimal.yaml", "valid/minimal-service-title-changed.yaml", nil)
138+
testUpdate(
139+
validator,
140+
"valid/minimal.yaml",
141+
func(atom *pdoknlv3.Atom) {
142+
atom.Spec.Service.Title = "New service title"
143+
},
144+
nil,
145+
)
85146
})
86147

87148
It("Should deny update atom with error label names cannot be added or deleted", func() {
88149
testUpdate(
89150
validator,
90151
"valid/minimal.yaml",
91-
"invalid/minimal-immutable-labels-key-change.yaml",
92-
errors.New("Atom.pdok.nl \"asis-readonly-prod\" is invalid: [metadata.labels.pdok.nl/dataset-id: Required value: labels cannot be removed, metadata.labels.pdok.nl/dataset-idsssssssss: Forbidden: new labels cannot be added]"),
152+
func(atom *pdoknlv3.Atom) {
153+
labels := atom.GetLabels()
154+
labels["pdok.nl/dataset-idsssssssss"] = labels["pdok.nl/dataset-ids"]
155+
delete(labels, "pdok.nl/dataset-ids")
156+
atom.Labels = labels
157+
},
158+
func(_, _ *pdoknlv3.Atom) field.ErrorList {
159+
return field.ErrorList{
160+
field.Forbidden(labelsPath.Child("pdok.nl/dataset-idsssssssss"), "new labels cannot be added"),
161+
}
162+
},
93163
)
94164
})
95165

96166
It("Should deny update atom with error label names are immutable", func() {
97167
testUpdate(
98168
validator,
99169
"valid/minimal.yaml",
100-
"invalid/minimal-immutable-labels-value-change.yaml",
101-
errors.New("Atom.pdok.nl \"asis-readonly-prod\" is invalid: metadata.labels.pdok.nl/dataset-id: Invalid value: \"wetlands-changed\": immutable: should be: wetlands"),
170+
func(atom *pdoknlv3.Atom) {
171+
labels := atom.GetLabels()
172+
labels["pdok.nl/dataset-id"] = "wetlands-changed"
173+
atom.Labels = labels
174+
},
175+
func(old, _ *pdoknlv3.Atom) field.ErrorList {
176+
return field.ErrorList{
177+
field.Invalid(labelsPath.Child("pdok.nl/dataset-id"), "wetlands-changed", "immutable: should be: "+old.Labels["pdok.nl/dataset-id"]),
178+
}
179+
},
102180
)
103181
})
104182

105183
It("Should deny update atom with error URL are immutable", func() {
106184
testUpdate(
107185
validator,
108186
"valid/minimal.yaml",
109-
"invalid/minimal-immutable-url.yaml", errors.New("Atom.pdok.nl \"asis-readonly-prod\" is invalid: spec.service.baseUrl: Forbidden: is immutable"),
187+
func(atom *pdoknlv3.Atom) {
188+
// net/url.URL doesn't deepcopy...
189+
oldURL := atom.Spec.Service.BaseURL.String()
190+
newURL, _ := model.ParseURL(oldURL)
191+
newURL.Path += "/extra"
192+
atom.Spec.Service.BaseURL = model.URL{URL: newURL}
193+
},
194+
func(_, _ *pdoknlv3.Atom) field.ErrorList {
195+
return field.ErrorList{
196+
field.Forbidden(servicePath.Child("baseUrl"), "is immutable"),
197+
}
198+
},
110199
)
111200
})
112201

113202
It("Should deny update atom as ingressRouteURLs cannot be removed", func() {
114203
testUpdate(
115204
validator,
116205
"valid/ingress-route-urls.yaml",
117-
"invalid/ingress-route-urls-removed-url.yaml",
118-
errors.New("Atom.pdok.nl \"ingress-route-urls\" is invalid: spec.ingressRouteUrls: Invalid value: \"[{http://localhost:32788/rvo/wetlands/atom}]\": urls cannot be removed, missing: {http://localhost:32788/other/path}"),
206+
func(atom *pdoknlv3.Atom) {
207+
atom.Spec.IngressRouteURLs = atom.Spec.IngressRouteURLs[:len(atom.Spec.IngressRouteURLs)-1]
208+
},
209+
func(_, new *pdoknlv3.Atom) field.ErrorList {
210+
return field.ErrorList{
211+
field.Invalid(field.NewPath("spec").Child("ingressRouteUrls"), fmt.Sprint(new.Spec.IngressRouteURLs), "urls cannot be removed, missing: {http://localhost:32788/other/path}"),
212+
}
213+
},
119214
)
120215
})
121216

122217
It("Should deny update atom when the service baseUrl is changed and the old value is not added to the ingressRouteUrls", func() {
123218
testUpdate(
124219
validator,
125220
"valid/minimal.yaml",
126-
"invalid/minimal-service-url-changed-ingress-route-urls-missing-old.yaml",
127-
errors.New("Atom.pdok.nl \"asis-readonly-prod\" is invalid: spec.ingressRouteUrls: Invalid value: \"[{http://localhost:32788/new/path}]\": must contain baseURL: http://localhost:32788/rvo/wetlands/atom"),
221+
func(atom *pdoknlv3.Atom) {
222+
newURL, _ := model.ParseURL("http://localhost:32788/new/path")
223+
224+
atom.Spec.IngressRouteURLs = model.IngressRouteURLs{{URL: model.URL{URL: newURL}}}
225+
atom.Spec.Service.BaseURL = model.URL{URL: newURL}
226+
},
227+
func(_, new *pdoknlv3.Atom) field.ErrorList {
228+
return field.ErrorList{
229+
field.Invalid(field.NewPath("spec").Child("ingressRouteUrls"), fmt.Sprint(new.Spec.IngressRouteURLs), "must contain baseURL: http://localhost:32788/rvo/wetlands/atom"),
230+
}
231+
},
128232
)
129233
})
130234

131235
It("Should deny update atom when the service baseUrl is changed and the new value is not added to the ingressRouteUrls", func() {
132236
testUpdate(
133237
validator,
134238
"valid/minimal.yaml",
135-
"invalid/minimal-service-url-changed-ingress-route-urls-missing-new.yaml",
136-
errors.New("Atom.pdok.nl \"asis-readonly-prod\" is invalid: spec.ingressRouteUrls: Invalid value: \"[{http://localhost:32788/rvo/wetlands/atom}]\": must contain baseURL: http://localhost:32788/new/path"),
239+
func(atom *pdoknlv3.Atom) {
240+
oldURL := atom.Spec.Service.BaseURL
241+
newURL, _ := model.ParseURL("http://localhost:32788/new/path")
242+
243+
atom.Spec.IngressRouteURLs = model.IngressRouteURLs{{URL: oldURL}}
244+
atom.Spec.Service.BaseURL = model.URL{URL: newURL}
245+
},
246+
func(_, new *pdoknlv3.Atom) field.ErrorList {
247+
return field.ErrorList{
248+
field.Invalid(field.NewPath("spec").Child("ingressRouteUrls"), fmt.Sprint(new.Spec.IngressRouteURLs), "must contain baseURL: http://localhost:32788/new/path"),
249+
}
250+
},
137251
)
138252
})
139253

140254
It("Should create and update atom with changed service url if ingressRouteUrls is filled correctly", func() {
141-
testUpdate(validator, "valid/minimal.yaml", "valid/minimal-service-url-changed.yaml", nil)
255+
testUpdate(
256+
validator,
257+
"valid/minimal.yaml",
258+
func(atom *pdoknlv3.Atom) {
259+
oldURL := atom.Spec.Service.BaseURL
260+
newURL, _ := model.ParseURL("http://localhost:32788/new/path")
261+
262+
atom.Spec.IngressRouteURLs = model.IngressRouteURLs{{URL: oldURL}, {URL: model.URL{URL: newURL}}}
263+
atom.Spec.Service.BaseURL = model.URL{URL: newURL}
264+
},
265+
nil,
266+
)
142267
})
143268
})
144269
})
145270

146-
func testUpdate(validator AtomCustomValidator, createFile, updateFile string, expectedError error) {
271+
func testUpdate(validator AtomCustomValidator, createFile string, updateFn func(atom *pdoknlv3.Atom), errFn func(atomOld, atomNew *pdoknlv3.Atom) field.ErrorList) {
147272
atomOld := testCreate(validator, createFile, nil)
148273

149274
By("Simulating an (in)valid update scenario")
150-
input, err := os.ReadFile("test_data/updates/" + updateFile)
151-
Expect(err).NotTo(HaveOccurred())
152-
atomNew := &pdoknlv3.Atom{}
153-
err = yaml.Unmarshal(input, atomNew)
154-
Expect(err).NotTo(HaveOccurred())
155-
Expect(atomOld.GetName()).To(Equal(atomNew.GetName()))
156-
warnings, errorsUpdate := validator.ValidateUpdate(ctx, atomOld, atomNew)
275+
atomNew := atomOld.DeepCopy()
276+
updateFn(atomNew)
277+
278+
warnings, err := validator.ValidateUpdate(ctx, atomOld, atomNew)
157279

158280
Expect(len(warnings)).To(Equal(0))
159281

160-
if expectedError == nil {
161-
Expect(errorsUpdate).To(Not(HaveOccurred()))
282+
if errFn == nil {
283+
Expect(err).To(Not(HaveOccurred()))
162284
} else {
163-
Expect(errorsUpdate).To(HaveOccurred())
164-
Expect(expectedError.Error()).To(Equal(errorsUpdate.Error()))
285+
Expect(err).To(HaveOccurred())
286+
Expect(
287+
apierrors.NewInvalid(schema.GroupKind{Group: "pdok.nl", Kind: "Atom"}, atomNew.Name, errFn(atomOld, atomNew)).Error(),
288+
).To(Equal(err.Error()))
165289
}
166290
}
167291

168-
func testCreate(validator AtomCustomValidator, createFile string, expectedError error) *pdoknlv3.Atom {
292+
func testCreate(validator AtomCustomValidator, createFile string, errFn func(atom *pdoknlv3.Atom) field.ErrorList) *pdoknlv3.Atom {
169293
By("simulating a (in)valid creation scenario")
170294
input, err := os.ReadFile("test_data/creates/" + createFile)
171295
Expect(err).NotTo(HaveOccurred())
@@ -175,10 +299,13 @@ func testCreate(validator AtomCustomValidator, createFile string, expectedError
175299
warnings, err := validator.ValidateCreate(ctx, atom)
176300
Expect(len(warnings)).To(Equal(0))
177301

178-
if expectedError == nil {
302+
if errFn == nil {
179303
Expect(err).To(Not(HaveOccurred()))
180304
} else {
181-
Expect(expectedError.Error()).To(Equal(err.Error()))
305+
Expect(err).To(HaveOccurred())
306+
Expect(
307+
apierrors.NewInvalid(schema.GroupKind{Group: "pdok.nl", Kind: "Atom"}, atom.Name, errFn(atom)).Error(),
308+
).To(Equal(err.Error()))
182309
}
183310

184311
return atom

internal/webhook/v3/test_data/README.md

Lines changed: 0 additions & 10 deletions
This file was deleted.

internal/webhook/v3/test_data/updates/invalid/minimal-immutable-url.yaml renamed to internal/webhook/v3/test_data/creates/invalid/unknown-ownerref.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ metadata:
1313
namespace: services
1414
spec:
1515
service:
16-
baseUrl: http://localhost:32788/rvo44444/wetlands/atom
16+
baseUrl: http://localhost:32788/rvo/wetlands/atom
1717
datasetFeeds:
1818
- author:
1919
@@ -48,7 +48,7 @@ spec:
4848
technicalName: wetlands
4949
title: wetlands
5050
lang: nl
51-
ownerInfoRef: pdok
51+
ownerInfoRef: random
5252
rights: https://creativecommons.org/publicdomain/zero/1.0/deed.nl
5353
serviceMetadataLinks:
5454
metadataIdentifier: 2751ba40-5100-4186-81be-b7fdee95b49c

internal/webhook/v3/test_data/updates/invalid/ingress-route-urls-removed-url.yaml

Lines changed: 0 additions & 62 deletions
This file was deleted.

0 commit comments

Comments
 (0)