k3s/pkg/util/strategicpatch/patch_test.go

434 lines
9.5 KiB
Go

/*
Copyright 2014 Google Inc. All rights reserved.
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 strategicpatch
import (
"encoding/json"
"fmt"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/ghodss/yaml"
)
type TestCases struct {
StrategicMergePatchCases []StrategicMergePatchCase
SortMergeListTestCases []SortMergeListCase
}
type StrategicMergePatchCase struct {
Description string
Patch map[string]interface{}
Original map[string]interface{}
Result map[string]interface{}
}
type SortMergeListCase struct {
Description string
Original map[string]interface{}
Sorted map[string]interface{}
}
type MergeItem struct {
Name string
Value string
MergingList []MergeItem `patchStrategy:"merge" patchMergeKey:"name"`
NonMergingList []MergeItem
MergingIntList []int `patchStrategy:"merge"`
NonMergingIntList []int
SimpleMap map[string]string
}
var testCaseData = []byte(`
strategicMergePatchCases:
- description: add new field
original:
name: 1
patch:
value: 1
result:
name: 1
value: 1
- description: remove field and add new field
original:
name: 1
patch:
name: null
value: 1
result:
value: 1
- description: merge arrays of scalars
original:
mergingIntList:
- 1
- 2
patch:
mergingIntList:
- 2
- 3
result:
mergingIntList:
- 1
- 2
- 3
- description: replace arrays of scalars
original:
nonMergingIntList:
- 1
- 2
patch:
nonMergingIntList:
- 2
- 3
result:
nonMergingIntList:
- 2
- 3
- description: update param of list that should be merged but had element added serverside
original:
mergingList:
- name: 1
value: 1
- name: 2
value: 2
patch:
mergingList:
- name: 1
value: a
result:
mergingList:
- name: 1
value: a
- name: 2
value: 2
- description: delete field when field is nested in a map
original:
simpleMap:
key1: 1
key2: 1
patch:
simpleMap:
key2: null
result:
simpleMap:
key1: 1
- description: update nested list when nested list should not be merged
original:
mergingList:
- name: 1
nonMergingList:
- name: 1
- name: 2
value: 2
- name: 2
patch:
mergingList:
- name: 1
nonMergingList:
- name: 1
value: 1
result:
mergingList:
- name: 1
nonMergingList:
- name: 1
value: 1
- name: 2
- description: update nested list when nested list should be merged
original:
mergingList:
- name: 1
mergingList:
- name: 1
- name: 2
value: 2
- name: 2
patch:
mergingList:
- name: 1
mergingList:
- name: 1
value: 1
result:
mergingList:
- name: 1
mergingList:
- name: 1
value: 1
- name: 2
value: 2
- name: 2
- description: update map when map should be replaced
original:
name: 1
value: 1
patch:
value: 1
$patch: replace
result:
value: 1
- description: merge empty merge lists
original:
mergingList: []
patch:
mergingList: []
result:
mergingList: []
- description: delete others in a map
original:
name: 1
value: 1
patch:
$patch: replace
result: {}
- description: delete item from a merge list
original:
mergingList:
- name: 1
- name: 2
patch:
mergingList:
- $patch: delete
name: 1
result:
mergingList:
- name: 2
- description: add and delete item from a merge list
original:
merginglist:
- name: 1
- name: 2
patch:
merginglist:
- name: 3
- $patch: delete
name: 1
result:
merginglist:
- name: 2
- name: 3
- description: delete all items from a merge list
original:
mergingList:
- name: 1
- name: 2
patch:
mergingList:
- $patch: replace
result:
mergingList: []
sortMergeListTestCases:
- description: sort one list of maps
original:
mergingList:
- name: 1
- name: 3
- name: 2
sorted:
mergingList:
- name: 1
- name: 2
- name: 3
- description: sort lists of maps but not nested lists of maps
original:
mergingList:
- name: 2
nonMergingList:
- name: 1
- name: 3
- name: 2
- name: 1
nonMergingList:
- name: 2
- name: 1
sorted:
mergingList:
- name: 1
nonMergingList:
- name: 2
- name: 1
- name: 2
nonMergingList:
- name: 1
- name: 3
- name: 2
- description: sort lists of maps and nested lists of maps
fieldTypes:
original:
mergingList:
- name: 2
mergingList:
- name: 1
- name: 3
- name: 2
- name: 1
mergingList:
- name: 2
- name: 1
sorted:
mergingList:
- name: 1
mergingList:
- name: 1
- name: 2
- name: 2
mergingList:
- name: 1
- name: 2
- name: 3
- description: merging list should NOT sort when nested in a non merging list
original:
nonMergingList:
- name: 2
mergingList:
- name: 1
- name: 3
- name: 2
- name: 1
mergingList:
- name: 2
- name: 1
sorted:
nonMergingList:
- name: 2
mergingList:
- name: 1
- name: 3
- name: 2
- name: 1
mergingList:
- name: 2
- name: 1
- description: sort a very nested list of maps
fieldTypes:
original:
mergingList:
- mergingList:
- mergingList:
- name: 2
- name: 1
sorted:
mergingList:
- mergingList:
- mergingList:
- name: 1
- name: 2
- description: sort nested lists of ints
original:
mergingList:
- name: 2
mergingIntList:
- 1
- 3
- 2
- name: 1
mergingIntList:
- 2
- 1
sorted:
mergingList:
- name: 1
mergingIntList:
- 1
- 2
- name: 2
mergingIntList:
- 1
- 2
- 3
`)
func TestStrategicMergePatch(t *testing.T) {
tc := TestCases{}
err := yaml.Unmarshal(testCaseData, &tc)
if err != nil {
t.Errorf("can't unmarshal test cases: %v", err)
return
}
var e MergeItem
for _, c := range tc.StrategicMergePatchCases {
result, err := StrategicMergePatchData(toJSON(c.Original), toJSON(c.Patch), e)
if err != nil {
t.Errorf("error patching: %v:\noriginal:\n%s\npatch:\n%s",
err, toYAML(c.Original), toYAML(c.Patch))
}
// Sort the lists that have merged maps, since order is not significant.
result, err = sortMergeListsByName(result, e)
if err != nil {
t.Errorf("error sorting result object: %v", err)
}
cResult, err := sortMergeListsByName(toJSON(c.Result), e)
if err != nil {
t.Errorf("error sorting result object: %v", err)
}
if !reflect.DeepEqual(result, cResult) {
t.Errorf("patching failed: %s\noriginal:\n%s\npatch:\n%s\nexpected result:\n%s\ngot result:\n%s",
c.Description, toYAML(c.Original), toYAML(c.Patch), jsonToYAML(cResult), jsonToYAML(result))
}
}
}
func TestSortMergeLists(t *testing.T) {
tc := TestCases{}
err := yaml.Unmarshal(testCaseData, &tc)
if err != nil {
t.Errorf("can't unmarshal test cases: %v", err)
return
}
var e MergeItem
for _, c := range tc.SortMergeListTestCases {
sorted, err := sortMergeListsByName(toJSON(c.Original), e)
if err != nil {
t.Errorf("sort arrays returned error: %v", err)
}
if !reflect.DeepEqual(sorted, toJSON(c.Sorted)) {
t.Errorf("sorting failed: %s\ntried to sort:\n%s\nexpected:\n%s\ngot:\n%s",
c.Description, toYAML(c.Original), toYAML(c.Sorted), jsonToYAML(sorted))
}
}
}
func toYAML(v interface{}) string {
y, err := yaml.Marshal(v)
if err != nil {
panic(fmt.Sprintf("yaml marshal failed: %v", err))
}
return string(y)
}
func toJSON(v interface{}) []byte {
j, err := json.Marshal(v)
if err != nil {
panic(fmt.Sprintf("json marshal failed: %s", spew.Sdump(v)))
}
return j
}
func jsonToYAML(j []byte) []byte {
y, err := yaml.JSONToYAML(j)
if err != nil {
panic(fmt.Sprintf("json to yaml failed: %v", err))
}
return y
}