Skip to content

Commit 670a803

Browse files
committed
add example testcases to verify that the generated code is actually working, include it in make test
On-behalf-of: @SAP christoph.mewes@sap.com
1 parent f6db86a commit 670a803

File tree

4 files changed

+302
-0
lines changed

4 files changed

+302
-0
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ lint: $(GOLANGCI_LINT)
9898
.PHONY: test
9999
test:
100100
go test ./...
101+
cd examples; go test ./...
101102

102103
# Note, running this locally if you have any modified files, even those that are not generated,
103104
# will result in an error. This target is mostly for CI jobs.

examples/test/kcp/controller_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
Copyright 2025 The KCP 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 kcp
18+
19+
import (
20+
"context"
21+
"testing"
22+
"time"
23+
24+
"github.com/kcp-dev/logicalcluster/v3"
25+
26+
clienttesting "github.com/kcp-dev/client-go/third_party/k8s.io/client-go/testing"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/apimachinery/pkg/util/wait"
29+
"k8s.io/apimachinery/pkg/watch"
30+
"k8s.io/client-go/tools/cache"
31+
32+
examplev1 "acme.corp/pkg/apis/example/v1"
33+
"acme.corp/pkg/kcp/clients/clientset/versioned/fake"
34+
informers "acme.corp/pkg/kcp/clients/informers/externalversions"
35+
)
36+
37+
// TestFakeClient demonstrates how to use a fake client with SharedInformerFactory in tests.
38+
func TestFakeClient(t *testing.T) {
39+
ctx, cancel := context.WithCancel(context.Background())
40+
defer cancel()
41+
42+
watcherStarted := make(chan struct{})
43+
// Create the fake client.
44+
client := fake.NewSimpleClientset()
45+
// A catch-all watch reactor that allows us to inject the watcherStarted channel.
46+
client.PrependWatchReactor("*", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) {
47+
gvr := action.GetResource()
48+
ns := action.GetNamespace()
49+
watch, err := client.Tracker().Watch(gvr, ns)
50+
if err != nil {
51+
return false, nil, err
52+
}
53+
close(watcherStarted)
54+
return true, watch, nil
55+
})
56+
57+
// We will create an informer that writes added testTypes to a channel.
58+
testTypes := make(chan *examplev1.TestType, 1)
59+
informerFactory := informers.NewSharedInformerFactory(client, 0)
60+
testTypeInformer := informerFactory.Example().V1().TestTypes().Informer()
61+
if _, err := testTypeInformer.AddEventHandler(&cache.ResourceEventHandlerFuncs{
62+
AddFunc: func(obj interface{}) {
63+
testType := obj.(*examplev1.TestType)
64+
t.Logf("testType added: %s/%s", testType.Namespace, testType.Name)
65+
testTypes <- testType
66+
},
67+
}); err != nil {
68+
t.Fatalf("Failed to add event handler: %v", err)
69+
}
70+
71+
// Make sure informers are running.
72+
informerFactory.Cluster(logicalcluster.Name("root")).Start(ctx.Done())
73+
74+
// This is not required in tests, but it serves as a proof-of-concept by
75+
// ensuring that the informer goroutine have warmed up and called List before
76+
// we send any events to it.
77+
cache.WaitForCacheSync(ctx.Done(), testTypeInformer.HasSynced)
78+
79+
// The fake client doesn't support resource version. Any writes to the client
80+
// after the informer's initial LIST and before the informer establishing the
81+
// watcher will be missed by the informer. Therefore we wait until the watcher
82+
// starts.
83+
// Note that the fake client isn't designed to work with informer. It
84+
// doesn't support resource version. It's encouraged to use a real client
85+
// in an integration/E2E test if you need to test complex behavior with
86+
// informer/controllers.
87+
<-watcherStarted
88+
// Inject an event into the fake client.
89+
p := &examplev1.TestType{ObjectMeta: metav1.ObjectMeta{Name: "my-testobj"}}
90+
_, err := client.ExampleV1().TestTypes().Cluster(logicalcluster.NewPath("root")).Namespace("test-ns").Create(ctx, p, metav1.CreateOptions{})
91+
if err != nil {
92+
t.Fatalf("error injecting testType add: %v", err)
93+
}
94+
95+
select {
96+
case testType := <-testTypes:
97+
t.Logf("Got testType from channel: %s/%s", testType.Namespace, testType.Name)
98+
case <-time.After(wait.ForeverTestTimeout):
99+
t.Error("Informer did not get the added testType")
100+
}
101+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
Copyright 2025 The KCP 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 kcpexisting
18+
19+
import (
20+
"context"
21+
"testing"
22+
"time"
23+
24+
"github.com/kcp-dev/logicalcluster/v3"
25+
26+
clienttesting "github.com/kcp-dev/client-go/third_party/k8s.io/client-go/testing"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/apimachinery/pkg/util/wait"
29+
"k8s.io/apimachinery/pkg/watch"
30+
"k8s.io/client-go/tools/cache"
31+
32+
examplev1 "acme.corp/pkg/apis/example/v1"
33+
"acme.corp/pkg/kcpexisting/clients/clientset/versioned/fake"
34+
informers "acme.corp/pkg/kcpexisting/clients/informers/externalversions"
35+
)
36+
37+
// TestFakeClient demonstrates how to use a fake client with SharedInformerFactory in tests.
38+
func TestFakeClient(t *testing.T) {
39+
ctx, cancel := context.WithCancel(context.Background())
40+
defer cancel()
41+
42+
watcherStarted := make(chan struct{})
43+
// Create the fake client.
44+
client := fake.NewSimpleClientset()
45+
// A catch-all watch reactor that allows us to inject the watcherStarted channel.
46+
client.PrependWatchReactor("*", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) {
47+
gvr := action.GetResource()
48+
ns := action.GetNamespace()
49+
watch, err := client.Tracker().Watch(gvr, ns)
50+
if err != nil {
51+
return false, nil, err
52+
}
53+
close(watcherStarted)
54+
return true, watch, nil
55+
})
56+
57+
// We will create an informer that writes added testTypes to a channel.
58+
testTypes := make(chan *examplev1.TestType, 1)
59+
informerFactory := informers.NewSharedInformerFactory(client, 0)
60+
testTypeInformer := informerFactory.Example().V1().TestTypes().Informer()
61+
if _, err := testTypeInformer.AddEventHandler(&cache.ResourceEventHandlerFuncs{
62+
AddFunc: func(obj interface{}) {
63+
testType := obj.(*examplev1.TestType)
64+
t.Logf("testType added: %s/%s", testType.Namespace, testType.Name)
65+
testTypes <- testType
66+
},
67+
}); err != nil {
68+
t.Fatalf("Failed to add event handler: %v", err)
69+
}
70+
71+
// Make sure informers are running.
72+
informerFactory.Cluster(logicalcluster.Name("root")).Start(ctx.Done())
73+
74+
// This is not required in tests, but it serves as a proof-of-concept by
75+
// ensuring that the informer goroutine have warmed up and called List before
76+
// we send any events to it.
77+
cache.WaitForCacheSync(ctx.Done(), testTypeInformer.HasSynced)
78+
79+
// The fake client doesn't support resource version. Any writes to the client
80+
// after the informer's initial LIST and before the informer establishing the
81+
// watcher will be missed by the informer. Therefore we wait until the watcher
82+
// starts.
83+
// Note that the fake client isn't designed to work with informer. It
84+
// doesn't support resource version. It's encouraged to use a real client
85+
// in an integration/E2E test if you need to test complex behavior with
86+
// informer/controllers.
87+
<-watcherStarted
88+
// Inject an event into the fake client.
89+
p := &examplev1.TestType{ObjectMeta: metav1.ObjectMeta{Name: "my-testobj"}}
90+
_, err := client.ExampleV1().TestTypes().Cluster(logicalcluster.NewPath("root")).Namespace("test-ns").Create(ctx, p, metav1.CreateOptions{})
91+
if err != nil {
92+
t.Fatalf("error injecting testType add: %v", err)
93+
}
94+
95+
select {
96+
case testType := <-testTypes:
97+
t.Logf("Got testType from channel: %s/%s", testType.Namespace, testType.Name)
98+
case <-time.After(wait.ForeverTestTimeout):
99+
t.Error("Informer did not get the added testType")
100+
}
101+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
Copyright 2025 The KCP 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 singlecluster
18+
19+
import (
20+
"context"
21+
"testing"
22+
"time"
23+
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
"k8s.io/apimachinery/pkg/util/wait"
26+
"k8s.io/apimachinery/pkg/watch"
27+
clienttesting "k8s.io/client-go/testing"
28+
"k8s.io/client-go/tools/cache"
29+
30+
examplev1 "acme.corp/pkg/apis/example/v1"
31+
"acme.corp/pkg/generated/clientset/versioned/fake"
32+
informers "acme.corp/pkg/generated/informers/externalversions"
33+
)
34+
35+
// TestFakeClient demonstrates how to use a fake client with SharedInformerFactory in tests.
36+
func TestFakeClient(t *testing.T) {
37+
ctx, cancel := context.WithCancel(context.Background())
38+
defer cancel()
39+
40+
watcherStarted := make(chan struct{})
41+
// Create the fake client.
42+
client := fake.NewSimpleClientset()
43+
// A catch-all watch reactor that allows us to inject the watcherStarted channel.
44+
client.PrependWatchReactor("*", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) {
45+
gvr := action.GetResource()
46+
ns := action.GetNamespace()
47+
watch, err := client.Tracker().Watch(gvr, ns)
48+
if err != nil {
49+
return false, nil, err
50+
}
51+
close(watcherStarted)
52+
return true, watch, nil
53+
})
54+
55+
// We will create an informer that writes added testTypes to a channel.
56+
testTypes := make(chan *examplev1.TestType, 1)
57+
informerFactory := informers.NewSharedInformerFactory(client, 0)
58+
testTypeInformer := informerFactory.Example().V1().TestTypes().Informer()
59+
if _, err := testTypeInformer.AddEventHandler(&cache.ResourceEventHandlerFuncs{
60+
AddFunc: func(obj interface{}) {
61+
testType := obj.(*examplev1.TestType)
62+
t.Logf("testType added: %s/%s", testType.Namespace, testType.Name)
63+
testTypes <- testType
64+
},
65+
}); err != nil {
66+
t.Fatalf("Failed to add event handler: %v", err)
67+
}
68+
69+
// Make sure informers are running.
70+
informerFactory.Start(ctx.Done())
71+
72+
// This is not required in tests, but it serves as a proof-of-concept by
73+
// ensuring that the informer goroutine have warmed up and called List before
74+
// we send any events to it.
75+
cache.WaitForCacheSync(ctx.Done(), testTypeInformer.HasSynced)
76+
77+
// The fake client doesn't support resource version. Any writes to the client
78+
// after the informer's initial LIST and before the informer establishing the
79+
// watcher will be missed by the informer. Therefore we wait until the watcher
80+
// starts.
81+
// Note that the fake client isn't designed to work with informer. It
82+
// doesn't support resource version. It's encouraged to use a real client
83+
// in an integration/E2E test if you need to test complex behavior with
84+
// informer/controllers.
85+
<-watcherStarted
86+
// Inject an event into the fake client.
87+
p := &examplev1.TestType{ObjectMeta: metav1.ObjectMeta{Name: "my-testobj"}}
88+
_, err := client.ExampleV1().TestTypes("test-ns").Create(ctx, p, metav1.CreateOptions{})
89+
if err != nil {
90+
t.Fatalf("error injecting testType add: %v", err)
91+
}
92+
93+
select {
94+
case testType := <-testTypes:
95+
t.Logf("Got testType from channel: %s/%s", testType.Namespace, testType.Name)
96+
case <-time.After(wait.ForeverTestTimeout):
97+
t.Error("Informer did not get the added testType")
98+
}
99+
}

0 commit comments

Comments
 (0)