github.com/windmeup/goreleaser@v1.21.95/internal/yaml/yaml_test.go (about) 1 package yaml 2 3 import ( 4 "bytes" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 ) 9 10 func TestYAMLMarshalError(t *testing.T) { 11 t.Parallel() 12 13 type yamlMarshalError struct { 14 BoolValue bool `yaml:"bool_value"` 15 FuncValue func() `yaml:"func_value"` 16 } 17 18 v := yamlMarshalError{ 19 BoolValue: true, 20 FuncValue: func() {}, 21 } 22 23 require.Panics(t, func() { 24 _, _ = Marshal(v) 25 }) 26 } 27 28 func TestYAML(t *testing.T) { 29 t.Parallel() 30 31 type ( 32 yamlKey struct { 33 BoolValue bool `yaml:"bool_value"` 34 NumberValue float64 `yaml:"number_value"` 35 StringValue string `yaml:"string_value"` 36 } 37 yamlObject struct { 38 Array []string `yaml:"array"` 39 Key yamlKey `yaml:"key"` 40 } 41 yamlReceiver struct { 42 Object yamlObject `yaml:"object"` 43 } 44 45 interfaceOrObject struct { 46 obj yamlReceiver 47 iface interface{} 48 } 49 ) 50 51 for _, toPin := range []struct { 52 Title string 53 InputYAML []byte 54 ExpectedYAML []byte // optional: when marshaled YAML is expected to differ from raw input (e.g. on bool flags) 55 Expected interfaceOrObject // maybe either untyped interface{} or yamlObject struct with struct tags 56 ExpectError bool 57 WantsStrict bool // apply Strict mode 58 }{ 59 { 60 Title: "happy path, untyped", 61 InputYAML: testYAMLObject(), 62 Expected: interfaceOrObject{ 63 iface: map[string]interface{}{ 64 "object": map[string]interface{}{ 65 "key": map[string]interface{}{ 66 "string_value": "This is a doc.\nOn multiple lines.\n", 67 "bool_value": "y", 68 "number_value": 10.23, 69 }, 70 "array": []interface{}{"x", "y"}, 71 }, 72 }, 73 }, 74 }, 75 { 76 Title: "happy path strict, untyped", 77 InputYAML: testYAMLObject(), 78 WantsStrict: true, 79 Expected: interfaceOrObject{ 80 iface: map[string]interface{}{ 81 "object": map[string]interface{}{ 82 "key": map[string]interface{}{ 83 "string_value": "This is a doc.\nOn multiple lines.\n", 84 "bool_value": "y", 85 "number_value": 10.23, 86 }, 87 "array": []interface{}{"x", "y"}, 88 }, 89 }, 90 }, 91 }, 92 { 93 Title: "happy path strict, explicit target", 94 InputYAML: testYAMLObject(), 95 ExpectedYAML: testYAMLObjectBool(), 96 WantsStrict: true, 97 Expected: interfaceOrObject{ 98 iface: nil, 99 obj: yamlReceiver{ 100 Object: yamlObject{ 101 Key: yamlKey{ 102 StringValue: "This is a doc.\nOn multiple lines.\n", 103 BoolValue: true, 104 NumberValue: 10.23, 105 }, 106 Array: []string{"x", "y"}, 107 }, 108 }, 109 }, 110 }, 111 { 112 Title: "happy path non-strict, explicit target", 113 InputYAML: testYAMLObjectNonStrict(), 114 ExpectedYAML: testYAMLObjectBool(), 115 WantsStrict: false, 116 Expected: interfaceOrObject{ 117 iface: nil, 118 obj: yamlReceiver{ 119 Object: yamlObject{ 120 Key: yamlKey{ 121 StringValue: "This is a doc.\nOn multiple lines.\n", 122 BoolValue: true, 123 NumberValue: 10.23, 124 }, 125 Array: []string{"x", "y"}, 126 }, 127 }, 128 }, 129 }, 130 { 131 Title: "happy path strict, explicit target: unknown field failure", 132 InputYAML: testYAMLObjectNonStrict(), 133 ExpectedYAML: testYAMLObjectBool(), 134 WantsStrict: true, 135 ExpectError: true, 136 Expected: interfaceOrObject{ 137 iface: nil, 138 obj: yamlReceiver{ 139 Object: yamlObject{ 140 Key: yamlKey{ 141 StringValue: "This is a doc.\nOn multiple lines.\n", 142 BoolValue: true, 143 NumberValue: 10.23, 144 }, 145 Array: []string{"x", "y"}, 146 }, 147 }, 148 }, 149 }, 150 } { 151 testCase := toPin 152 153 t.Run(testCase.Title, func(t *testing.T) { 154 t.Parallel() 155 156 var ( 157 err error 158 b, expectedOutput []byte 159 ) 160 iface := testCase.Expected.iface 161 obj := testCase.Expected.obj 162 163 expectedInput := toPlainYaml(testCase.InputYAML) 164 165 if testCase.WantsStrict { 166 if iface != nil { 167 err = UnmarshalStrict(expectedInput, &iface) 168 } else { 169 err = UnmarshalStrict(expectedInput, &obj) 170 } 171 } else { 172 if iface != nil { 173 err = Unmarshal(expectedInput, &iface) 174 } else { 175 err = Unmarshal(expectedInput, &obj) 176 } 177 } 178 179 if testCase.ExpectError { 180 require.Error(t, err) 181 182 return 183 } 184 185 require.NoError(t, err) 186 187 if iface != nil { 188 require.EqualValues(t, testCase.Expected.iface, iface) 189 190 b, err = Marshal(iface) 191 require.NoError(t, err) 192 } else { 193 require.EqualValues(t, testCase.Expected.obj, obj) 194 195 b, err = Marshal(obj) 196 require.NoError(t, err) 197 } 198 199 if testCase.ExpectedYAML == nil { 200 expectedOutput = expectedInput 201 } else { 202 expectedOutput = toPlainYaml(testCase.ExpectedYAML) 203 } 204 205 require.EqualValues(t, expectedOutput, b) 206 }) 207 } 208 } 209 210 func toPlainYaml(in []byte) []byte { 211 // ensure we got legit yaml for go strings that may have been reindented using tabs, or leading new CR in source. 212 return bytes.ReplaceAll( 213 bytes.TrimLeft(in, "\n\r"), 214 []byte("\t"), bytes.Repeat([]byte(" "), 4), 215 ) 216 } 217 218 func testYAMLObject() []byte { 219 return []byte(` 220 object: 221 array: 222 - x 223 - "y" 224 key: 225 bool_value: "y" 226 number_value: 10.23 227 string_value: | 228 This is a doc. 229 On multiple lines. 230 `) 231 } 232 233 func testYAMLObjectBool() []byte { 234 // same object, but the "y" YAML for bool has been marshaled as "true" 235 return []byte(` 236 object: 237 array: 238 - x 239 - "y" 240 key: 241 bool_value: true 242 number_value: 10.23 243 string_value: | 244 This is a doc. 245 On multiple lines. 246 `) 247 } 248 249 func testYAMLObjectNonStrict() []byte { 250 // same object, but with an extra unknown value 251 return []byte(` 252 object: 253 array: 254 - x 255 - "y" 256 key: 257 bool_value: true 258 number_value: 10.23 259 string_value: | 260 This is a doc. 261 On multiple lines. 262 unknown: 'wrong' 263 `) 264 }