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  }