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  }