github.com/sacloud/iaas-api-go@v1.12.0/mapconv/map_test.go (about)

     1  // Copyright 2022-2023 The sacloud/iaas-api-go Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mapconv
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/fatih/structs"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestMap_Set(t *testing.T) {
    25  	expects := []struct {
    26  		caseName string
    27  		source   map[string]interface{}
    28  		dest     map[string]interface{}
    29  	}{
    30  		{
    31  			caseName: "minimum",
    32  			source: map[string]interface{}{
    33  				"test": "test",
    34  			},
    35  			dest: map[string]interface{}{
    36  				"test": "test",
    37  			},
    38  		},
    39  		{
    40  			caseName: "nested",
    41  			source: map[string]interface{}{
    42  				"test.A":    "A",
    43  				"test.B":    "B",
    44  				"test.C.C1": "C1",
    45  				"test.C.C2": "C2",
    46  				"outer":     "outer",
    47  				"int":       1,
    48  				"float":     1.1,
    49  			},
    50  			dest: map[string]interface{}{
    51  				"test": map[string]interface{}{
    52  					"A": "A",
    53  					"B": "B",
    54  					"C": map[string]interface{}{
    55  						"C1": "C1",
    56  						"C2": "C2",
    57  					},
    58  				},
    59  				"outer": "outer",
    60  				"int":   1,
    61  				"float": 1.1,
    62  			},
    63  		},
    64  		{
    65  			caseName: "slice",
    66  			source: map[string]interface{}{
    67  				"slice.slice.value": []interface{}{"value4", "value5"},
    68  			},
    69  			dest: map[string]interface{}{
    70  				"slice": map[string]interface{}{
    71  					"slice": map[string]interface{}{
    72  						"value": []interface{}{"value4", "value5"},
    73  					},
    74  				},
    75  			},
    76  		},
    77  		{
    78  			caseName: "expanded slice",
    79  			source: map[string]interface{}{
    80  				"[]slice.value": []interface{}{"value1", "value2"},
    81  			},
    82  			dest: map[string]interface{}{
    83  				"slice": []map[string]interface{}{
    84  					{"value": "value1"},
    85  					{"value": "value2"},
    86  				},
    87  			},
    88  		},
    89  		{
    90  			caseName: "expanded nested slice",
    91  			source: map[string]interface{}{
    92  				"[]slice.slice.value": []interface{}{"value4", "value5"},
    93  			},
    94  			dest: map[string]interface{}{
    95  				"slice": []map[string]interface{}{
    96  					{
    97  						"slice": map[string]interface{}{
    98  							"value": "value4",
    99  						},
   100  					},
   101  					{
   102  						"slice": map[string]interface{}{
   103  							"value": "value5",
   104  						},
   105  					},
   106  				},
   107  			},
   108  		},
   109  		{
   110  			caseName: "expanded nested slice with middle slice",
   111  			source: map[string]interface{}{
   112  				"slice.[]slice.value": []interface{}{"value4", "value5"},
   113  			},
   114  			dest: map[string]interface{}{
   115  				"slice": map[string]interface{}{
   116  					"slice": []map[string]interface{}{
   117  						{"value": "value4"},
   118  						{"value": "value5"},
   119  					},
   120  				},
   121  			},
   122  		},
   123  		{
   124  			caseName: "expanded nested slice with last slice",
   125  			source: map[string]interface{}{
   126  				"slice.slice.[]value": []interface{}{"value4", "value5"},
   127  			},
   128  			dest: map[string]interface{}{
   129  				"slice": map[string]interface{}{
   130  					"slice": map[string]interface{}{
   131  						"value": []interface{}{"value4", "value5"},
   132  					},
   133  				},
   134  			},
   135  		},
   136  		{
   137  			caseName: "expanded deep nested slice",
   138  			source: map[string]interface{}{
   139  				"[]slice.[]slice.value": []interface{}{"value4", "value5"},
   140  			},
   141  			dest: map[string]interface{}{
   142  				"slice": []map[string]interface{}{
   143  					{
   144  						"slice": []map[string]interface{}{
   145  							{"value": "value4"},
   146  						},
   147  					},
   148  					{
   149  						"slice": []map[string]interface{}{
   150  							{"value": "value5"},
   151  						},
   152  					},
   153  				},
   154  			},
   155  		},
   156  	}
   157  
   158  	for _, expect := range expects {
   159  		t.Run(expect.caseName, func(t *testing.T) {
   160  			m := Map(make(map[string]interface{}))
   161  			for k, v := range expect.source {
   162  				m.Set(k, v)
   163  			}
   164  			require.Equal(t, expect.dest, m.Map())
   165  		})
   166  	}
   167  }
   168  
   169  func TestMap_Get(t *testing.T) {
   170  	expects := []struct {
   171  		caseName  string
   172  		keyValues map[string]interface{}
   173  		source    map[string]interface{}
   174  		err       error
   175  	}{
   176  		{
   177  			caseName: "minimum",
   178  			keyValues: map[string]interface{}{
   179  				"test": "test",
   180  			},
   181  			source: map[string]interface{}{
   182  				"test": "test",
   183  			},
   184  		},
   185  		{
   186  			caseName: "nested",
   187  			keyValues: map[string]interface{}{
   188  				"test.A":    "A",
   189  				"test.B":    "B",
   190  				"test.C.C1": "C1",
   191  				"test.C.C2": "C2",
   192  				"outer":     "outer",
   193  				"int":       1,
   194  				"float":     1.1,
   195  			},
   196  			source: map[string]interface{}{
   197  				"test": map[string]interface{}{
   198  					"A": "A",
   199  					"B": "B",
   200  					"C": map[string]interface{}{
   201  						"C1": "C1",
   202  						"C2": "C2",
   203  					},
   204  				},
   205  				"outer": "outer",
   206  				"int":   1,
   207  				"float": 1.1,
   208  			},
   209  		},
   210  		{
   211  			caseName: "slice",
   212  			keyValues: map[string]interface{}{
   213  				"slice.value": []interface{}{"value1", "value2"},
   214  			},
   215  			source: map[string]interface{}{
   216  				"slice": []map[string]interface{}{
   217  					{"value": "value1"},
   218  					{"value": "value2"},
   219  				},
   220  			},
   221  		},
   222  		{
   223  			caseName: "nested slice",
   224  			keyValues: map[string]interface{}{
   225  				"slice.slice.value": []interface{}{"value4", "value5"},
   226  			},
   227  			source: map[string]interface{}{
   228  				"slice": []map[string]interface{}{
   229  					{"value": "value1"},
   230  					{"value": "value2"},
   231  					{
   232  						"value": "value3",
   233  						"slice": []map[string]interface{}{
   234  							{"value": "value4"},
   235  							{"value": "value5"},
   236  						},
   237  					},
   238  				},
   239  			},
   240  		},
   241  		{
   242  			caseName: "with invalid key",
   243  			keyValues: map[string]interface{}{
   244  				"test.A.B": nil,
   245  			},
   246  			source: map[string]interface{}{
   247  				"test": map[string]interface{}{
   248  					"A": "test",
   249  				},
   250  			},
   251  		},
   252  	}
   253  
   254  	for _, expect := range expects {
   255  		t.Run(expect.caseName, func(t *testing.T) {
   256  			m := Map(expect.source)
   257  			for k, v := range expect.keyValues {
   258  				value, err := m.Get(k)
   259  				require.Equal(t, expect.err, err)
   260  				if err == nil {
   261  					require.Equal(t, v, value)
   262  				}
   263  			}
   264  		})
   265  	}
   266  }
   267  
   268  type A struct {
   269  	Foo *B `structs:",omitempty"`
   270  }
   271  
   272  type B struct {
   273  	Bar *C `structs:",omitempty"`
   274  }
   275  
   276  type C struct {
   277  	Baz interface{} `structs:",omitempty"`
   278  }
   279  
   280  func TestMap_GetWithEmptyStruct(t *testing.T) {
   281  	cases := []struct {
   282  		in     interface{}
   283  		expect interface{}
   284  	}{
   285  		{in: &A{Foo: &B{Bar: &C{Baz: "FooBarBaz"}}}, expect: "FooBarBaz"},
   286  		{in: &A{Foo: &B{Bar: &C{}}}, expect: nil},
   287  		{in: &A{Foo: &B{}}, expect: nil},
   288  	}
   289  	for _, tc := range cases {
   290  		m := Map(structs.Map(tc.in))
   291  		got, err := m.Get("Foo.Bar.Baz")
   292  		if err != nil {
   293  			t.Fatal(err)
   294  		}
   295  		if got != tc.expect {
   296  			t.Fatalf("got unexpected value: expected: %v actual:%v", tc.expect, got)
   297  		}
   298  	}
   299  }