github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/yaml/yaml_test.go (about) 1 package yaml_test 2 3 import ( 4 "bytes" 5 "testing" 6 7 "github.com/bingoohuang/gg/pkg/yaml" 8 "github.com/bingoohuang/gg/pkg/yaml/ast" 9 "golang.org/x/xerrors" 10 ) 11 12 func TestMarshal(t *testing.T) { 13 var v struct { 14 A int 15 B string 16 } 17 v.A = 1 18 v.B = "hello" 19 bytes, err := yaml.Marshal(v) 20 if err != nil { 21 t.Fatalf("%+v", err) 22 } 23 if string(bytes) != "a: 1\nb: hello\n" { 24 t.Fatal("failed to marshal") 25 } 26 } 27 28 func TestUnmarshal(t *testing.T) { 29 yml := ` 30 %YAML 1.2 31 --- 32 a: 1 33 b: c 34 ` 35 var v struct { 36 A int 37 B string 38 } 39 if err := yaml.Unmarshal([]byte(yml), &v); err != nil { 40 t.Fatalf("%+v", err) 41 } 42 } 43 44 type marshalTest struct{} 45 46 func (t *marshalTest) MarshalYAML() ([]byte, error) { 47 return yaml.Marshal(yaml.MapSlice{ 48 { 49 "a", 1, 50 }, 51 { 52 "b", "hello", 53 }, 54 { 55 "c", true, 56 }, 57 { 58 "d", map[string]string{"x": "y"}, 59 }, 60 }) 61 } 62 63 type marshalTest2 struct{} 64 65 func (t *marshalTest2) MarshalYAML() (interface{}, error) { 66 return yaml.MapSlice{ 67 { 68 "a", 2, 69 }, 70 { 71 "b", "world", 72 }, 73 { 74 "c", true, 75 }, 76 }, nil 77 } 78 79 func TestMarshalYAML(t *testing.T) { 80 var v struct { 81 A *marshalTest 82 B *marshalTest2 83 } 84 v.A = &marshalTest{} 85 v.B = &marshalTest2{} 86 bytes, err := yaml.Marshal(v) 87 if err != nil { 88 t.Fatalf("failed to Marshal: %+v", err) 89 } 90 expect := ` 91 a: 92 a: 1 93 b: hello 94 c: true 95 d: 96 x: y 97 b: 98 a: 2 99 b: world 100 c: true 101 ` 102 actual := "\n" + string(bytes) 103 if expect != actual { 104 t.Fatalf("failed to MarshalYAML expect:[%s], actual:[%s]", expect, actual) 105 } 106 } 107 108 type unmarshalTest struct { 109 a int 110 b string 111 c bool 112 } 113 114 func (t *unmarshalTest) UnmarshalYAML(b []byte) error { 115 if t.a != 0 { 116 return xerrors.New("unexpected field value to a") 117 } 118 if t.b != "" { 119 return xerrors.New("unexpected field value to b") 120 } 121 if t.c { 122 return xerrors.New("unexpected field value to c") 123 } 124 var v struct { 125 A int 126 B string 127 C bool 128 } 129 if err := yaml.Unmarshal(b, &v); err != nil { 130 return err 131 } 132 t.a = v.A 133 t.b = v.B 134 t.c = v.C 135 return nil 136 } 137 138 type unmarshalTest2 struct { 139 a int 140 b string 141 c bool 142 } 143 144 func (t *unmarshalTest2) UnmarshalYAML(unmarshal func(interface{}) error) error { 145 var v struct { 146 A int 147 B string 148 C bool 149 } 150 if t.a != 0 { 151 return xerrors.New("unexpected field value to a") 152 } 153 if t.b != "" { 154 return xerrors.New("unexpected field value to b") 155 } 156 if t.c { 157 return xerrors.New("unexpected field value to c") 158 } 159 if err := unmarshal(&v); err != nil { 160 return err 161 } 162 t.a = v.A 163 t.b = v.B 164 t.c = v.C 165 return nil 166 } 167 168 func TestUnmarshalYAML(t *testing.T) { 169 yml := ` 170 a: 171 a: 1 172 b: hello 173 c: true 174 b: 175 a: 2 176 b: world 177 c: true 178 ` 179 var v struct { 180 A *unmarshalTest 181 B *unmarshalTest2 182 } 183 if err := yaml.Unmarshal([]byte(yml), &v); err != nil { 184 t.Fatalf("failed to Unmarshal: %+v", err) 185 } 186 if v.A == nil { 187 t.Fatal("failed to UnmarshalYAML") 188 } 189 if v.A.a != 1 { 190 t.Fatal("failed to UnmarshalYAML") 191 } 192 if v.A.b != "hello" { 193 t.Fatal("failed to UnmarshalYAML") 194 } 195 if !v.A.c { 196 t.Fatal("failed to UnmarshalYAML") 197 } 198 if v.B == nil { 199 t.Fatal("failed to UnmarshalYAML") 200 } 201 if v.B.a != 2 { 202 t.Fatal("failed to UnmarshalYAML") 203 } 204 if v.B.b != "world" { 205 t.Fatal("failed to UnmarshalYAML") 206 } 207 if !v.B.c { 208 t.Fatal("failed to UnmarshalYAML") 209 } 210 } 211 212 type ( 213 ObjectMap map[string]*Object 214 ObjectDecl struct { 215 Name string `yaml:"-"` 216 *Object `yaml:",inline,anchor"` 217 } 218 ) 219 220 func (m ObjectMap) MarshalYAML() (interface{}, error) { 221 newMap := map[string]*ObjectDecl{} 222 for k, v := range m { 223 newMap[k] = &ObjectDecl{Name: k, Object: v} 224 } 225 return newMap, nil 226 } 227 228 type rootObject struct { 229 Single ObjectMap `yaml:"single"` 230 Collection map[string][]*Object `yaml:"collection"` 231 } 232 233 type Object struct { 234 *Object `yaml:",omitempty,inline,alias"` 235 MapValue map[string]interface{} `yaml:",omitempty,inline"` 236 } 237 238 func TestInlineAnchorAndAlias(t *testing.T) { 239 yml := `--- 240 single: 241 default: &default 242 id: 1 243 name: john 244 user_1: &user_1 245 id: 1 246 name: ken 247 user_2: &user_2 248 <<: *default 249 id: 2 250 collection: 251 defaults: 252 - *default 253 - <<: *default 254 - <<: *default 255 id: 2 256 users: 257 - <<: *user_1 258 - <<: *user_2 259 - <<: *user_1 260 id: 3 261 - <<: *user_1 262 id: 4 263 - <<: *user_1 264 id: 5 265 ` 266 var v rootObject 267 if err := yaml.Unmarshal([]byte(yml), &v); err != nil { 268 t.Fatal(err) 269 } 270 opt := yaml.MarshalAnchor(func(anchor *ast.AnchorNode, value interface{}) error { 271 if o, ok := value.(*ObjectDecl); ok { 272 return anchor.SetName(o.Name) 273 } 274 return nil 275 }) 276 var buf bytes.Buffer 277 if err := yaml.NewEncoder(&buf, opt).Encode(v); err != nil { 278 t.Fatalf("%+v", err) 279 } 280 actual := "---\n" + buf.String() 281 if yml != actual { 282 t.Fatalf("failed to marshal: expected:[%s] actual:[%s]", yml, actual) 283 } 284 } 285 286 func TestMapSlice_Map(t *testing.T) { 287 yml := ` 288 a: b 289 c: d 290 ` 291 var v yaml.MapSlice 292 if err := yaml.Unmarshal([]byte(yml), &v); err != nil { 293 t.Fatal(err) 294 } 295 m := v.ToMap() 296 if len(m) != 2 { 297 t.Fatal("failed to convert MapSlice to map") 298 } 299 if m["a"] != "b" { 300 t.Fatal("failed to convert MapSlice to map") 301 } 302 if m["c"] != "d" { 303 t.Fatal("failed to convert MapSlice to map") 304 } 305 } 306 307 func TestMarshalWithModifiedAnchorAlias(t *testing.T) { 308 yml := ` 309 a: &a 1 310 b: *a 311 ` 312 var v struct { 313 A *int `yaml:"a,anchor"` 314 B *int `yaml:"b,alias"` 315 } 316 if err := yaml.Unmarshal([]byte(yml), &v); err != nil { 317 t.Fatal(err) 318 } 319 node, err := yaml.ValueToNode(v) 320 if err != nil { 321 t.Fatal(err) 322 } 323 anchors := ast.Filter(ast.AnchorType, node) 324 if len(anchors) != 1 { 325 t.Fatal("failed to filter node") 326 } 327 anchor := anchors[0].(*ast.AnchorNode) 328 if err := anchor.SetName("b"); err != nil { 329 t.Fatal(err) 330 } 331 aliases := ast.Filter(ast.AliasType, node) 332 if len(anchors) != 1 { 333 t.Fatal("failed to filter node") 334 } 335 alias := aliases[0].(*ast.AliasNode) 336 if err := alias.SetName("b"); err != nil { 337 t.Fatal(err) 338 } 339 340 expected := ` 341 a: &b 1 342 b: *b` 343 344 actual := "\n" + node.String() 345 if expected != actual { 346 t.Fatalf("failed to marshal: expected:[%q] but got [%q]", expected, actual) 347 } 348 } 349 350 func Test_YAMLToJSON(t *testing.T) { 351 yml := ` 352 foo: 353 bar: 354 - a 355 - b 356 - c 357 a: 1 358 ` 359 actual, err := yaml.YAMLToJSON([]byte(yml)) 360 if err != nil { 361 t.Fatal(err) 362 } 363 expected := `{"foo": {"bar": ["a", "b", "c"]}, "a": 1}` 364 if expected+"\n" != string(actual) { 365 t.Fatalf("failed to convert yaml to json: expected [%q] but got [%q]", expected, actual) 366 } 367 } 368 369 func Test_JSONToYAML(t *testing.T) { 370 json := `{"foo": {"bar": ["a", "b", "c"]}, "a": 1}` 371 expected := ` 372 foo: 373 bar: 374 - a 375 - b 376 - c 377 a: 1 378 ` 379 actual, err := yaml.JSONToYAML([]byte(json)) 380 if err != nil { 381 t.Fatal(err) 382 } 383 if expected != "\n"+string(actual) { 384 t.Fatalf("failed to convert json to yaml: expected [%q] but got [%q]", expected, actual) 385 } 386 } 387 388 func Test_WithCommentOption(t *testing.T) { 389 v := struct { 390 Foo string `yaml:"foo"` 391 Bar map[string]interface{} `yaml:"bar"` 392 Baz struct { 393 X int `yaml:"x"` 394 } `yaml:"baz"` 395 }{ 396 Foo: "aaa", 397 Bar: map[string]interface{}{"bbb": "ccc"}, 398 Baz: struct { 399 X int `yaml:"x"` 400 }{X: 10}, 401 } 402 t.Run("line comment", func(t *testing.T) { 403 b, err := yaml.MarshalWithOptions(v, yaml.WithComment( 404 yaml.CommentMap{ 405 "$.foo": yaml.LineComment("foo comment"), 406 "$.bar": yaml.LineComment("bar comment"), 407 "$.bar.bbb": yaml.LineComment("bbb comment"), 408 "$.baz.x": yaml.LineComment("x comment"), 409 }, 410 )) 411 if err != nil { 412 t.Fatal(err) 413 } 414 expected := ` 415 foo: aaa #foo comment 416 bar: #bar comment 417 bbb: ccc #bbb comment 418 baz: 419 x: 10 #x comment 420 ` 421 actual := "\n" + string(b) 422 if expected != actual { 423 t.Fatalf("expected:%s but got %s", expected, actual) 424 } 425 }) 426 t.Run("head comment", func(t *testing.T) { 427 b, err := yaml.MarshalWithOptions(v, yaml.WithComment( 428 yaml.CommentMap{ 429 "$.foo": yaml.HeadComment( 430 "foo comment", 431 "foo comment2", 432 ), 433 "$.bar": yaml.HeadComment( 434 "bar comment", 435 "bar comment2", 436 ), 437 "$.bar.bbb": yaml.HeadComment( 438 "bbb comment", 439 "bbb comment2", 440 ), 441 "$.baz.x": yaml.HeadComment( 442 "x comment", 443 "x comment2", 444 ), 445 }, 446 )) 447 if err != nil { 448 t.Fatal(err) 449 } 450 expected := ` 451 #foo comment 452 #foo comment2 453 foo: aaa 454 #bar comment 455 #bar comment2 456 bar: 457 #bbb comment 458 #bbb comment2 459 bbb: ccc 460 baz: 461 #x comment 462 #x comment2 463 x: 10 464 ` 465 actual := "\n" + string(b) 466 if expected != actual { 467 t.Fatalf("expected:%s but got %s", expected, actual) 468 } 469 }) 470 }