diff --git a/pkg/dataoperation/context_test.go b/pkg/dataoperation/context_test.go new file mode 100644 index 00000000000..b9bbbe29fd2 --- /dev/null +++ b/pkg/dataoperation/context_test.go @@ -0,0 +1,47 @@ +/* +Copyright 2026 The Fluid Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dataoperation + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/types" + + cruntime "github.com/fluid-cloudnative/fluid/pkg/runtime" +) + +var _ = Describe("ReconcileRequestContext", func() { + It("should preserve embedded runtime and operation-specific fields", func() { + context := ReconcileRequestContext{ + ReconcileRequestContext: cruntime.ReconcileRequestContext{ + NamespacedName: types.NamespacedName{ + Namespace: "fluid-system", + Name: "sample-op", + }, + FinalizerName: "fluid-runtime-finalizer", + }, + OpStatus: nil, + DataOpFinalizerName: "fluid.io/finalizer", + } + + Expect(context.NamespacedName).To(Equal(types.NamespacedName{Namespace: "fluid-system", Name: "sample-op"})) + Expect(context.FinalizerName).To(Equal("fluid-runtime-finalizer")) + Expect(context.OpStatus).To(BeNil()) + Expect(context.DataObject).To(BeNil()) + Expect(context.DataOpFinalizerName).To(Equal("fluid.io/finalizer")) + }) +}) diff --git a/pkg/dataoperation/interface_test.go b/pkg/dataoperation/interface_test.go new file mode 100644 index 00000000000..7b08765f68f --- /dev/null +++ b/pkg/dataoperation/interface_test.go @@ -0,0 +1,66 @@ +/* +Copyright 2026 The Fluid Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dataoperation + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("OperationInterface contract", func() { + It("should expose the operation label constant", func() { + Expect(OperationLabel).To(Equal("fluid.io/operation")) + }) + + It("should expose the dataload operation type constant", func() { + Expect(DataLoadType).To(Equal(OperationType("DataLoad"))) + }) + + It("should expose the databackup operation type constant", func() { + Expect(DataBackupType).To(Equal(OperationType("DataBackup"))) + }) + + It("should expose the datamigrate operation type constant", func() { + Expect(DataMigrateType).To(Equal(OperationType("DataMigrate"))) + }) + + It("should expose the dataprocess operation type constant", func() { + Expect(DataProcessType).To(Equal(OperationType("DataProcess"))) + }) + + It("should build a dataload operation with the provided ttl", func() { + ttl := int32(300) + + operation := BuildMockDataloadOperationReconcilerInterface(DataLoadType, &ttl) + + Expect(operation).NotTo(BeNil()) + Expect(operation.GetOperationType()).To(Equal(DataLoadType)) + existingTTL, err := operation.GetTTL() + Expect(err).NotTo(HaveOccurred()) + Expect(existingTTL).To(Equal(&ttl)) + Expect(operation.GetParallelTaskNumber()).To(Equal(int32(1))) + }) + + It("should report a type mismatch when ttl is requested for the wrong operation type", func() { + operation := BuildMockDataloadOperationReconcilerInterface(DataBackupType, nil) + + ttl, err := operation.GetTTL() + + Expect(err).To(MatchError("the dataoperation type is DataBackup, not DataloadType")) + Expect(ttl).To(BeNil()) + }) +}) diff --git a/pkg/dataoperation/mock/mock_operation_test.go b/pkg/dataoperation/mock/mock_operation_test.go new file mode 100644 index 00000000000..9e80a50b797 --- /dev/null +++ b/pkg/dataoperation/mock/mock_operation_test.go @@ -0,0 +1,153 @@ +/* +Copyright 2026 The Fluid Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mock + +import ( + "errors" + + datav1alpha1 "github.com/fluid-cloudnative/fluid/api/v1alpha1" + fluidcommon "github.com/fluid-cloudnative/fluid/pkg/common" + dataoperation "github.com/fluid-cloudnative/fluid/pkg/dataoperation" + flruntime "github.com/fluid-cloudnative/fluid/pkg/runtime" + "github.com/golang/mock/gomock" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/types" +) + +var _ = Describe("generated dataoperation mocks", func() { + var ctrl *gomock.Controller + + BeforeEach(func() { + ctrl = gomock.NewController(GinkgoT()) + }) + + AfterEach(func() { + ctrl.Finish() + }) + + It("should build operation interfaces from the builder mock", func() { + builder := NewMockOperationInterfaceBuilder(ctrl) + operation := NewMockOperationInterface(ctrl) + + builder.EXPECT().Build(gomock.Nil()).Return(operation, nil) + + built, err := builder.Build(nil) + + Expect(err).NotTo(HaveOccurred()) + Expect(built).To(Equal(operation)) + }) + + It("should delegate getters through gomock", func() { + operation := NewMockOperationInterface(ctrl) + ttl := int32(42) + dataset := &datav1alpha1.Dataset{} + namespacedNames := []types.NamespacedName{{Namespace: "fluid", Name: "dataset"}} + releaseName := types.NamespacedName{Namespace: "fluid", Name: "release"} + statusHandler := NewMockStatusHandler(ctrl) + + operation.EXPECT().GetChartsDirectory().Return("/charts") + operation.EXPECT().GetOperationObject().Return(nil) + operation.EXPECT().GetOperationType().Return(dataoperation.DataProcessType) + operation.EXPECT().GetParallelTaskNumber().Return(int32(3)) + operation.EXPECT().GetPossibleTargetDatasetNamespacedNames().Return(namespacedNames) + operation.EXPECT().GetReleaseNameSpacedName().Return(releaseName) + operation.EXPECT().GetStatusHandler().Return(statusHandler) + operation.EXPECT().GetTTL().Return(&ttl, nil) + operation.EXPECT().GetTargetDataset().Return(dataset, nil) + + Expect(operation.GetChartsDirectory()).To(Equal("/charts")) + Expect(operation.GetOperationObject()).To(BeNil()) + Expect(operation.GetOperationType()).To(Equal(dataoperation.DataProcessType)) + Expect(operation.GetParallelTaskNumber()).To(Equal(int32(3))) + Expect(operation.GetPossibleTargetDatasetNamespacedNames()).To(Equal(namespacedNames)) + Expect(operation.GetReleaseNameSpacedName()).To(Equal(releaseName)) + Expect(operation.GetStatusHandler()).To(Equal(statusHandler)) + retrievedTTL, err := operation.GetTTL() + Expect(err).NotTo(HaveOccurred()) + Expect(retrievedTTL).To(Equal(&ttl)) + retrievedDataset, err := operation.GetTargetDataset() + Expect(err).NotTo(HaveOccurred()) + Expect(retrievedDataset).To(Equal(dataset)) + }) + + It("should delegate preceding operation checks through gomock", func() { + operation := NewMockOperationInterface(ctrl) + + operation.EXPECT().HasPrecedingOperation().Return(true) + + Expect(operation.HasPrecedingOperation()).To(BeTrue()) + }) + + It("should delegate dataset progress status updates through gomock", func() { + operation := NewMockOperationInterface(ctrl) + dataset := &datav1alpha1.Dataset{} + + operation.EXPECT().RemoveTargetDatasetStatusInProgress(dataset) + operation.EXPECT().SetTargetDatasetStatusInProgress(dataset) + + operation.RemoveTargetDatasetStatusInProgress(dataset) + operation.SetTargetDatasetStatusInProgress(dataset) + }) + + It("should delegate operation status updates through gomock", func() { + operation := NewMockOperationInterface(ctrl) + opStatus := &datav1alpha1.OperationStatus{} + statusErr := errors.New("status update failed") + + operation.EXPECT().UpdateOperationApiStatus(opStatus).Return(statusErr) + + Expect(operation.UpdateOperationApiStatus(opStatus)).To(MatchError(statusErr)) + }) + + It("should delegate completion status updates through gomock", func() { + operation := NewMockOperationInterface(ctrl) + completionErr := errors.New("completion update failed") + completionInfo := map[string]string{"result": "done"} + + operation.EXPECT().UpdateStatusInfoForCompleted(completionInfo).Return(completionErr) + + Expect(operation.UpdateStatusInfoForCompleted(completionInfo)).To(MatchError(completionErr)) + }) + + It("should delegate validation through gomock", func() { + operation := NewMockOperationInterface(ctrl) + conditions := []datav1alpha1.Condition{{Type: fluidcommon.Complete}} + validateErr := errors.New("validate failed") + ctx := flruntime.ReconcileRequestContext{} + + operation.EXPECT().Validate(gomock.Any()).Return(conditions, validateErr) + + retrievedConditions, err := operation.Validate(ctx) + Expect(err).To(MatchError(validateErr)) + Expect(retrievedConditions).To(Equal(conditions)) + }) + + It("should delegate status handling through gomock", func() { + statusHandler := NewMockStatusHandler(ctrl) + opStatus := &datav1alpha1.OperationStatus{} + updatedStatus := &datav1alpha1.OperationStatus{Duration: "5s"} + statusErr := errors.New("status failed") + + statusHandler.EXPECT().GetOperationStatus(flruntime.ReconcileRequestContext{}, opStatus).Return(updatedStatus, statusErr) + + result, err := statusHandler.GetOperationStatus(flruntime.ReconcileRequestContext{}, opStatus) + + Expect(err).To(MatchError(statusErr)) + Expect(result).To(Equal(updatedStatus)) + }) +}) diff --git a/pkg/dataoperation/mock/mock_suite_test.go b/pkg/dataoperation/mock/mock_suite_test.go new file mode 100644 index 00000000000..18ba62bc5d7 --- /dev/null +++ b/pkg/dataoperation/mock/mock_suite_test.go @@ -0,0 +1,29 @@ +/* +Copyright 2026 The Fluid Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mock + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestMock(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Dataoperation Mock Suite") +} diff --git a/pkg/dataoperation/mock_test.go b/pkg/dataoperation/mock_test.go new file mode 100644 index 00000000000..2e3d9b120a6 --- /dev/null +++ b/pkg/dataoperation/mock_test.go @@ -0,0 +1,49 @@ +/* +Copyright 2026 The Fluid Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dataoperation + +import ( + datav1alpha1 "github.com/fluid-cloudnative/fluid/api/v1alpha1" + "github.com/fluid-cloudnative/fluid/pkg/runtime" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("mockDataloadOperationReconciler", func() { + var operation mockDataloadOperationReconciler + + BeforeEach(func() { + operation = mockDataloadOperationReconciler{} + }) + + DescribeTable("should panic for unimplemented operation methods", func(call func()) { + Expect(call).To(PanicWith("unimplemented")) + }, + Entry("HasPrecedingOperation", func() { operation.HasPrecedingOperation() }), + Entry("GetOperationObject", func() { operation.GetOperationObject() }), + Entry("GetChartsDirectory", func() { operation.GetChartsDirectory() }), + Entry("GetReleaseNameSpacedName", func() { operation.GetReleaseNameSpacedName() }), + Entry("GetStatusHandler", func() { operation.GetStatusHandler() }), + Entry("GetTargetDataset", func() { _, _ = operation.GetTargetDataset() }), + Entry("GetPossibleTargetDatasetNamespacedNames", func() { operation.GetPossibleTargetDatasetNamespacedNames() }), + Entry("RemoveTargetDatasetStatusInProgress", func() { operation.RemoveTargetDatasetStatusInProgress(&datav1alpha1.Dataset{}) }), + Entry("SetTargetDatasetStatusInProgress", func() { operation.SetTargetDatasetStatusInProgress(&datav1alpha1.Dataset{}) }), + Entry("UpdateOperationApiStatus", func() { _ = operation.UpdateOperationApiStatus(&datav1alpha1.OperationStatus{}) }), + Entry("UpdateStatusInfoForCompleted", func() { _ = operation.UpdateStatusInfoForCompleted(map[string]string{"k": "v"}) }), + Entry("Validate", func() { _, _ = operation.Validate(runtime.ReconcileRequestContext{}) }), + ) +}) diff --git a/pkg/dataoperation/suite_test.go b/pkg/dataoperation/suite_test.go new file mode 100644 index 00000000000..7df37af9544 --- /dev/null +++ b/pkg/dataoperation/suite_test.go @@ -0,0 +1,29 @@ +/* +Copyright 2026 The Fluid Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dataoperation + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestDataoperation(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Dataoperation Suite") +}