Skip to content

Commit 40fe01e

Browse files
authored
test(dataflow): add Ginkgo/Gomega package tests (#5741)
* test(dataflow): add Ginkgo/Gomega package tests Signed-off-by: Harsh <harshmastic@gmail.com> * fix(dataflow): cover DataProcess error reporting Signed-off-by: Harsh <harshmastic@gmail.com> * test(dataflow): deduplicate repeated test literals Signed-off-by: Harsh <harshmastic@gmail.com> * test(dataflow): deduplicate remaining test literals Signed-off-by: Harsh <harshmastic@gmail.com> * test(dataflow): fix staticcheck error helper Signed-off-by: Harsh <harshmastic@gmail.com> --------- Signed-off-by: Harsh <harshmastic@gmail.com>
1 parent cfa6d47 commit 40fe01e

5 files changed

Lines changed: 1443 additions & 2 deletions

File tree

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/*
2+
Copyright 2026 The Fluid 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 dataflow
18+
19+
import (
20+
"context"
21+
"time"
22+
23+
. "github.com/onsi/ginkgo/v2"
24+
. "github.com/onsi/gomega"
25+
26+
datav1alpha1 "github.com/fluid-cloudnative/fluid/api/v1alpha1"
27+
"github.com/fluid-cloudnative/fluid/pkg/common"
28+
"github.com/fluid-cloudnative/fluid/pkg/utils/fake"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/apimachinery/pkg/runtime"
31+
"k8s.io/apimachinery/pkg/types"
32+
"k8s.io/client-go/tools/record"
33+
"k8s.io/utils/ptr"
34+
ctrl "sigs.k8s.io/controller-runtime"
35+
"sigs.k8s.io/controller-runtime/pkg/builder"
36+
"sigs.k8s.io/controller-runtime/pkg/client"
37+
logf "sigs.k8s.io/controller-runtime/pkg/log"
38+
)
39+
40+
// newTestDataFlowReconciler builds a DataFlowReconciler for unit tests.
41+
func newTestDataFlowReconciler(s *runtime.Scheme, objs ...runtime.Object) *DataFlowReconciler {
42+
if s == nil {
43+
s = runtime.NewScheme()
44+
_ = datav1alpha1.AddToScheme(s)
45+
}
46+
fakeClient := fake.NewFakeClientWithScheme(s, objs...)
47+
log := logf.Log.WithName("dataflow-test")
48+
recorder := record.NewFakeRecorder(32)
49+
return NewDataFlowReconciler(fakeClient, log, recorder, 30*time.Second)
50+
}
51+
52+
var _ = Describe("DataFlowReconciler", func() {
53+
54+
Describe("ControllerName", func() {
55+
It("should return the expected controller name", func() {
56+
r := newTestDataFlowReconciler(nil)
57+
Expect(r.ControllerName()).To(Equal("DataFlowReconciler"))
58+
})
59+
})
60+
61+
Describe("NewDataFlowReconciler", func() {
62+
It("should create a non-nil reconciler with correct fields", func() {
63+
s := runtime.NewScheme()
64+
_ = datav1alpha1.AddToScheme(s)
65+
r := newTestDataFlowReconciler(s)
66+
Expect(r).NotTo(BeNil())
67+
Expect(r.Client).NotTo(BeNil())
68+
Expect(r.Recorder).NotTo(BeNil())
69+
Expect(r.ResyncPeriod).To(Equal(30 * time.Second))
70+
})
71+
})
72+
73+
Describe("Reconcile", func() {
74+
It("should return no error and no requeue when no operation objects exist for a given name", func() {
75+
s := runtime.NewScheme()
76+
_ = datav1alpha1.AddToScheme(s)
77+
r := newTestDataFlowReconciler(s)
78+
req := ctrl.Request{
79+
NamespacedName: types.NamespacedName{Name: "missing", Namespace: "default"},
80+
}
81+
result, err := r.Reconcile(context.TODO(), req)
82+
Expect(err).NotTo(HaveOccurred())
83+
Expect(result).To(Equal(ctrl.Result{}))
84+
})
85+
86+
It("should requeue when a DataLoad with RunAfter exists and preceding op is not complete", func() {
87+
s := runtime.NewScheme()
88+
_ = datav1alpha1.AddToScheme(s)
89+
90+
precedingLoad := &datav1alpha1.DataLoad{
91+
ObjectMeta: metav1.ObjectMeta{Name: "preceding", Namespace: "default"},
92+
Status: datav1alpha1.OperationStatus{
93+
Phase: common.PhaseExecuting,
94+
},
95+
}
96+
97+
waitingLoad := &datav1alpha1.DataLoad{
98+
ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"},
99+
Spec: datav1alpha1.DataLoadSpec{
100+
RunAfter: &datav1alpha1.OperationRef{
101+
ObjectRef: datav1alpha1.ObjectRef{
102+
Kind: "DataLoad",
103+
Name: "preceding",
104+
},
105+
},
106+
},
107+
Status: datav1alpha1.OperationStatus{
108+
WaitingFor: datav1alpha1.WaitingStatus{
109+
OperationComplete: ptr.To(true),
110+
},
111+
},
112+
}
113+
114+
r := newTestDataFlowReconciler(s, precedingLoad, waitingLoad)
115+
req := ctrl.Request{
116+
NamespacedName: types.NamespacedName{Name: "test", Namespace: "default"},
117+
}
118+
result, err := r.Reconcile(context.TODO(), req)
119+
Expect(err).NotTo(HaveOccurred())
120+
Expect(result.RequeueAfter).To(Equal(30 * time.Second))
121+
})
122+
123+
It("should not requeue when a DataLoad with RunAfter exists and preceding op is complete", func() {
124+
s := runtime.NewScheme()
125+
_ = datav1alpha1.AddToScheme(s)
126+
127+
precedingLoad := &datav1alpha1.DataLoad{
128+
ObjectMeta: metav1.ObjectMeta{Name: "preceding", Namespace: "default"},
129+
Status: datav1alpha1.OperationStatus{
130+
Phase: common.PhaseComplete,
131+
},
132+
}
133+
134+
waitingLoad := &datav1alpha1.DataLoad{
135+
ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"},
136+
Spec: datav1alpha1.DataLoadSpec{
137+
RunAfter: &datav1alpha1.OperationRef{
138+
ObjectRef: datav1alpha1.ObjectRef{
139+
Kind: "DataLoad",
140+
Name: "preceding",
141+
},
142+
},
143+
},
144+
Status: datav1alpha1.OperationStatus{
145+
WaitingFor: datav1alpha1.WaitingStatus{
146+
OperationComplete: ptr.To(true),
147+
},
148+
},
149+
}
150+
151+
r := newTestDataFlowReconciler(s, precedingLoad, waitingLoad)
152+
req := ctrl.Request{
153+
NamespacedName: types.NamespacedName{Name: "test", Namespace: "default"},
154+
}
155+
result, err := r.Reconcile(context.TODO(), req)
156+
Expect(err).NotTo(HaveOccurred())
157+
Expect(result).To(Equal(ctrl.Result{}))
158+
})
159+
})
160+
})
161+
162+
// DataFlowEnabled and setupWatches depend on discovery.GetFluidDiscovery() which uses a sync.Once
163+
// singleton that requires a live cluster connection. We test the code paths that avoid touching
164+
// the discovery singleton by temporarily substituting an empty reconcileKinds map so the loop body
165+
// (which calls GetFluidDiscovery) is never entered. This covers the for-loop entry, toSetup
166+
// construction, and return-false / return-bld paths without any cluster dependency.
167+
168+
var _ = Describe("DataFlowEnabled with empty reconcileKinds", func() {
169+
var saved map[string]client.Object
170+
171+
BeforeEach(func() {
172+
saved = reconcileKinds
173+
reconcileKinds = map[string]client.Object{}
174+
})
175+
176+
AfterEach(func() {
177+
reconcileKinds = saved
178+
})
179+
180+
It("should return false when no resource kinds are registered", func() {
181+
Expect(DataFlowEnabled()).To(BeFalse())
182+
})
183+
})
184+
185+
var _ = Describe("setupWatches with empty reconcileKinds", func() {
186+
var saved map[string]client.Object
187+
188+
BeforeEach(func() {
189+
saved = reconcileKinds
190+
reconcileKinds = map[string]client.Object{}
191+
})
192+
193+
AfterEach(func() {
194+
reconcileKinds = saved
195+
})
196+
197+
It("should return the builder unchanged when no resource kinds are registered", func() {
198+
// With an empty reconcileKinds the toSetup slice stays empty, neither bld.For nor
199+
// bld.Watches is called, and the function returns bld as-is. We pass nil as bld to
200+
// keep the test self-contained without requiring a real controller-runtime Builder.
201+
result := setupWatches(nil, nil, builder.Predicates{})
202+
Expect(result).To(BeNil())
203+
})
204+
})

0 commit comments

Comments
 (0)