1/*
2Copyright 2019 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package testing
18
19import (
20	"testing"
21
22	"github.com/stretchr/testify/assert"
23	"k8s.io/apimachinery/pkg/api/meta"
24	"k8s.io/apimachinery/pkg/runtime"
25	"k8s.io/apimachinery/pkg/runtime/schema"
26)
27
28func TestOriginalObjectCaptured(t *testing.T) {
29	// this ReactionFunc sets the resources ClusterName
30	const testClusterName = "some-value"
31	reactors := []ReactionFunc{
32		func(action Action) (bool, runtime.Object, error) {
33			createAction := action.(CreateActionImpl)
34			accessor, err := meta.Accessor(createAction.Object)
35			if err != nil {
36				return false, nil, err
37			}
38
39			// set any field on the resource
40			accessor.SetClusterName(testClusterName)
41
42			return true, createAction.Object, nil
43		},
44	}
45
46	// create a new Fake with the test reactors
47	f := &Fake{}
48	for _, r := range reactors {
49		f.AddReactor("", "", r)
50	}
51
52	// construct a test resource
53	testResource := schema.GroupVersionResource{Group: "", Version: "test_version", Resource: "test_kind"}
54	testObj := getArbitraryResource(testResource, "test_name", "test_namespace")
55
56	// create a fake CreateAction
57	action := CreateActionImpl{
58		Object: testObj,
59	}
60
61	// execute the reaction chain
62	ret, err := f.Invokes(action, nil)
63	assert.NoError(t, err, "running Invokes failed")
64
65	// obtain a metadata accessor for the returned resource
66	accessor, err := meta.Accessor(ret)
67	if err != nil {
68		t.Fatalf("unexpected error: %v", err)
69	}
70
71	// validate that the returned resource was modified by the ReactionFunc
72	if accessor.GetClusterName() != testClusterName {
73		t.Errorf("expected resource returned by Invokes to be modified by the ReactionFunc")
74	}
75	// verify one action was performed
76	if len(f.actions) != 1 {
77		t.Errorf("expected 1 action to be executed")
78		t.FailNow()
79	}
80	// check to ensure the recorded action has not been modified by the chain
81	createAction := f.actions[0].(CreateActionImpl)
82	accessor, err = meta.Accessor(createAction.Object)
83	if err != nil {
84		t.Fatalf("unexpected error: %v", err)
85	}
86	if accessor.GetClusterName() != "" {
87		t.Errorf("expected Action recorded to not be modified by ReactionFunc but it was")
88	}
89}
90
91func TestReactorChangesPersisted(t *testing.T) {
92	// this ReactionFunc sets the resources ClusterName
93	const testClusterName = "some-value"
94	reactors := []ReactionFunc{
95		func(action Action) (bool, runtime.Object, error) {
96			createAction := action.(CreateActionImpl)
97			accessor, err := meta.Accessor(createAction.Object)
98			if err != nil {
99				return false, nil, err
100			}
101
102			// set any field on the resource
103			accessor.SetClusterName(testClusterName)
104
105			return false, createAction.Object, nil
106		},
107		func(action Action) (bool, runtime.Object, error) {
108			createAction := action.(CreateActionImpl)
109			accessor, err := meta.Accessor(createAction.Object)
110			if err != nil {
111				return false, nil, err
112			}
113
114			// ensure the clusterName is set to testClusterName already
115			if accessor.GetClusterName() != testClusterName {
116				t.Errorf("expected resource passed to second reactor to be modified by first reactor")
117			}
118
119			return true, createAction.Object, nil
120		},
121	}
122
123	// create a new Fake with the test reactors
124	f := &Fake{}
125	for _, r := range reactors {
126		f.AddReactor("", "", r)
127	}
128
129	// construct a test resource
130	testResource := schema.GroupVersionResource{Group: "", Version: "test_version", Resource: "test_kind"}
131	testObj := getArbitraryResource(testResource, "test_name", "test_namespace")
132
133	// create a fake CreateAction
134	action := CreateActionImpl{
135		Object: testObj,
136	}
137
138	// execute the reaction chain
139	ret, err := f.Invokes(action, nil)
140	assert.NoError(t, err, "running Invokes failed")
141
142	// obtain a metadata accessor for the returned resource
143	accessor, err := meta.Accessor(ret)
144	if err != nil {
145		t.Fatalf("unexpected error: %v", err)
146	}
147
148	// validate that the returned resource was modified by the ReactionFunc
149	if accessor.GetClusterName() != testClusterName {
150		t.Errorf("expected resource returned by Invokes to be modified by the ReactionFunc")
151	}
152	// verify one action was performed
153	if len(f.actions) != 1 {
154		t.Errorf("expected 1 action to be executed")
155		t.FailNow()
156	}
157	// check to ensure the recorded action has not been modified by the chain
158	createAction := f.actions[0].(CreateActionImpl)
159	accessor, err = meta.Accessor(createAction.Object)
160	if err != nil {
161		t.Fatalf("unexpected error: %v", err)
162	}
163	if accessor.GetClusterName() != "" {
164		t.Errorf("expected Action recorded to not be modified by ReactionFunc but it was")
165	}
166}
167