Skip to content

Commit 63ed41b

Browse files
authored
test(controllers): migrate core pkg/controllers tests to Ginkgo (#5679)
* test(ctrl): add manager and operation controller tests Signed-off-by: Harsh <harshmastic@gmail.com> * test(ctrl): add core engine test helper Signed-off-by: Harsh <harshmastic@gmail.com> * test(ctrl): tighten controller helper coverage from bot review Signed-off-by: Harsh <harshmastic@gmail.com> * test(controllers): isolate GetConfigOrDie test in subprocess Signed-off-by: Harsh <harshmastic@gmail.com> * fix: update copyright year to 2026 in test files Signed-off-by: Harsh <harshmastic@gmail.com> * test(ctrl): address sonarqube bot comments Signed-off-by: Harsh <harshmastic@gmail.com> * test(jindo): improve minio setup and data seeding in e2e script Signed-off-by: Harsh <harshmastic@gmail.com> --------- Signed-off-by: Harsh <harshmastic@gmail.com>
1 parent 70870a5 commit 63ed41b

File tree

5 files changed

+930
-3
lines changed

5 files changed

+930
-3
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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 controllers
18+
19+
import (
20+
ctrl "sigs.k8s.io/controller-runtime"
21+
22+
datav1alpha1 "github.com/fluid-cloudnative/fluid/api/v1alpha1"
23+
"github.com/fluid-cloudnative/fluid/pkg/dataoperation"
24+
cruntime "github.com/fluid-cloudnative/fluid/pkg/runtime"
25+
)
26+
27+
type fakeEngineCore struct {
28+
id string
29+
}
30+
31+
func (e *fakeEngineCore) ID() string { return e.id }
32+
33+
func (e *fakeEngineCore) Shutdown() error { return nil }
34+
35+
func (e *fakeEngineCore) Setup(ctx cruntime.ReconcileRequestContext) (bool, error) {
36+
return true, nil
37+
}
38+
39+
func (e *fakeEngineCore) CreateVolume() error { return nil }
40+
41+
func (e *fakeEngineCore) DeleteVolume() error { return nil }
42+
43+
func (e *fakeEngineCore) Sync(ctx cruntime.ReconcileRequestContext) error { return nil }
44+
45+
func (e *fakeEngineCore) Validate(ctx cruntime.ReconcileRequestContext) error { return nil }
46+
47+
func (e *fakeEngineCore) Operate(ctx cruntime.ReconcileRequestContext, opStatus *datav1alpha1.OperationStatus, operation dataoperation.OperationInterface) (ctrl.Result, error) {
48+
return ctrl.Result{}, nil
49+
}

pkg/controllers/manager_test.go

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
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 controllers
18+
19+
import (
20+
"os"
21+
"os/exec"
22+
"path/filepath"
23+
"time"
24+
25+
. "github.com/onsi/ginkgo/v2"
26+
. "github.com/onsi/gomega"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
28+
)
29+
30+
const testItem = "test-item"
31+
32+
var _ = Describe("NewFluidControllerRateLimiter", func() {
33+
It("should create a rate limiter with valid parameters", func() {
34+
limiter := NewFluidControllerRateLimiter(
35+
5*time.Millisecond,
36+
1000*time.Second,
37+
10,
38+
100,
39+
)
40+
Expect(limiter).NotTo(BeNil())
41+
})
42+
43+
It("should return increasing delays for repeated failures on the same item", func() {
44+
limiter := NewFluidControllerRateLimiter(
45+
5*time.Millisecond,
46+
1000*time.Second,
47+
10,
48+
100,
49+
)
50+
first := limiter.When(testItem)
51+
second := limiter.When(testItem)
52+
Expect(second).To(BeNumerically(">=", first))
53+
})
54+
55+
It("should return different delays for different items", func() {
56+
limiter := NewFluidControllerRateLimiter(
57+
5*time.Millisecond,
58+
1000*time.Second,
59+
10,
60+
100,
61+
)
62+
// Advance item-a several times to increase its delay
63+
for i := 0; i < 5; i++ {
64+
limiter.When("item-a")
65+
}
66+
delayA := limiter.When("item-a")
67+
delayB := limiter.When("item-b")
68+
Expect(delayA).To(BeNumerically(">", delayB))
69+
})
70+
71+
It("should respect the max delay parameter", func() {
72+
maxDelay := 100 * time.Millisecond
73+
limiter := NewFluidControllerRateLimiter(
74+
5*time.Millisecond,
75+
maxDelay,
76+
10,
77+
100,
78+
)
79+
// Push the item through many failures
80+
for i := 0; i < 100; i++ {
81+
limiter.When(testItem)
82+
}
83+
delay := limiter.When(testItem)
84+
Expect(delay).To(BeNumerically("<=", maxDelay))
85+
})
86+
87+
It("should reset delay after forgetting an item", func() {
88+
limiter := NewFluidControllerRateLimiter(
89+
5*time.Millisecond,
90+
1000*time.Second,
91+
10,
92+
100,
93+
)
94+
for i := 0; i < 10; i++ {
95+
limiter.When(testItem)
96+
}
97+
limiter.Forget(testItem)
98+
delay := limiter.When(testItem)
99+
firstDelay := limiter.When("fresh-item")
100+
// After forget, the item's delay should be close to the initial value
101+
Expect(delay).To(BeNumerically("<=", firstDelay*2))
102+
})
103+
})
104+
105+
var _ = Describe("manager client and config helpers", func() {
106+
Describe("NewFluidControllerClient", func() {
107+
var (
108+
originalVal string
109+
wasSet bool
110+
)
111+
112+
BeforeEach(func() {
113+
originalVal, wasSet = os.LookupEnv("HELM_DRIVER")
114+
})
115+
116+
AfterEach(func() {
117+
if wasSet {
118+
Expect(os.Setenv("HELM_DRIVER", originalVal)).To(Succeed())
119+
} else {
120+
Expect(os.Unsetenv("HELM_DRIVER")).To(Succeed())
121+
}
122+
})
123+
124+
It("returns error on secret driver path with nil rest config", func() {
125+
Expect(os.Setenv("HELM_DRIVER", "secret")).To(Succeed())
126+
127+
c, err := NewFluidControllerClient(nil, client.Options{})
128+
Expect(err).To(HaveOccurred())
129+
Expect(c).To(BeNil())
130+
})
131+
132+
It("returns error on cache-bypass path with nil rest config", func() {
133+
Expect(os.Setenv("HELM_DRIVER", "configmap")).To(Succeed())
134+
135+
c, err := NewFluidControllerClient(nil, client.Options{Cache: &client.CacheOptions{}})
136+
Expect(err).To(HaveOccurred())
137+
Expect(c).To(BeNil())
138+
})
139+
})
140+
141+
Describe("NewCacheClientBypassSecrets", func() {
142+
It("returns error with nil rest config", func() {
143+
c, err := NewCacheClientBypassSecrets(nil, client.Options{Cache: &client.CacheOptions{}})
144+
Expect(err).To(HaveOccurred())
145+
Expect(c).To(BeNil())
146+
})
147+
})
148+
149+
Describe("GetConfigOrDieWithQPSAndBurst", func() {
150+
var (
151+
originalKubeconfig string
152+
wasSet bool
153+
)
154+
155+
BeforeEach(func() {
156+
originalKubeconfig, wasSet = os.LookupEnv("KUBECONFIG")
157+
})
158+
159+
AfterEach(func() {
160+
if wasSet {
161+
Expect(os.Setenv("KUBECONFIG", originalKubeconfig)).To(Succeed())
162+
} else {
163+
Expect(os.Unsetenv("KUBECONFIG")).To(Succeed())
164+
}
165+
})
166+
167+
It("sets qps and burst when both are positive", func() {
168+
if os.Getenv("FLUID_GET_CONFIG_SUBPROCESS") == "1" {
169+
cfg := GetConfigOrDieWithQPSAndBurst(123, 456)
170+
if cfg == nil || cfg.QPS != float32(123) || cfg.Burst != 456 {
171+
os.Exit(2)
172+
}
173+
os.Exit(0)
174+
}
175+
176+
tmpDir := GinkgoT().TempDir()
177+
kubeconfig := filepath.Join(tmpDir, "config")
178+
content := `apiVersion: v1
179+
kind: Config
180+
clusters:
181+
- name: local
182+
cluster:
183+
server: https://127.0.0.1:65535
184+
insecure-skip-tls-verify: true
185+
users:
186+
- name: local-user
187+
user:
188+
token: fake-token
189+
contexts:
190+
- name: local
191+
context:
192+
cluster: local
193+
user: local-user
194+
current-context: local
195+
`
196+
Expect(os.WriteFile(kubeconfig, []byte(content), 0o600)).To(Succeed())
197+
Expect(os.Setenv("KUBECONFIG", kubeconfig)).To(Succeed())
198+
199+
cmd := exec.Command(os.Args[0], "-test.run=TestControllers", "-ginkgo.focus=sets qps and burst when both are positive")
200+
cmd.Env = append(os.Environ(), "FLUID_GET_CONFIG_SUBPROCESS=1", "KUBECONFIG="+kubeconfig)
201+
out, err := cmd.CombinedOutput()
202+
Expect(err).NotTo(HaveOccurred(), string(out))
203+
})
204+
})
205+
})

0 commit comments

Comments
 (0)