Skip to content

Commit 5428f06

Browse files
committed
PDOK-18065 Atom-Operator - validation unit-tests
1 parent 22fa969 commit 5428f06

File tree

6 files changed

+246
-8
lines changed

6 files changed

+246
-8
lines changed

internal/webhook/v3/atom_webhook_test.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ package v3
2727
import (
2828
. "github.com/onsi/ginkgo/v2" //nolint:revive // ginkgo bdd
2929
. "github.com/onsi/gomega" //nolint:revive // ginkgo bdd
30+
"os"
31+
"sigs.k8s.io/yaml"
3032

3133
pdoknlv3 "github.com/pdok/atom-operator/api/v3"
3234
// TODO (user): Add any additional imports if needed
@@ -42,11 +44,14 @@ var _ = Describe("Atom Webhook", func() {
4244
BeforeEach(func() {
4345
obj = &pdoknlv3.Atom{}
4446
oldObj = &pdoknlv3.Atom{}
45-
validator = AtomCustomValidator{}
47+
validator = AtomCustomValidator{
48+
Client: k8sClient,
49+
}
4650
Expect(validator).NotTo(BeNil(), "Expected validator to be initialized")
4751
Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized")
4852
Expect(obj).NotTo(BeNil(), "Expected obj to be initialized")
4953
// TODO (user): Add any setup logic common to all tests
54+
5055
})
5156

5257
AfterEach(func() {
@@ -56,12 +61,19 @@ var _ = Describe("Atom Webhook", func() {
5661
Context("When creating or updating Atom under Validating Webhook", func() {
5762
// TODO (user): Add logic for validating webhooks
5863
// Example:
59-
// It("Should deny creation if a required field is missing", func() {
60-
// By("simulating an invalid creation scenario")
61-
// obj.SomeRequiredField = ""
62-
// Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred())
63-
// })
64-
//
64+
It("Should deny creation if a required field is missing", func() {
65+
By("simulating an invalid creation scenario")
66+
input, err := os.ReadFile("test_data/input/1-no-error-no-warning.yaml")
67+
Expect(err).NotTo(HaveOccurred())
68+
atom := &pdoknlv3.Atom{}
69+
err = yaml.Unmarshal(input, atom)
70+
Expect(err).NotTo(HaveOccurred())
71+
println(atom.Spec.Service.OwnerInfoRef)
72+
warnings, errors := validator.ValidateCreate(ctx, atom)
73+
Expect(errors).To(BeNil())
74+
Expect(len(warnings)).To(Equal(0))
75+
})
76+
6577
// It("Should admit creation if all required fields are present", func() {
6678
// By("simulating an invalid creation scenario")
6779
// obj.SomeRequiredField = "valid_value"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## Append samples of your project ##
2+
resources:
3+
- v1_ownerinfo.yaml
4+
# +kubebuilder:scaffold:manifestskustomizesamples
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package samples
2+
3+
import (
4+
_ "embed"
5+
v1 "github.com/pdok/smooth-operator/api/v1"
6+
"sigs.k8s.io/yaml"
7+
)
8+
9+
//go:embed v1_ownerinfo.yaml
10+
var ownerInfoContent string
11+
12+
func OwnerInfoSample() (*v1.OwnerInfo, error) {
13+
var sample v1.OwnerInfo
14+
err := yaml.Unmarshal([]byte(ownerInfoContent), &sample)
15+
return &sample, err
16+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
apiVersion: pdok.nl/v1
2+
kind: OwnerInfo
3+
metadata:
4+
name: pdok
5+
namespace: services
6+
labels:
7+
app.kubernetes.io/name: atom-operator
8+
app.kubernetes.io/managed-by: kustomize
9+
spec:
10+
metadataUrls:
11+
csw:
12+
hrefTemplate: "https://www.nationaalgeoregister.nl/geonetwork/srv/dut/csw?service=CSW&version=2.0.2&request=GetRecordById&outputschema=http://www.isotc211.org/2005/gmd&elementsetname=full&id={{identifier}}"
13+
type: alternate
14+
opensearch:
15+
hrefTemplate: "https://www.nationaalgeoregister.nl/geonetwork/opensearch/dut/{{identifier}}/OpenSearchDescription.xml"
16+
type: alternate
17+
html:
18+
hrefTemplate: "https://www.nationaalgeoregister.nl/geonetwork/srv/dut/catalog.search#/metadata/{{identifier}}"
19+
type: alternate
20+
namespaceTemplate: "http://{{prefix}}.geonovum.nl"
21+
atom:
22+
defaultStylesheet: https://service.pdok.nl/atom/style/style.xsl
23+
author: # author/owner van de dataset
24+
name: pdok
25+
26+
wfs:
27+
serviceprovider:
28+
providername: PDOK
29+
providersite:
30+
type: simple
31+
href: https://pdok.nl
32+
servicecontact:
33+
individualname: KlantContactCenter PDOK
34+
positionname: pointOfContact
35+
contactinfo:
36+
phone:
37+
voice:
38+
facsmile:
39+
text:
40+
address:
41+
deliverypoint:
42+
city: Apeldoorn
43+
administrativearea:
44+
postalcode:
45+
country: Netherlands
46+
electronicmailaddress: [email protected]
47+
onlineresource:
48+
type:
49+
href:
50+
hoursofservice:
51+
contactinstructions:
52+
role:
53+
wms:
54+
contactinformation:
55+
contactpersonprimary:
56+
contactperson: KlantContactCenter PDOK
57+
contactorganization: PDOK
58+
contactposition: pointOfContact
59+
contactaddress:
60+
addresstype:
61+
address:
62+
city: Apeldoorn
63+
stateorprovince:
64+
postcode:
65+
country: Netherlands
66+
contactvoicetelephone:
67+
contactfacsimiletelephone:
68+
contactelectronicmailAddress: [email protected]
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
apiVersion: pdok.nl/v3
2+
kind: Atom
3+
metadata:
4+
annotations:
5+
kangaroo.pdok.nl/lifecycle-phase: prod
6+
kangaroo.pdok.nl/service-bundle-ids: 65daed5f-e9e4-5791-a7c9-7e9effcca520
7+
kangaroo.pdok.nl/readonly: "true"
8+
labels:
9+
pdok.nl/dataset-id: wetlands
10+
pdok.nl/owner-id: rvo
11+
pdok.nl/service-type: atom
12+
name: asis-readonly-prod
13+
namespace: services
14+
spec:
15+
service:
16+
baseUrl: http://localhost:32788/rvo/wetlands/atom
17+
datasetFeeds:
18+
- author:
19+
20+
name: Ministerie van EL&I - GIS Competence Center
21+
datasetMetadataLinks:
22+
metadataIdentifier: 07d73b60-dfd6-4c54-9c82-9fac70c6c48e
23+
templates:
24+
- csw
25+
- html
26+
entries:
27+
- content:
28+
Wetlands zijn de natte natuurgebieden in Nederland (44 gebieden).
29+
Het Wetland verdrag is op 2 februari 1971 te Ramsar in Iran ondertekend.
30+
Nederland was een van de zestien landen die het Verdrag toen ondertekende.
31+
downloadlinks:
32+
- data: public/rvo/wetlands/65daed5f-e9e4-5791-a7c9-7e9effcca520/3/wetlands.gpkg
33+
polygon:
34+
bbox:
35+
maxx: "7.5553527"
36+
maxy: "55.66948"
37+
minx: "2.354173"
38+
miny: "50.71447"
39+
srs:
40+
name: Amersfoort / RD New
41+
uri: https://www.opengis.net/def/crs/EPSG/0/28992
42+
technicalName: wetlands
43+
title: wetlands
44+
updated: "2025-02-28T09:04:17Z"
45+
spatialDatasetIdentifierCode: 07d73b60-dfd6-4c54-9c82-9fac70c6c48e
46+
spatialDatasetIdentifierNamespace: http://www.pdok.nl
47+
subtitle: wetlands
48+
technicalName: wetlands
49+
title: wetlands
50+
lang: nl
51+
ownerInfoRef: pdok
52+
rights: https://creativecommons.org/publicdomain/zero/1.0/deed.nl
53+
serviceMetadataLinks:
54+
metadataIdentifier: 2751ba40-5100-4186-81be-b7fdee95b49c
55+
templates:
56+
- csw
57+
- opensearch
58+
- html
59+
subtitle: Download Service van wetlands
60+
title: Wetlands

internal/webhook/v3/webhook_suite_test.go

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,17 @@ package v3
2727
import (
2828
"context"
2929
"crypto/tls"
30+
"errors"
3031
"fmt"
32+
samples "github.com/pdok/atom-operator/internal/webhook/v3/ownerinfo-test"
33+
smoothoperatorv1 "github.com/pdok/smooth-operator/api/v1"
34+
"golang.org/x/tools/go/packages"
35+
corev1 "k8s.io/api/core/v1"
36+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
37+
"k8s.io/apimachinery/pkg/util/json"
3138
"net"
3239
"os"
40+
"os/exec"
3341
"path/filepath"
3442
"testing"
3543
"time"
@@ -39,7 +47,9 @@ import (
3947

4048
admissionv1 "k8s.io/api/admission/v1"
4149
"k8s.io/apimachinery/pkg/runtime"
50+
"k8s.io/client-go/kubernetes"
4251
"k8s.io/client-go/rest"
52+
4353
ctrl "sigs.k8s.io/controller-runtime"
4454
"sigs.k8s.io/controller-runtime/pkg/client"
4555
"sigs.k8s.io/controller-runtime/pkg/envtest"
@@ -82,17 +92,29 @@ var _ = BeforeSuite(func() {
8292

8393
err = admissionv1.AddToScheme(scheme)
8494
Expect(err).NotTo(HaveOccurred())
95+
err = smoothoperatorv1.AddToScheme(scheme)
96+
Expect(err).NotTo(HaveOccurred())
8597

8698
// +kubebuilder:scaffold:scheme
8799

88100
By("bootstrapping test environment")
101+
traefikCRDPath := must(getTraefikCRDPath())
102+
ownerInfoCRDPath := must(getOwnerInfoCRDPath())
89103
testEnv = &envtest.Environment{
90-
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")},
91104
ErrorIfCRDPathMissing: false,
92105

93106
WebhookInstallOptions: envtest.WebhookInstallOptions{
94107
Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")},
95108
},
109+
CRDInstallOptions: envtest.CRDInstallOptions{
110+
Scheme: nil,
111+
Paths: []string{
112+
filepath.Join("..", "..", "..", "config", "crd", "bases", "pdok.nl_atoms.yaml"),
113+
traefikCRDPath,
114+
ownerInfoCRDPath,
115+
},
116+
ErrorIfPathMissing: true,
117+
},
96118
}
97119

98120
// Retrieve the first found binary directory to allow running tests from IDEs
@@ -109,6 +131,23 @@ var _ = BeforeSuite(func() {
109131
Expect(err).NotTo(HaveOccurred())
110132
Expect(k8sClient).NotTo(BeNil())
111133

134+
By("creating manager namespace")
135+
136+
clientset, err := kubernetes.NewForConfig(cfg)
137+
namespace22 := &corev1.Namespace{
138+
ObjectMeta: metav1.ObjectMeta{
139+
Name: "services",
140+
},
141+
}
142+
clientset.CoreV1().Namespaces().Create(ctx, namespace22, metav1.CreateOptions{})
143+
Expect(err).NotTo(HaveOccurred(), "Failed to create namespace")
144+
ownerInfo, err := samples.OwnerInfoSample()
145+
Expect(err).NotTo(HaveOccurred())
146+
Expect(ownerInfo).NotTo(BeNil())
147+
148+
err = k8sClient.Create(ctx, ownerInfo)
149+
Expect(err).NotTo(HaveOccurred())
150+
112151
// start webhook server using Manager.
113152
webhookInstallOptions := &testEnv.WebhookInstallOptions
114153
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
@@ -176,3 +215,42 @@ func getFirstFoundEnvTestBinaryDir() string {
176215
}
177216
return ""
178217
}
218+
219+
func must[T any](t T, err error) T {
220+
if err != nil {
221+
panic(err)
222+
}
223+
return t
224+
}
225+
226+
func getOwnerInfoCRDPath() (string, error) {
227+
smoothOperatorModule, err := getModule("github.com/pdok/smooth-operator")
228+
if err != nil {
229+
return "", err
230+
}
231+
if smoothOperatorModule.Dir == "" {
232+
return "", errors.New("cannot find path for smooth-operator module")
233+
}
234+
return filepath.Join(smoothOperatorModule.Dir, "config", "crd", "bases", "pdok.nl_ownerinfo.yaml"), nil
235+
}
236+
237+
func getTraefikCRDPath() (string, error) {
238+
traefikModule, err := getModule("github.com/traefik/traefik/v3")
239+
if err != nil {
240+
return "", err
241+
}
242+
if traefikModule.Dir == "" {
243+
return "", errors.New("cannot find path for traefik module")
244+
}
245+
return filepath.Join(traefikModule.Dir, "integration", "fixtures", "k8s", "01-traefik-crd.yml"), nil
246+
}
247+
248+
func getModule(name string) (module *packages.Module, err error) {
249+
out, err := exec.Command("go", "list", "-json", "-m", name).Output()
250+
if err != nil {
251+
return
252+
}
253+
module = &packages.Module{}
254+
err = json.Unmarshal(out, module)
255+
return
256+
}

0 commit comments

Comments
 (0)