Skip to content

test(vineyard): migrate controller tests to Ginkgo/Gomega#5699

Open
hxrshxz wants to merge 2 commits intofluid-cloudnative:masterfrom
hxrshxz:test/vineyard-controller-ginkgo-migration
Open

test(vineyard): migrate controller tests to Ginkgo/Gomega#5699
hxrshxz wants to merge 2 commits intofluid-cloudnative:masterfrom
hxrshxz:test/vineyard-controller-ginkgo-migration

Conversation

@hxrshxz
Copy link
Contributor

@hxrshxz hxrshxz commented Mar 18, 2026

Ⅰ. Describe what this PR does

Add Ginkgo v2/Gomega unit tests for pkg/controllers/v1alpha1/vineyard/ and add the missing VineyardRuntimeKind constant used by the migrated tests.

Ⅱ. Does this pull request fix one issue?

NONE

Ⅲ. List the added test cases (unit test/integration test) if any, please explain if no tests are needed.

  • Add suite_test.go Ginkgo bootstrap for pkg/controllers/v1alpha1/vineyard/
  • Add unit coverage for getRuntime, GetOrCreateEngine, RemoveEngine, NewRuntimeReconciler, ControllerName, and Reconcile

Ⅳ. Describe how to verify it

make fmt
go build ./api/v1alpha1/...
go test -coverprofile=/tmp/fluid-vineyard-pr.cover ./pkg/controllers/v1alpha1/vineyard/... -count=1 -v
go tool cover -func=/tmp/fluid-vineyard-pr.cover

Ⅴ. Special notes for reviews

N/A

Add Ginkgo v2/Gomega test suite for pkg/controllers/v1alpha1/vineyard/
covering getRuntime, GetOrCreateEngine, RemoveEngine, NewRuntimeReconciler,
ControllerName, and Reconcile. All 11 specs pass with 92.3% package coverage.

Also add VineyardRuntimeKind constant to api/v1alpha1/vineyardruntime_types.go,
filling the same gap that exists for all other runtimes (JindoRuntimeKind,
EFCRuntimeKind, ThinRuntimeKind, JuiceFSRuntimeKind).

Signed-off-by: Harsh <harshmastic@gmail.com>
Copilot AI review requested due to automatic review settings March 18, 2026 14:52
@fluid-e2e-bot
Copy link

fluid-e2e-bot bot commented Mar 18, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign ronggu for approval by writing /assign @ronggu in a comment. For more information see:The Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@fluid-e2e-bot
Copy link

fluid-e2e-bot bot commented Mar 18, 2026

Hi @hxrshxz. Thanks for your PR.

I'm waiting for a fluid-cloudnative member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request migrates the controller tests for the Vineyard controller to Ginkgo/Gomega, providing a more structured and readable testing framework. It also adds a missing constant and increases unit test coverage for several key functions within the controller.

Highlights

  • Ginkgo/Gomega Migration: Migrated controller tests for pkg/controllers/v1alpha1/vineyard/ to Ginkgo v2/Gomega.
  • Missing Constant: Added the missing VineyardRuntimeKind constant.
  • Unit Test Coverage: Added unit test coverage for getRuntime, GetOrCreateEngine, RemoveEngine, NewRuntimeReconciler, ControllerName, and Reconcile.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates pkg/controllers/v1alpha1/vineyard/ controller unit tests to Ginkgo v2/Gomega and adds a missing API constant to support the new tests.

Changes:

  • Added Ginkgo test suite bootstrap for the vineyard controller package.
  • Added Ginkgo/Gomega unit tests covering runtime retrieval, engine caching/removal, reconciler construction, controller naming, and selected reconcile paths.
  • Added VineyardRuntimeKind constant in the Vineyard API types.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
pkg/controllers/v1alpha1/vineyard/suite_test.go Adds Ginkgo v2 suite bootstrap for the vineyard controller tests.
pkg/controllers/v1alpha1/vineyard/implement_test.go Adds Ginkgo unit tests for getRuntime, GetOrCreateEngine, and RemoveEngine.
pkg/controllers/v1alpha1/vineyard/controller_test.go Adds Ginkgo unit tests for reconciler construction, controller name, and reconcile behavior.
api/v1alpha1/vineyardruntime_types.go Introduces VineyardRuntimeKind constant used by migrated tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive unit tests for the VineyardRuntime controller and its associated implementation logic, covering scenarios like controller initialization, reconciliation of existing and non-existent runtimes, handling invalid runtime names, and engine lifecycle management (creation, caching, and removal). Additionally, a new constant VineyardRuntimeKind has been added to the API types.

@codecov
Copy link

codecov bot commented Mar 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.22%. Comparing base (50940f8) to head (3facfb1).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #5699   +/-   ##
=======================================
  Coverage   61.22%   61.22%           
=======================================
  Files         444      444           
  Lines       30557    30557           
=======================================
  Hits        18710    18710           
  Misses      10307    10307           
  Partials     1540     1540           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@hxrshxz
Copy link
Contributor Author

hxrshxz commented Mar 18, 2026

/gemini review

1 similar comment
@hxrshxz
Copy link
Contributor Author

hxrshxz commented Mar 18, 2026

/gemini review

Signed-off-by: Harsh <harshmastic@gmail.com>
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request migrates the controller tests for vineyard to use the Ginkgo/Gomega framework, improving the structure and readability of the tests. The changes include adding new test files (controller_test.go, implement_test.go, suite_test.go) and a necessary constant VineyardRuntimeKind.

My review focuses on improving the maintainability of the new tests by reducing code duplication. I've suggested using BeforeEach blocks for common test setup, which will make the tests cleaner and more consistent. This also addresses a minor inconsistency in one of the test cases.

Comment on lines +34 to +120
var _ = Describe("VineyardRuntime Controller", func() {
Describe("NewRuntimeReconciler", func() {
It("should create a RuntimeReconciler with non-nil fields", func() {
s := runtime.NewScheme()
Expect(datav1alpha1.AddToScheme(s)).NotTo(HaveOccurred())
mockClient := fake.NewFakeClientWithScheme(s)
recorder := record.NewFakeRecorder(16)

r := NewRuntimeReconciler(mockClient, fake.NullLogger(), s, recorder)

Expect(r).NotTo(BeNil())
Expect(r.Scheme).NotTo(BeNil())
Expect(r.engines).NotTo(BeNil())
Expect(r.mutex).NotTo(BeNil())
Expect(r.RuntimeReconciler).NotTo(BeNil())
})
})

Describe("ControllerName", func() {
It("should return the correct controller name", func() {
s := runtime.NewScheme()
mockClient := fake.NewFakeClientWithScheme(s)
recorder := record.NewFakeRecorder(16)

r := NewRuntimeReconciler(mockClient, fake.NullLogger(), s, recorder)

Expect(r.ControllerName()).To(Equal(controllerName))
})
})

Describe("Reconcile", func() {
It("should return empty result when runtime is not found", func() {
s := runtime.NewScheme()
Expect(datav1alpha1.AddToScheme(s)).NotTo(HaveOccurred())
mockClient := fake.NewFakeClientWithScheme(s)
recorder := record.NewFakeRecorder(16)

r := NewRuntimeReconciler(mockClient, fake.NullLogger(), s, recorder)

req := ctrl.Request{
NamespacedName: types.NamespacedName{
Name: "nonexistent-runtime",
Namespace: "default",
},
}

result, err := r.Reconcile(context.TODO(), req)
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal(ctrl.Result{}))
})

It("should requeue with error when runtime name violates the controller DNS-1035 validation rule", func() {
s := runtime.NewScheme()
Expect(datav1alpha1.AddToScheme(s)).NotTo(HaveOccurred())

// Runtime name starting with a digit fails the controller's DNS-1035 validation.
invalidName := "20-vineyard"
vineyardRuntime := &datav1alpha1.VineyardRuntime{
ObjectMeta: metav1.ObjectMeta{
Name: invalidName,
Namespace: "default",
},
}
dataset := &datav1alpha1.Dataset{
ObjectMeta: metav1.ObjectMeta{
Name: invalidName,
Namespace: "default",
},
}

mockClient := fake.NewFakeClientWithScheme(s, vineyardRuntime, dataset)
recorder := record.NewFakeRecorder(16)
r := NewRuntimeReconciler(mockClient, fake.NullLogger(), s, recorder)

req := ctrl.Request{
NamespacedName: types.NamespacedName{
Name: invalidName,
Namespace: "default",
},
}

_, err := r.Reconcile(context.TODO(), req)
// Invalid runtime name causes an error path
Expect(err).To(HaveOccurred())
})
})
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's repeated setup code across the test cases, such as initializing the runtime scheme. This can be consolidated into a BeforeEach block to improve code readability and maintainability.

Additionally, the test case for ControllerName is missing the call to datav1alpha1.AddToScheme(s), which is inconsistent with other tests and could lead to failures if the code under test requires the scheme to be aware of datav1alpha1 types.

Consider refactoring to use a BeforeEach block for common setup, which will also fix the inconsistency and reduce code duplication.

Comment on lines +35 to +282
var _ = Describe("VineyardRuntime Implement", func() {
const (
runtimeName = "test-vineyard"
runtimeNamespace = "fluid"
)

Describe("getRuntime", func() {
It("should return the runtime when it exists", func() {
s := runtime.NewScheme()
Expect(datav1alpha1.AddToScheme(s)).NotTo(HaveOccurred())

vineyardRuntime := &datav1alpha1.VineyardRuntime{
ObjectMeta: metav1.ObjectMeta{
Name: runtimeName,
Namespace: runtimeNamespace,
},
}

mockClient := fake.NewFakeClientWithScheme(s, vineyardRuntime)
recorder := record.NewFakeRecorder(16)
r := NewRuntimeReconciler(mockClient, fake.NullLogger(), s, recorder)

ctx := cruntime.ReconcileRequestContext{
Context: context.TODO(),
NamespacedName: types.NamespacedName{
Name: runtimeName,
Namespace: runtimeNamespace,
},
Client: mockClient,
Log: fake.NullLogger(),
}

got, err := r.getRuntime(ctx)
Expect(err).NotTo(HaveOccurred())
Expect(got).NotTo(BeNil())
Expect(got.Name).To(Equal(runtimeName))
Expect(got.Namespace).To(Equal(runtimeNamespace))
})

It("should return error when runtime does not exist", func() {
s := runtime.NewScheme()
Expect(datav1alpha1.AddToScheme(s)).NotTo(HaveOccurred())

mockClient := fake.NewFakeClientWithScheme(s)
recorder := record.NewFakeRecorder(16)
r := NewRuntimeReconciler(mockClient, fake.NullLogger(), s, recorder)

ctx := cruntime.ReconcileRequestContext{
Context: context.TODO(),
NamespacedName: types.NamespacedName{
Name: "nonexistent",
Namespace: runtimeNamespace,
},
Client: mockClient,
Log: fake.NullLogger(),
}

got, err := r.getRuntime(ctx)
Expect(err).To(HaveOccurred())
Expect(got).To(BeNil())
})
})

Describe("GetOrCreateEngine", func() {
It("should create a new engine when not present in cache", func() {
s := runtime.NewScheme()
Expect(datav1alpha1.AddToScheme(s)).NotTo(HaveOccurred())

vineyardRuntime := &datav1alpha1.VineyardRuntime{
ObjectMeta: metav1.ObjectMeta{
Name: runtimeName,
Namespace: runtimeNamespace,
},
}
dataset := &datav1alpha1.Dataset{
ObjectMeta: metav1.ObjectMeta{
Name: runtimeName,
Namespace: runtimeNamespace,
},
}

mockClient := fake.NewFakeClientWithScheme(s, vineyardRuntime, dataset)
recorder := record.NewFakeRecorder(16)
r := NewRuntimeReconciler(mockClient, fake.NullLogger(), s, recorder)

ctx := cruntime.ReconcileRequestContext{
Context: context.TODO(),
NamespacedName: types.NamespacedName{
Name: runtimeName,
Namespace: runtimeNamespace,
},
Client: mockClient,
Log: fake.NullLogger(),
RuntimeType: common.VineyardRuntime,
EngineImpl: common.VineyardEngineImpl,
Runtime: vineyardRuntime,
}

engine, err := r.GetOrCreateEngine(ctx)
Expect(err).NotTo(HaveOccurred())
Expect(engine).NotTo(BeNil())
})

It("should return cached engine on second call with same context", func() {
s := runtime.NewScheme()
Expect(datav1alpha1.AddToScheme(s)).NotTo(HaveOccurred())

vineyardRuntime := &datav1alpha1.VineyardRuntime{
ObjectMeta: metav1.ObjectMeta{
Name: runtimeName,
Namespace: runtimeNamespace,
},
}
dataset := &datav1alpha1.Dataset{
ObjectMeta: metav1.ObjectMeta{
Name: runtimeName,
Namespace: runtimeNamespace,
},
}

mockClient := fake.NewFakeClientWithScheme(s, vineyardRuntime, dataset)
recorder := record.NewFakeRecorder(16)
r := NewRuntimeReconciler(mockClient, fake.NullLogger(), s, recorder)

ctx := cruntime.ReconcileRequestContext{
Context: context.TODO(),
NamespacedName: types.NamespacedName{
Name: runtimeName,
Namespace: runtimeNamespace,
},
Client: mockClient,
Log: fake.NullLogger(),
RuntimeType: common.VineyardRuntime,
EngineImpl: common.VineyardEngineImpl,
Runtime: vineyardRuntime,
}

engine1, err := r.GetOrCreateEngine(ctx)
Expect(err).NotTo(HaveOccurred())

engine2, err := r.GetOrCreateEngine(ctx)
Expect(err).NotTo(HaveOccurred())

// Both calls should return the same engine instance (cached)
Expect(engine1).To(BeIdenticalTo(engine2))
})

It("should return error for unknown engine impl", func() {
s := runtime.NewScheme()
Expect(datav1alpha1.AddToScheme(s)).NotTo(HaveOccurred())

vineyardRuntime := &datav1alpha1.VineyardRuntime{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: runtimeNamespace,
},
}

mockClient := fake.NewFakeClientWithScheme(s, vineyardRuntime)
recorder := record.NewFakeRecorder(16)
r := NewRuntimeReconciler(mockClient, fake.NullLogger(), s, recorder)

ctx := cruntime.ReconcileRequestContext{
Context: context.TODO(),
NamespacedName: types.NamespacedName{
Name: "test",
Namespace: runtimeNamespace,
},
Client: mockClient,
Log: fake.NullLogger(),
RuntimeType: common.VineyardRuntime,
EngineImpl: "unknown-engine-impl",
Runtime: vineyardRuntime,
}

_, err := r.GetOrCreateEngine(ctx)
Expect(err).To(HaveOccurred())
})
})

Describe("RemoveEngine", func() {
It("should remove an engine from the cache", func() {
s := runtime.NewScheme()
Expect(datav1alpha1.AddToScheme(s)).NotTo(HaveOccurred())

vineyardRuntime := &datav1alpha1.VineyardRuntime{
ObjectMeta: metav1.ObjectMeta{
Name: runtimeName,
Namespace: runtimeNamespace,
},
}
dataset := &datav1alpha1.Dataset{
ObjectMeta: metav1.ObjectMeta{
Name: runtimeName,
Namespace: runtimeNamespace,
},
}

mockClient := fake.NewFakeClientWithScheme(s, vineyardRuntime, dataset)
recorder := record.NewFakeRecorder(16)
r := NewRuntimeReconciler(mockClient, fake.NullLogger(), s, recorder)

ctx := cruntime.ReconcileRequestContext{
Context: context.TODO(),
NamespacedName: types.NamespacedName{
Name: runtimeName,
Namespace: runtimeNamespace,
},
Client: mockClient,
Log: fake.NullLogger(),
RuntimeType: common.VineyardRuntime,
EngineImpl: common.VineyardEngineImpl,
Runtime: vineyardRuntime,
}

// First create an engine
_, err := r.GetOrCreateEngine(ctx)
Expect(err).NotTo(HaveOccurred())

// Remove it
r.RemoveEngine(ctx)

// engines map should now be empty
r.mutex.Lock()
defer r.mutex.Unlock()
Expect(r.engines).To(BeEmpty())
})

It("should be a no-op when engine is not in cache", func() {
s := runtime.NewScheme()
mockClient := fake.NewFakeClientWithScheme(s)
recorder := record.NewFakeRecorder(16)
r := NewRuntimeReconciler(mockClient, fake.NullLogger(), s, recorder)

ctx := cruntime.ReconcileRequestContext{
Context: context.TODO(),
NamespacedName: types.NamespacedName{
Name: "missing",
Namespace: runtimeNamespace,
},
Log: fake.NullLogger(),
}

// Should not panic or error
Expect(func() { r.RemoveEngine(ctx) }).NotTo(Panic())
})
})
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is significant code duplication for test setup across different Describe and It blocks. For example, runtime.NewScheme(), datav1alpha1.AddToScheme(s), and record.NewFakeRecorder(16) are called in almost every test case.

To improve maintainability and reduce redundancy, you can use BeforeEach blocks at appropriate levels (Describe or Context) to handle this common setup. This will make the tests cleaner and easier to manage.

@sonarqubecloud
Copy link

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request migrates the unit tests for the Vineyard controller to Ginkgo/Gomega, which is a good improvement for test structure and readability. The new tests cover the main functionalities of the reconciler and its helper methods. I've found a few critical issues in the new test code that would prevent compilation. Please see my comments for details.

},
Client: mockClient,
Log: fake.NullLogger(),
RuntimeType: common.VineyardRuntime,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The constant common.VineyardRuntime does not appear to be defined in the pkg/common package, which will lead to a compilation error. Please use the string literal "vineyard" instead, which corresponds to the runtimeType for Vineyard.

Suggested change
RuntimeType: common.VineyardRuntime,
RuntimeType: "vineyard",

},
Client: mockClient,
Log: fake.NullLogger(),
RuntimeType: common.VineyardRuntime,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The constant common.VineyardRuntime does not appear to be defined in the pkg/common package, which will lead to a compilation error. Please use the string literal "vineyard" instead.

Suggested change
RuntimeType: common.VineyardRuntime,
RuntimeType: "vineyard",

},
Client: mockClient,
Log: fake.NullLogger(),
RuntimeType: common.VineyardRuntime,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The constant common.VineyardRuntime does not appear to be defined in the pkg/common package, which will lead to a compilation error. Please use the string literal "vineyard" instead.

Suggested change
RuntimeType: common.VineyardRuntime,
RuntimeType: "vineyard",

},
Client: mockClient,
Log: fake.NullLogger(),
RuntimeType: common.VineyardRuntime,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The constant common.VineyardRuntime does not appear to be defined in the pkg/common package, which will lead to a compilation error. Please use the string literal "vineyard" instead.

Suggested change
RuntimeType: common.VineyardRuntime,
RuntimeType: "vineyard",

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants