Skip to content

Commit 41bc715

Browse files
committed
refactor komega package
rename Matcher to komega since it's not really a matcher komega.With... methods now return copies allow to specify a Gomega instance to use
1 parent c472f24 commit 41bc715

File tree

4 files changed

+274
-260
lines changed

4 files changed

+274
-260
lines changed

pkg/envtest/komega/interfaces.go

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,30 +27,20 @@ import (
2727

2828
// Komega is the root interface that the Matcher implements.
2929
type Komega interface {
30-
KomegaAsync
31-
KomegaSync
32-
WithContext(context.Context) Komega
33-
}
34-
35-
// KomegaSync is the interface for any sync assertions that
36-
// the matcher implements.
37-
type KomegaSync interface {
3830
Create(client.Object, ...client.CreateOption) gomega.GomegaAssertion
39-
Delete(client.Object, ...client.DeleteOption) gomega.GomegaAssertion
40-
WithExtras(...interface{}) KomegaSync
41-
}
42-
43-
// KomegaAsync is the interface for any async assertions that
44-
// the matcher implements.
45-
type KomegaAsync interface {
46-
Consistently(runtime.Object, ...client.ListOption) gomega.AsyncAssertion
47-
Eventually(runtime.Object, ...client.ListOption) gomega.AsyncAssertion
4831
Get(client.Object) gomega.AsyncAssertion
4932
List(client.ObjectList, ...client.ListOption) gomega.AsyncAssertion
5033
Update(client.Object, UpdateFunc, ...client.UpdateOption) gomega.AsyncAssertion
5134
UpdateStatus(client.Object, UpdateFunc, ...client.UpdateOption) gomega.AsyncAssertion
52-
WithTimeout(time.Duration) KomegaAsync
53-
WithPollInterval(time.Duration) KomegaAsync
35+
Delete(client.Object, ...client.DeleteOption) gomega.GomegaAssertion
36+
37+
Consistently(runtime.Object, ...client.ListOption) gomega.AsyncAssertion
38+
Eventually(runtime.Object, ...client.ListOption) gomega.AsyncAssertion
39+
40+
WithExtras(...interface{}) Komega
41+
WithTimeout(time.Duration) Komega
42+
WithPollInterval(time.Duration) Komega
43+
WithContext(context.Context) Komega
5444
}
5545

5646
// UpdateFunc modifies the object fetched from the API server before sending

pkg/envtest/komega/komega.go

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
/*
2+
Copyright 2021 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 komega
18+
19+
import (
20+
"context"
21+
"testing"
22+
"time"
23+
24+
"github.com/onsi/gomega"
25+
"k8s.io/apimachinery/pkg/runtime"
26+
"k8s.io/apimachinery/pkg/types"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
28+
)
29+
30+
// komega has Gomega Matchers that use the controller-runtime client.
31+
type komega struct {
32+
ctx context.Context
33+
g gomega.Gomega
34+
client client.Client
35+
extras []interface{}
36+
timeout time.Duration
37+
pollInterval time.Duration
38+
}
39+
40+
var _ Komega = &komega{}
41+
42+
// NewKomega creates a new instance with a given client.
43+
func NewKomega(c client.Client) *komega {
44+
return &komega{
45+
g: gomega.Default,
46+
client: c,
47+
}
48+
}
49+
50+
// NewKomegaWithT creates a new instance with a given client and a Gomega derived from a given testing.T.
51+
func NewKomegaWithT(t *testing.T, c client.Client) *komega {
52+
return &komega{
53+
g: gomega.NewWithT(t),
54+
client: c,
55+
}
56+
}
57+
58+
// NewKomegaWithG creates a new instance with a given client and gomega.
59+
func NewKomegaWithG(g gomega.Gomega, c client.Client) *komega {
60+
return &komega{
61+
g: g,
62+
client: c,
63+
}
64+
}
65+
66+
// WithContext returns a copy using the given Context.
67+
func (k komega) WithContext(ctx context.Context) Komega {
68+
k.ctx = ctx
69+
return &k
70+
}
71+
72+
// WithExtras returns a copy using the given extras.
73+
func (k komega) WithExtras(extras ...interface{}) Komega {
74+
k.extras = extras
75+
return &k
76+
}
77+
78+
// WithPollInterval returns a copy using the given poll interval.
79+
func (k komega) WithPollInterval(interval time.Duration) Komega {
80+
k.pollInterval = interval
81+
return &k
82+
}
83+
84+
// WithTimeout returns a copy using the given timeout.
85+
func (k komega) WithTimeout(timeout time.Duration) Komega {
86+
k.timeout = timeout
87+
return &k
88+
}
89+
90+
// context returns the matcher context if one has been set.
91+
// Else it returns the context.TODO().
92+
func (k *komega) context() context.Context {
93+
if k.ctx == nil {
94+
return context.TODO()
95+
}
96+
return k.ctx
97+
}
98+
99+
// intervals constructs the intervals for async assertions.
100+
// If no timeout is set, the list will be empty.
101+
func (k *komega) intervals() []interface{} {
102+
if k.timeout == 0 {
103+
return []interface{}{}
104+
}
105+
out := []interface{}{k.timeout}
106+
if k.pollInterval != 0 {
107+
out = append(out, k.pollInterval)
108+
}
109+
return out
110+
}
111+
112+
// Create creates an object and forwards the error for matching.
113+
func (k *komega) Create(obj client.Object, opts ...client.CreateOption) gomega.GomegaAssertion {
114+
err := k.client.Create(k.context(), obj, opts...)
115+
return k.g.Expect(err, k.extras...)
116+
}
117+
118+
// Get fetches an object until the forwarded error matches.
119+
func (k *komega) Get(obj client.Object) gomega.GomegaAsyncAssertion {
120+
key := types.NamespacedName{
121+
Name: obj.GetName(),
122+
Namespace: obj.GetNamespace(),
123+
}
124+
get := func() error {
125+
return k.client.Get(k.context(), key, obj)
126+
}
127+
return k.g.Eventually(get, k.intervals()...)
128+
}
129+
130+
// List fetches a list until the forwarded error matches.
131+
func (k *komega) List(obj client.ObjectList, opts ...client.ListOption) gomega.GomegaAsyncAssertion {
132+
list := func() error {
133+
return k.client.List(k.context(), obj, opts...)
134+
}
135+
return k.g.Eventually(list, k.intervals()...)
136+
}
137+
138+
// Update tries to update an object by applying the updateFunc until the forwarded error matches.
139+
func (k *komega) Update(obj client.Object, updateFunc UpdateFunc, opts ...client.UpdateOption) gomega.GomegaAsyncAssertion {
140+
key := types.NamespacedName{
141+
Name: obj.GetName(),
142+
Namespace: obj.GetNamespace(),
143+
}
144+
update := func() error {
145+
err := k.client.Get(k.context(), key, obj)
146+
if err != nil {
147+
return err
148+
}
149+
return k.client.Update(k.context(), updateFunc(obj), opts...)
150+
}
151+
return k.g.Eventually(update, k.intervals()...)
152+
}
153+
154+
// UpdateStatus tries to update an object's status by applying the updateFunc until the forwarded error matches.
155+
func (k *komega) UpdateStatus(obj client.Object, updateFunc UpdateFunc, opts ...client.UpdateOption) gomega.GomegaAsyncAssertion {
156+
key := types.NamespacedName{
157+
Name: obj.GetName(),
158+
Namespace: obj.GetNamespace(),
159+
}
160+
update := func() error {
161+
err := k.client.Get(k.context(), key, obj)
162+
if err != nil {
163+
return err
164+
}
165+
return k.client.Status().Update(k.context(), updateFunc(obj), opts...)
166+
}
167+
return k.g.Eventually(update, k.intervals()...)
168+
}
169+
170+
// Delete deletes an object and forwards the error for matching.
171+
func (k *komega) Delete(obj client.Object, opts ...client.DeleteOption) gomega.GomegaAssertion {
172+
err := k.client.Delete(k.context(), obj, opts...)
173+
return gomega.Expect(err, k.extras...)
174+
}
175+
176+
// Consistently gets an object using Gomega's Consistently.
177+
// See https://onsi.github.io/gomega/#consistently for how it works.
178+
// It supports listing objects as well.
179+
func (k *komega) Consistently(obj runtime.Object, opts ...client.ListOption) gomega.GomegaAsyncAssertion {
180+
// If the object is a list, return a list
181+
if o, ok := obj.(client.ObjectList); ok {
182+
return k.consistentlyList(o, opts...)
183+
}
184+
if o, ok := obj.(client.Object); ok {
185+
return k.consistentlyObject(o)
186+
}
187+
//Should not get here
188+
panic("Unknown object.")
189+
}
190+
191+
// consistentlyclient.Object gets an individual object from the API server.
192+
func (k *komega) consistentlyObject(obj client.Object) gomega.GomegaAsyncAssertion {
193+
key := types.NamespacedName{
194+
Name: obj.GetName(),
195+
Namespace: obj.GetNamespace(),
196+
}
197+
get := func() client.Object {
198+
err := k.client.Get(k.context(), key, obj)
199+
if err != nil {
200+
panic(err)
201+
}
202+
return obj
203+
}
204+
return k.g.Consistently(get, k.intervals()...)
205+
}
206+
207+
// consistentlyList gets an list of objects from the API server.
208+
func (k *komega) consistentlyList(obj client.ObjectList, opts ...client.ListOption) gomega.GomegaAsyncAssertion {
209+
list := func() client.ObjectList {
210+
err := k.client.List(k.context(), obj, opts...)
211+
if err != nil {
212+
panic(err)
213+
}
214+
return obj
215+
}
216+
return k.g.Consistently(list, k.intervals()...)
217+
}
218+
219+
// Eventually gets an object repeatedly until it matches.
220+
// See https://onsi.github.io/gomega/#eventually for how it works.
221+
// It supports listing objects as well.
222+
func (k *komega) Eventually(obj runtime.Object, opts ...client.ListOption) gomega.GomegaAsyncAssertion {
223+
// If the object is a list, return a list
224+
if o, ok := obj.(client.ObjectList); ok {
225+
return k.eventuallyList(o, opts...)
226+
}
227+
if o, ok := obj.(client.Object); ok {
228+
return k.eventuallyObject(o)
229+
}
230+
//Should not get here
231+
panic("Unknown object.")
232+
}
233+
234+
// eventuallyObject gets an individual object from the API server.
235+
func (k *komega) eventuallyObject(obj client.Object) gomega.GomegaAsyncAssertion {
236+
key := types.NamespacedName{
237+
Name: obj.GetName(),
238+
Namespace: obj.GetNamespace(),
239+
}
240+
get := func() client.Object {
241+
err := k.client.Get(k.context(), key, obj)
242+
if err != nil {
243+
panic(err)
244+
}
245+
return obj
246+
}
247+
return k.g.Eventually(get, k.intervals()...)
248+
}
249+
250+
// eventuallyList gets a list type from the API server.
251+
func (k *komega) eventuallyList(obj client.ObjectList, opts ...client.ListOption) gomega.GomegaAsyncAssertion {
252+
list := func() client.ObjectList {
253+
err := k.client.List(k.context(), obj, opts...)
254+
if err != nil {
255+
panic(err)
256+
}
257+
return obj
258+
}
259+
return k.g.Eventually(list, k.intervals()...)
260+
}

0 commit comments

Comments
 (0)