github.com/amplia-iiot/yutil@v1.0.1-0.20231229120411-5d96a4c5a136/pkg/merge/content_test.go (about) 1 /* 2 Copyright (c) 2021 amplia-iiot 3 4 Permission is hereby granted, free of charge, to any person obtaining a copy 5 of this software and associated documentation files (the "Software"), to deal 6 in the Software without restriction, including without limitation the rights 7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 copies of the Software, and to permit persons to whom the Software is 9 furnished to do so, subject to the following conditions: 10 11 The above copyright notice and this permission notice shall be included in all 12 copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 SOFTWARE. 21 */ 22 23 package merge 24 25 import ( 26 "errors" 27 "testing" 28 29 itesting "github.com/amplia-iiot/yutil/internal/testing" 30 "github.com/amplia-iiot/yutil/internal/yaml" 31 ) 32 33 func TestMergeContents(t *testing.T) { 34 for _, i := range []struct { 35 base string 36 changes string 37 expected string 38 }{ 39 { 40 base: `data: {one: 1, two: 2 }`, 41 changes: `data: { two: two}`, 42 expected: `data: {one: 1, two: two}`, 43 }, 44 { 45 base: `data: {one: 1, two: 2 }`, 46 changes: `data: { three: 3}`, 47 expected: `data: {one: 1, two: 2, three: 3}`, 48 }, 49 // Strings are formatted without quotes 50 { 51 base: `data: {one: 1, two: 2 }`, 52 changes: `data: { three: 'three'}`, 53 expected: `data: {one: 1, two: 2, three: three}`, 54 }, 55 // Unless containing special chars 56 { 57 base: `data: {one: 1, two: 2 }`, 58 changes: `data: { three: "{"}`, 59 expected: `data: {one: 1, two: 2, three: "{"}`, 60 }, 61 // Arrays are replaced, not joined 62 { 63 base: `data: {simple: one, extra: extra, array: [{name: one}, {name: two}]}`, 64 changes: `data: {simple: two, array: [{name: three}]}`, 65 expected: `data: {simple: two, extra: extra, array: [{name: three}]}`, 66 }, 67 // Primitive values can replace complex values 68 { 69 base: `data: {one: {two: 2}}`, 70 changes: `data: {one: 1}`, 71 expected: `data: {one: 1}`, 72 }, 73 { 74 base: `data: {one: [1]}`, 75 changes: `data: {one: 1}`, 76 expected: `data: {one: 1}`, 77 }, 78 // Multiple root estructures 79 { 80 base: "data: {one: one} \ndata3: {value: 1}", 81 changes: "data: { two: two}\ndata2: {two: two}\ndata3: {value: 2}", 82 expected: "data: {one: one, two: two}\ndata2: {two: two}\ndata3: {value: 2}", 83 }, 84 // Empty values (https://github.com/amplia-iiot/yutil/issues/3) 85 { 86 base: `data: {one: one}`, 87 changes: `data: {one: 0}`, 88 expected: `data: {one: 0}`, 89 }, 90 { 91 base: `data: {one: one}`, 92 changes: `data: {one: ''}`, 93 expected: `data: {one: ''}`, 94 }, 95 { 96 base: `data: {one: true}`, 97 changes: `data: {one: false}`, 98 expected: `data: {one: false}`, 99 }, 100 { 101 base: `data: {one: [one]}`, 102 changes: `data: {one: []}`, 103 expected: `data: {one: []}`, 104 }, 105 { 106 base: `data: {one: {two: 2}}`, 107 changes: `data: {one: {}}`, 108 expected: `data: {one: {two: 2}}`, // Special case, empty struct does not override 109 }, 110 { 111 base: `data: {one: {two: 2}}`, 112 changes: `data: {one: null}`, 113 expected: `data: {one: null}`, // You need null to override a struct 114 }, 115 { 116 base: "data: {one: one , three: three} \ndata3: {value: 1}\ndata4: {value: 4}", 117 changes: "data: { two: two, three: }\ndata2: {two: two}\ndata3: {value: 2}\ndata4: ", 118 expected: "data: {one: one, two: two, three: }\ndata2: {two: two}\ndata3: {value: 2}\ndata4: ", 119 }, 120 { 121 base: "data: {one: one , three: three} \ndata3: {value: 1}\ndata4: {value: 4}", 122 changes: "data: { two: two, three: }\ndata2: {two: two}\ndata3: {value: 2}\ndata4: ", 123 expected: "data: {one: one, two: two, three: null }\ndata2: {two: two}\ndata3: {value: 2}\ndata4: null", 124 }, 125 { 126 base: "data: {one: one , three: three} \ndata3: {value: 1}\ndata4: {value: 4}", 127 changes: "data: { two: two, three: null }\ndata2: {two: two}\ndata3: {value: 2}\ndata4: null", 128 expected: "data: {one: one, two: two, three: }\ndata2: {two: two}\ndata3: {value: 2}\ndata4:", 129 }, 130 } { 131 merged, err := MergeContents(i.base, i.changes) 132 if err != nil { 133 t.Fatal(err) 134 } 135 itesting.AssertEqual(t, format(t, i.expected), merged) 136 } 137 } 138 139 func TestMergeContentsInvalid(t *testing.T) { 140 for _, i := range []struct { 141 base string 142 changes string 143 expected string 144 }{ 145 { 146 base: `;`, 147 changes: `data: 2`, 148 expected: `cannot unmarshal`, 149 }, 150 { 151 base: `data: 1`, 152 changes: `;`, 153 expected: `cannot unmarshal`, 154 }, 155 } { 156 merged, err := MergeContents(i.base, i.changes) 157 itesting.AssertError(t, i.expected, err) 158 if merged != "" { 159 t.Fatalf("Should not have merged") 160 } 161 } 162 } 163 164 func TestMergeContentsMergeError(t *testing.T) { 165 // Mock merge internal function 166 originalMerge := yaml.Merge 167 defer func() { yaml.Merge = originalMerge }() 168 yaml.Merge = func(base, changes map[string]interface{}) (map[string]interface{}, error) { 169 return nil, errors.New("merging error") 170 } 171 merged, err := MergeContents("", "") 172 itesting.AssertError(t, "merging error", err) 173 if merged != "" { 174 t.Fatalf("Should not have merged") 175 } 176 } 177 178 func TestMergeAllContentsInvalid(t *testing.T) { 179 for _, i := range []struct { 180 contents []string 181 expected string 182 }{ 183 // At least two 184 { 185 contents: make([]string, 0), 186 expected: `slice must contain at least two contents`, 187 }, 188 { 189 contents: make([]string, 1), 190 expected: `slice must contain at least two contents`, 191 }, 192 // Parsing error 193 { 194 contents: []string{ 195 "data: 1", 196 "data: 2", 197 ";", 198 }, 199 expected: `cannot unmarshal`, 200 }, 201 { 202 contents: []string{ 203 ";", 204 "data: 2", 205 "data: 3", 206 }, 207 expected: `cannot unmarshal`, 208 }, 209 } { 210 merged, err := MergeAllContents(i.contents) 211 itesting.AssertError(t, i.expected, err) 212 if merged != "" { 213 t.Fatalf("Should not have merged") 214 } 215 } 216 } 217 218 func format(t *testing.T, content string) string { 219 data, err := yaml.Parse(content) 220 if err != nil { 221 t.Fatalf("Error formatting '%s'. %s", content, err) 222 } 223 var formatted string 224 formatted, err = yaml.Compose(data) 225 if err != nil { 226 t.Fatalf("Error formatting '%s'. %s", content, err) 227 } 228 return formatted 229 }