Skip to content

Commit

Permalink
feat(common): add ut for common tasks and refactor task utils (#6010)
Browse files Browse the repository at this point in the history
Signed-off-by: liubo02 <[email protected]>
  • Loading branch information
liubog2008 authored Dec 25, 2024
1 parent a716b34 commit 749fbbb
Show file tree
Hide file tree
Showing 25 changed files with 1,482 additions and 381 deletions.
29 changes: 18 additions & 11 deletions pkg/client/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,24 @@ import "sigs.k8s.io/controller-runtime/pkg/client"

// Add alias of client.XXX to avoid import
// two client pkgs
type Object = client.Object
type ObjectList = client.ObjectList
type ObjectKey = client.ObjectKey

type Options = client.Options
type DeleteOption = client.DeleteOption

type MatchingLabels = client.MatchingLabels
type MatchingFields = client.MatchingFields
type InNamespace = client.InNamespace
type ListOptions = client.ListOptions
type (
Object = client.Object
ObjectList = client.ObjectList
ObjectKey = client.ObjectKey
)

type (
Options = client.Options
DeleteOption = client.DeleteOption
ListOption = client.ListOption
)

type (
MatchingLabels = client.MatchingLabels
MatchingFields = client.MatchingFields
InNamespace = client.InNamespace
ListOptions = client.ListOptions
)

type PropagationPolicy = client.PropagationPolicy

Expand Down
75 changes: 46 additions & 29 deletions pkg/client/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,34 @@ func (*fakeParser) Type(schema.GroupVersionKind) *typed.ParseableType {
return &typed.DeducedParseableType
}

func NewFakeClient(objs ...client.Object) Client {
c := newFakeUnderlayClient(objs...)
type FakeClient interface {
Client
WithError(verb, resource string, err error)
}

func NewFakeClient(objs ...client.Object) FakeClient {
fc := newFakeUnderlayClient(objs...)

return &applier{
WithWatch: c,
parser: &fakeParser{},
return &fakeClient{
Client: &applier{
WithWatch: fc,
parser: &fakeParser{},
},
fc: fc,
}
}

type fakeClient struct {
Client
fc *fakeUnderlayClient
}

func (fc *fakeClient) WithError(verb, resource string, err error) {
fc.fc.PrependReactor(verb, resource, func(_ testing.Action) (bool, runtime.Object, error) {
return true, nil, err
})
}

type fakeUnderlayClient struct {
testing.Fake
tracker testing.ObjectTracker
Expand All @@ -67,7 +86,7 @@ type fakeUnderlayClient struct {

var _ client.WithWatch = &fakeUnderlayClient{}

func newFakeUnderlayClient(objs ...client.Object) client.WithWatch {
func newFakeUnderlayClient(objs ...client.Object) *fakeUnderlayClient {
t := testing.NewObjectTracker(scheme.Scheme, scheme.Codecs.UniversalDecoder())
for _, obj := range objs {
if err := t.Add(obj); err != nil {
Expand Down Expand Up @@ -536,47 +555,45 @@ func (c *fakeUnderlayClient) PatchReactionFunc(action *testing.PatchActionImpl)

switch action.GetPatchType() {
case types.JSONPatchType:
patch, err2 := jsonpatch.DecodePatch(action.GetPatch())
if err2 != nil {
return true, nil, err2
patch, err := jsonpatch.DecodePatch(action.GetPatch())
if err != nil {
return true, nil, err
}
modified, err2 := patch.Apply(old)
if err2 != nil {
return true, nil, err2
modified, err := patch.Apply(old)
if err != nil {
return true, nil, err
}

//nolint:gocritic // use := shadow err
if err2 = json.Unmarshal(modified, obj); err2 != nil {
return true, nil, err2
if err := json.Unmarshal(modified, obj); err != nil {
return true, nil, err
}
case types.MergePatchType:
modified, err2 := jsonpatch.MergePatch(old, action.GetPatch())
if err2 != nil {
return true, nil, err2
modified, err := jsonpatch.MergePatch(old, action.GetPatch())
if err != nil {
return true, nil, err
}

//nolint:gocritic // use := shadow err
if err2 = json.Unmarshal(modified, obj); err2 != nil {
return true, nil, err2
if err := json.Unmarshal(modified, obj); err != nil {
return true, nil, err
}
case types.StrategicMergePatchType:
mergedByte, err2 := strategicpatch.StrategicMergePatch(old, action.GetPatch(), obj)
if err2 != nil {
return true, nil, err2
mergedByte, err := strategicpatch.StrategicMergePatch(old, action.GetPatch(), obj)
if err != nil {
return true, nil, err
}
//nolint:gocritic // use := shadow err
if err2 = json.Unmarshal(mergedByte, obj); err2 != nil {
return true, nil, err2
if err := json.Unmarshal(mergedByte, obj); err != nil {
return true, nil, err
}
case types.ApplyPatchType:
patchObj := &unstructured.Unstructured{Object: map[string]any{}}
if err = yaml.Unmarshal(action.GetPatch(), &patchObj.Object); err != nil {
if err := yaml.Unmarshal(action.GetPatch(), &patchObj.Object); err != nil {
return true, nil, fmt.Errorf("error decoding YAML: %w", err)
}
obj, err = manager.Apply(obj, patchObj, "tidb-operator", true)
ret, err := manager.Apply(obj, patchObj, "tidb-operator", true)
if err != nil {
return true, nil, err
}
obj = ret

default:
return true, nil, fmt.Errorf("PatchType is not supported")
Expand Down
8 changes: 4 additions & 4 deletions pkg/controllers/cluster/tasks/finalizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ func TestFinalizer(t *testing.T) {
},
{
desc: "removed finalizer",
cluster: fake.FakeObj[v1alpha1.Cluster]("test",
fake.SetDeleteTimestamp[v1alpha1.Cluster](), fake.AddFinalizer[v1alpha1.Cluster]()),
cluster: fake.FakeObj("test",
fake.DeleteNow[v1alpha1.Cluster](), fake.AddFinalizer[v1alpha1.Cluster]()),
expected: task.Complete().Break().With("removed finalizer"),
},
{
desc: "deleting components",
cluster: fake.FakeObj[v1alpha1.Cluster]("test",
fake.SetDeleteTimestamp[v1alpha1.Cluster](), fake.AddFinalizer[v1alpha1.Cluster]()),
cluster: fake.FakeObj("test",
fake.DeleteNow[v1alpha1.Cluster](), fake.AddFinalizer[v1alpha1.Cluster]()),
pdGroup: fake.FakeObj[v1alpha1.PDGroup]("pd-group"),
expected: task.Fail().With("deleting components"),
hasFinalizer: true,
Expand Down
16 changes: 8 additions & 8 deletions pkg/controllers/common/cond.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,26 @@ package common

import "github.com/pingcap/tidb-operator/pkg/utils/task/v3"

func CondPDHasBeenDeleted(ctx PDGetter) task.Condition {
func CondPDHasBeenDeleted(ctx PDState) task.Condition {
return task.CondFunc(func() bool {
return ctx.GetPD() == nil
return ctx.PD() == nil
})
}

func CondPDIsDeleting(ctx PDGetter) task.Condition {
func CondPDIsDeleting(ctx PDState) task.Condition {
return task.CondFunc(func() bool {
return !ctx.GetPD().GetDeletionTimestamp().IsZero()
return !ctx.PD().GetDeletionTimestamp().IsZero()
})
}

func CondClusterIsSuspending(ctx ClusterGetter) task.Condition {
func CondClusterIsSuspending(ctx ClusterState) task.Condition {
return task.CondFunc(func() bool {
return ctx.GetCluster().ShouldSuspendCompute()
return ctx.Cluster().ShouldSuspendCompute()
})
}

func CondClusterIsPaused(ctx ClusterGetter) task.Condition {
func CondClusterIsPaused(ctx ClusterState) task.Condition {
return task.CondFunc(func() bool {
return ctx.GetCluster().ShouldPauseReconcile()
return ctx.Cluster().ShouldPauseReconcile()
})
}
162 changes: 162 additions & 0 deletions pkg/controllers/common/cond_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright 2024 PingCAP, Inc.
//
// 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 common

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/pingcap/tidb-operator/apis/core/v1alpha1"
"github.com/pingcap/tidb-operator/pkg/utils/fake"
)

func TestCondPDHasBeenDeleted(t *testing.T) {
cases := []struct {
desc string
state *fakeState[v1alpha1.PD]
expectedCond bool
}{
{
desc: "cond is false",
state: &fakeState[v1alpha1.PD]{
obj: fake.FakeObj[v1alpha1.PD]("test"),
},
},
{
desc: "cond is true",
state: &fakeState[v1alpha1.PD]{},
expectedCond: true,
},
}

for i := range cases {
c := &cases[i]
t.Run(c.desc, func(tt *testing.T) {
tt.Parallel()

s := &fakePDState{s: c.state}
cond := CondPDHasBeenDeleted(s)
assert.Equal(tt, c.expectedCond, cond.Satisfy(), c.desc)
})
}
}

func TestCondPDIsDeleting(t *testing.T) {
cases := []struct {
desc string
state *fakeState[v1alpha1.PD]
expectedCond bool
}{
{
desc: "cond is false",
state: &fakeState[v1alpha1.PD]{
obj: fake.FakeObj[v1alpha1.PD]("test"),
},
},
{
desc: "cond is true",
state: &fakeState[v1alpha1.PD]{
obj: fake.FakeObj("test", fake.DeleteNow[v1alpha1.PD]()),
},
expectedCond: true,
},
}

for i := range cases {
c := &cases[i]
t.Run(c.desc, func(tt *testing.T) {
tt.Parallel()

s := &fakePDState{s: c.state}
cond := CondPDIsDeleting(s)
assert.Equal(tt, c.expectedCond, cond.Satisfy(), c.desc)
})
}
}

func TestCondClusterIsSuspending(t *testing.T) {
cases := []struct {
desc string
state *fakeState[v1alpha1.Cluster]
expectedCond bool
}{
{
desc: "cond is false",
state: &fakeState[v1alpha1.Cluster]{
obj: fake.FakeObj[v1alpha1.Cluster]("test"),
},
},
{
desc: "cond is true",
state: &fakeState[v1alpha1.Cluster]{
obj: fake.FakeObj("test", func(obj *v1alpha1.Cluster) *v1alpha1.Cluster {
obj.Spec.SuspendAction = &v1alpha1.SuspendAction{
SuspendCompute: true,
}
return obj
}),
},
expectedCond: true,
},
}

for i := range cases {
c := &cases[i]
t.Run(c.desc, func(tt *testing.T) {
tt.Parallel()

s := &fakeClusterState{s: c.state}
cond := CondClusterIsSuspending(s)
assert.Equal(tt, c.expectedCond, cond.Satisfy(), c.desc)
})
}
}

func TestCondClusterIsPaused(t *testing.T) {
cases := []struct {
desc string
state *fakeState[v1alpha1.Cluster]
expectedCond bool
}{
{
desc: "cond is false",
state: &fakeState[v1alpha1.Cluster]{
obj: fake.FakeObj[v1alpha1.Cluster]("test"),
},
},
{
desc: "cond is true",
state: &fakeState[v1alpha1.Cluster]{
obj: fake.FakeObj("test", func(obj *v1alpha1.Cluster) *v1alpha1.Cluster {
obj.Spec.Paused = true
return obj
}),
},
expectedCond: true,
},
}

for i := range cases {
c := &cases[i]
t.Run(c.desc, func(tt *testing.T) {
tt.Parallel()

s := &fakeClusterState{s: c.state}
cond := CondClusterIsPaused(s)
assert.Equal(tt, c.expectedCond, cond.Satisfy(), c.desc)
})
}
}
Loading

0 comments on commit 749fbbb

Please sign in to comment.