Skip to content

Commit 4662d24

Browse files
committed
test(alluxio): migrate suite to Ginkgo v2 and add unit tests for controller and implement
- Migrate BeforeSuite to Ginkgo v2 style with NodeTimeout(60s) - Add unit tests for NewRuntimeReconciler, ControllerName, and Reconcile - Add unit tests for getRuntime, RemoveEngine, GetOrCreateEngine - Use context.Background() throughout tests - Use non-nil MockEngine stub in RemoveEngine test Signed-off-by: Harsh <harshmastic@gmail.com>
1 parent a69c888 commit 4662d24

3 files changed

Lines changed: 273 additions & 9 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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 alluxio
18+
19+
import (
20+
"context"
21+
22+
. "github.com/onsi/ginkgo/v2"
23+
. "github.com/onsi/gomega"
24+
25+
datav1alpha1 "github.com/fluid-cloudnative/fluid/api/v1alpha1"
26+
"github.com/fluid-cloudnative/fluid/pkg/utils/fake"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/apimachinery/pkg/runtime"
29+
"k8s.io/apimachinery/pkg/types"
30+
"k8s.io/client-go/tools/record"
31+
ctrl "sigs.k8s.io/controller-runtime"
32+
)
33+
34+
var _ = Describe("AlluxioRuntimeController", func() {
35+
const (
36+
testName = "alluxio-test"
37+
testNamespace = "default"
38+
)
39+
40+
var (
41+
s *runtime.Scheme
42+
)
43+
44+
BeforeEach(func() {
45+
s = runtime.NewScheme()
46+
Expect(datav1alpha1.AddToScheme(s)).To(Succeed())
47+
})
48+
49+
Describe("NewRuntimeReconciler", func() {
50+
It("creates a RuntimeReconciler with expected fields", func() {
51+
c := fake.NewFakeClientWithScheme(s)
52+
recorder := record.NewFakeRecorder(10)
53+
r := NewRuntimeReconciler(c, fake.NullLogger(), s, recorder)
54+
55+
Expect(r).ToNot(BeNil())
56+
Expect(r.Scheme).To(Equal(s))
57+
Expect(r.engines).ToNot(BeNil())
58+
Expect(r.mutex).ToNot(BeNil())
59+
Expect(r.RuntimeReconciler).ToNot(BeNil())
60+
})
61+
})
62+
63+
Describe("ControllerName", func() {
64+
It("returns the expected controller name", func() {
65+
c := fake.NewFakeClientWithScheme(s)
66+
recorder := record.NewFakeRecorder(10)
67+
r := NewRuntimeReconciler(c, fake.NullLogger(), s, recorder)
68+
69+
Expect(r.ControllerName()).To(Equal(controllerName))
70+
})
71+
})
72+
73+
Describe("Reconcile", func() {
74+
It("returns empty result when the AlluxioRuntime is not found", func() {
75+
c := fake.NewFakeClientWithScheme(s)
76+
recorder := record.NewFakeRecorder(10)
77+
r := NewRuntimeReconciler(c, fake.NullLogger(), s, recorder)
78+
79+
req := ctrl.Request{
80+
NamespacedName: types.NamespacedName{
81+
Name: "nonexistent",
82+
Namespace: testNamespace,
83+
},
84+
}
85+
result, err := r.Reconcile(context.Background(), req)
86+
Expect(err).ToNot(HaveOccurred())
87+
Expect(result).To(Equal(ctrl.Result{}))
88+
})
89+
90+
It("proceeds past getRuntime when the AlluxioRuntime exists", func() {
91+
rt := &datav1alpha1.AlluxioRuntime{
92+
ObjectMeta: metav1.ObjectMeta{
93+
Name: testName,
94+
Namespace: testNamespace,
95+
},
96+
}
97+
c := fake.NewFakeClientWithScheme(s, rt)
98+
recorder := record.NewFakeRecorder(10)
99+
r := NewRuntimeReconciler(c, fake.NullLogger(), s, recorder)
100+
101+
req := ctrl.Request{
102+
NamespacedName: types.NamespacedName{
103+
Name: testName,
104+
Namespace: testNamespace,
105+
},
106+
}
107+
// Reconcile proceeds past getRuntime, builds the engine, then hits the
108+
// "no dataset bound" branch which returns RequeueAfter(5s) with no error.
109+
result, err := r.Reconcile(context.Background(), req)
110+
Expect(err).ToNot(HaveOccurred())
111+
Expect(result.RequeueAfter).ToNot(BeZero())
112+
})
113+
})
114+
})
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
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 alluxio
18+
19+
import (
20+
"context"
21+
"sync"
22+
23+
"github.com/golang/mock/gomock"
24+
. "github.com/onsi/ginkgo/v2"
25+
. "github.com/onsi/gomega"
26+
27+
datav1alpha1 "github.com/fluid-cloudnative/fluid/api/v1alpha1"
28+
"github.com/fluid-cloudnative/fluid/pkg/controllers"
29+
"github.com/fluid-cloudnative/fluid/pkg/ddc"
30+
"github.com/fluid-cloudnative/fluid/pkg/ddc/base"
31+
basemock "github.com/fluid-cloudnative/fluid/pkg/ddc/base/mock"
32+
cruntime "github.com/fluid-cloudnative/fluid/pkg/runtime"
33+
"github.com/fluid-cloudnative/fluid/pkg/utils/fake"
34+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35+
"k8s.io/apimachinery/pkg/runtime"
36+
"k8s.io/apimachinery/pkg/types"
37+
"k8s.io/client-go/tools/record"
38+
)
39+
40+
// makeReconciler creates a RuntimeReconciler with a fake client for unit tests.
41+
func makeReconciler(s *runtime.Scheme, objs ...runtime.Object) *RuntimeReconciler {
42+
c := fake.NewFakeClientWithScheme(s, objs...)
43+
r := &RuntimeReconciler{
44+
Scheme: s,
45+
mutex: &sync.Mutex{},
46+
engines: map[string]base.Engine{},
47+
}
48+
r.RuntimeReconciler = controllers.NewRuntimeReconciler(r, c, fake.NullLogger(), record.NewFakeRecorder(10))
49+
return r
50+
}
51+
52+
var _ = Describe("implement", func() {
53+
const (
54+
rtName = "test-alluxio"
55+
rtNamespace = "default"
56+
)
57+
58+
var (
59+
s *runtime.Scheme
60+
)
61+
62+
BeforeEach(func() {
63+
s = runtime.NewScheme()
64+
Expect(datav1alpha1.AddToScheme(s)).To(Succeed())
65+
})
66+
67+
Describe("getRuntime", func() {
68+
It("returns the AlluxioRuntime when it exists", func() {
69+
rt := &datav1alpha1.AlluxioRuntime{
70+
ObjectMeta: metav1.ObjectMeta{
71+
Name: rtName,
72+
Namespace: rtNamespace,
73+
},
74+
}
75+
r := makeReconciler(s, rt)
76+
ctx := cruntime.ReconcileRequestContext{
77+
Context: context.Background(),
78+
NamespacedName: types.NamespacedName{Name: rtName, Namespace: rtNamespace},
79+
Log: fake.NullLogger(),
80+
Client: r.Client,
81+
}
82+
83+
got, err := r.getRuntime(ctx)
84+
Expect(err).ToNot(HaveOccurred())
85+
Expect(got).ToNot(BeNil())
86+
Expect(got.Name).To(Equal(rtName))
87+
Expect(got.Namespace).To(Equal(rtNamespace))
88+
})
89+
90+
It("returns an error when the AlluxioRuntime does not exist", func() {
91+
r := makeReconciler(s)
92+
ctx := cruntime.ReconcileRequestContext{
93+
Context: context.Background(),
94+
NamespacedName: types.NamespacedName{Name: "missing", Namespace: rtNamespace},
95+
Log: fake.NullLogger(),
96+
Client: r.Client,
97+
}
98+
99+
_, err := r.getRuntime(ctx)
100+
Expect(err).To(HaveOccurred())
101+
})
102+
})
103+
104+
Describe("RemoveEngine", func() {
105+
It("removes an engine entry from the engines map", func() {
106+
r := makeReconciler(s)
107+
nsn := types.NamespacedName{Name: rtName, Namespace: rtNamespace}
108+
id := ddc.GenerateEngineID(nsn)
109+
110+
// Inject a non-nil stub engine so RemoveEngine has a real entry to delete.
111+
mockCtrl := gomock.NewController(GinkgoT())
112+
var stubEngine base.Engine = basemock.NewMockEngine(mockCtrl)
113+
r.mutex.Lock()
114+
r.engines[id] = stubEngine
115+
r.mutex.Unlock()
116+
117+
ctx := cruntime.ReconcileRequestContext{
118+
Context: context.Background(),
119+
NamespacedName: nsn,
120+
Log: fake.NullLogger(),
121+
}
122+
r.RemoveEngine(ctx)
123+
124+
r.mutex.Lock()
125+
_, exists := r.engines[id]
126+
r.mutex.Unlock()
127+
Expect(exists).To(BeFalse())
128+
})
129+
130+
It("is a no-op when no engine exists for the id", func() {
131+
r := makeReconciler(s)
132+
ctx := cruntime.ReconcileRequestContext{
133+
Context: context.Background(),
134+
NamespacedName: types.NamespacedName{Name: "no-such", Namespace: rtNamespace},
135+
Log: fake.NullLogger(),
136+
}
137+
// Must not panic.
138+
Expect(func() { r.RemoveEngine(ctx) }).ToNot(Panic())
139+
})
140+
})
141+
142+
Describe("GetOrCreateEngine", func() {
143+
It("returns an error when no matching engine builder is registered", func() {
144+
r := makeReconciler(s)
145+
ctx := cruntime.ReconcileRequestContext{
146+
Context: context.Background(),
147+
NamespacedName: types.NamespacedName{Name: rtName, Namespace: rtNamespace},
148+
Log: fake.NullLogger(),
149+
EngineImpl: "unknown-impl",
150+
Client: r.Client,
151+
}
152+
_, err := r.GetOrCreateEngine(ctx)
153+
Expect(err).To(HaveOccurred())
154+
})
155+
})
156+
})

pkg/controllers/v1alpha1/alluxio/suite_test.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ limitations under the License.
1717
package alluxio
1818

1919
import (
20-
"os"
2120
"path/filepath"
2221
"testing"
22+
"time"
2323

2424
. "github.com/onsi/ginkgo/v2"
2525
. "github.com/onsi/gomega"
@@ -40,7 +40,6 @@ import (
4040
var cfg *rest.Config
4141
var k8sClient client.Client
4242
var testEnv *envtest.Environment
43-
var useExistingCluster = false
4443

4544
func TestAPIs(t *testing.T) {
4645
RegisterFailHandler(Fail)
@@ -49,11 +48,8 @@ func TestAPIs(t *testing.T) {
4948
"Controller Suite")
5049
}
5150

52-
var _ = BeforeSuite(func(done Done) {
51+
var _ = BeforeSuite(NodeTimeout(60*time.Second), func() {
5352
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
54-
if env := os.Getenv("USE_EXISTING_CLUSTER"); env == "true" {
55-
useExistingCluster = true
56-
}
5753

5854
By("bootstrapping test environment")
5955
testEnv = &envtest.Environment{
@@ -73,9 +69,7 @@ var _ = BeforeSuite(func(done Done) {
7369
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
7470
Expect(err).ToNot(HaveOccurred())
7571
Expect(k8sClient).ToNot(BeNil())
76-
77-
close(done)
78-
}, 60)
72+
})
7973

8074
var _ = AfterSuite(func() {
8175
By("tearing down the test environment")

0 commit comments

Comments
 (0)