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  }