github.com/yandex/pandora@v0.5.32/lib/mp/map_test.go (about)

     1  package mp
     2  
     3  import (
     4  	"encoding/json"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  )
    10  
    11  func TestGetMapValue(t *testing.T) {
    12  	var cases = []struct {
    13  		name    string
    14  		reqMap  map[string]any
    15  		v       string
    16  		want    any
    17  		wantErr string
    18  	}{
    19  		{
    20  			name: "valid index for nested map with array map",
    21  			reqMap: map[string]any{
    22  				"source": map[string]any{
    23  					"items": []map[string]any{
    24  						{"id": "1"},
    25  						{"id": "2"},
    26  					},
    27  				},
    28  			},
    29  			v:    "source.items[0].id",
    30  			want: "1",
    31  		},
    32  		{
    33  			name: "invalid index for nested map with array map",
    34  			reqMap: map[string]any{
    35  				"source": map[string]any{
    36  					"items": []map[string]any{
    37  						{"id": "1"},
    38  						{"id": "2"},
    39  					},
    40  				},
    41  			},
    42  			v:       "source.items[asd].id",
    43  			wantErr: "failed to calc index for []map[string]interface {}; err: index should be integer or one of [next, rand, last], but got `asd`",
    44  		},
    45  		{
    46  			name: "should slice type error ",
    47  			reqMap: map[string]any{
    48  				"source": map[string]any{
    49  					"items": []map[string]any{
    50  						{"id": "1"},
    51  						{"id": "2"},
    52  					},
    53  				},
    54  			},
    55  			v:       "source.items[0].id[0]",
    56  			wantErr: "cant extract value path=`id`,segment=`source.items[0].id[0]`,err=invalid type of value `1`, string",
    57  		},
    58  		{
    59  			name: "not last segment",
    60  			reqMap: map[string]any{
    61  				"source": map[string]any{
    62  					"items": []map[string]any{
    63  						{"id": "1"},
    64  						{"id": "2"},
    65  					},
    66  				},
    67  			},
    68  			v:       "source.items[0].id.field",
    69  			wantErr: "not last segment id in path source.items[0].id.field",
    70  		},
    71  		{
    72  			name: "segment data not found",
    73  			reqMap: map[string]any{
    74  				"source": map[string]any{
    75  					"items": []map[string]any{
    76  						{"id": "1"},
    77  						{"id": "2"},
    78  					},
    79  				},
    80  			},
    81  			v:       "source.data[0].id",
    82  			want:    "1",
    83  			wantErr: "segment data not found in path source.data[0].id",
    84  		},
    85  		{
    86  			name: "extract map from array / element in array / valid index",
    87  			reqMap: map[string]any{
    88  				"source": map[string]any{
    89  					"items": []map[string]any{
    90  						{"id": "1"},
    91  						{"id": "2"},
    92  					},
    93  				},
    94  			},
    95  			v:    "source.items[1]",
    96  			want: map[string]any{"id": "2"},
    97  		},
    98  		{
    99  			name: "retrieve non existent key / error when searching for missing key",
   100  			reqMap: map[string]any{
   101  				"source": map[string]any{
   102  					"items": []map[string]any{
   103  						{"id": "1"},
   104  						{"id": "2"},
   105  					},
   106  				},
   107  			},
   108  			v:       "source.items[1].title",
   109  			want:    nil,
   110  			wantErr: "segment title not found in path source.items[1].title",
   111  		},
   112  		{
   113  			name: "access items in nested map / valid path / return items list",
   114  			reqMap: map[string]any{
   115  				"source": map[string]any{
   116  					"items": []map[string]any{
   117  						{"id": "1"},
   118  						{"id": "2"},
   119  					},
   120  				},
   121  			},
   122  			v: "source.items",
   123  			want: []map[string]any{
   124  				{"id": "1"},
   125  				{"id": "2"},
   126  			},
   127  		},
   128  		{
   129  			name: "valid value for map[string]string in map[string]any",
   130  			reqMap: map[string]any{
   131  				"source": map[string]any{
   132  					"items": []map[string]string{
   133  						{"id": "1"},
   134  						{"id": "2"},
   135  					},
   136  				},
   137  			},
   138  			v:    "source.items[0].id",
   139  			want: "1",
   140  		},
   141  		{
   142  			name: "invalid index for map[string]string in map[string]any",
   143  			reqMap: map[string]any{
   144  				"source": map[string]any{
   145  					"items": []map[string]string{
   146  						{"id": "1"},
   147  						{"id": "2"},
   148  					},
   149  				},
   150  			},
   151  			v:       "source.items[asd].id",
   152  			want:    "1",
   153  			wantErr: "failed to calc index for []map[string]string; err: index should be integer or one of [next, rand, last], but got `asd`",
   154  		},
   155  		{
   156  			name: "duplicate test case / same as / test case5",
   157  			reqMap: map[string]any{
   158  				"source": map[string]any{
   159  					"items": []map[string]any{
   160  						{"id": "1"},
   161  						{"id": "2"},
   162  					},
   163  				},
   164  			},
   165  			v:    "source.items[0].id",
   166  			want: "1",
   167  		},
   168  		{
   169  			name: "valid key for []any in map[string]any",
   170  			reqMap: map[string]any{
   171  				"source": map[string]any{
   172  					"items": []any{
   173  						map[string]any{
   174  							"id":   "1",
   175  							"name": "name1",
   176  						},
   177  						map[string]any{
   178  							"id":   "2",
   179  							"name": "name2",
   180  						},
   181  					},
   182  				},
   183  			},
   184  			v:    "source.items[next].name",
   185  			want: "name1",
   186  		},
   187  		{
   188  			name: "invalid index for []any in map[string]any",
   189  			reqMap: map[string]any{
   190  				"source": map[string]any{
   191  					"items": []any{
   192  						map[string]any{
   193  							"id":   "1",
   194  							"name": "name1",
   195  						},
   196  						map[string]any{
   197  							"id":   "2",
   198  							"name": "name2",
   199  						},
   200  					},
   201  				},
   202  			},
   203  			v:       "source.items[asd].name",
   204  			wantErr: "failed to calc index for []interface {}; err: index should be integer or one of [next, rand, last], but got `asd`",
   205  		},
   206  		{
   207  			name: "slice of strings",
   208  			reqMap: map[string]any{
   209  				"source": map[string]any{
   210  					"items": []string{
   211  						"1",
   212  						"2",
   213  					},
   214  				},
   215  			},
   216  			v:    "source.items[0]",
   217  			want: "1",
   218  		},
   219  		{
   220  			name: "slice of strings / invalid index",
   221  			reqMap: map[string]any{
   222  				"source": map[string]any{
   223  					"items": []string{
   224  						"1",
   225  						"2",
   226  					},
   227  				},
   228  			},
   229  			v:       "source.items[asd]",
   230  			wantErr: "failed to calc index for []string; err: index should be integer or one of [next, rand, last], but got `asd`",
   231  		},
   232  		{
   233  			name: "not last segment items in path for []string",
   234  			reqMap: map[string]any{
   235  				"source": map[string]any{
   236  					"items": []string{
   237  						"1",
   238  						"2",
   239  					},
   240  				},
   241  			},
   242  			v:       "source.items[0].key",
   243  			wantErr: "not last segment items in path source.items[0].key",
   244  		},
   245  		{
   246  			name: "extract value from array / by index / success",
   247  			reqMap: map[string]any{
   248  				"source": map[string]any{
   249  					"items": []any{11, 22, 33},
   250  				},
   251  			},
   252  			v:    "source.items[0]",
   253  			want: 11,
   254  		},
   255  		{
   256  			name: "slice of ints",
   257  			reqMap: map[string]any{
   258  				"source": map[string]any{
   259  					"items": []int{11, 22, 33},
   260  				},
   261  			},
   262  			v:    "source.items[0]",
   263  			want: 11,
   264  		},
   265  		{
   266  			name: "slice of ints",
   267  			reqMap: map[string]any{
   268  				"source": map[string]any{
   269  					"items": []int64{11, 22, 33},
   270  				},
   271  			},
   272  			v:    "source.items[0]",
   273  			want: int64(11),
   274  		},
   275  		{
   276  			name: "slice of ints",
   277  			reqMap: map[string]any{
   278  				"source": map[string]any{
   279  					"items": []float64{11, 22, 33},
   280  				},
   281  			},
   282  			v:    "source.items[0]",
   283  			want: float64(11),
   284  		},
   285  		{
   286  			name: "invalid key / in array / element / return error",
   287  			reqMap: map[string]any{
   288  				"source": map[string]any{
   289  					"items": []any{11, 22, 33},
   290  				},
   291  			},
   292  			v:       "source.items[0].id",
   293  			want:    nil,
   294  			wantErr: "not last segment items in path source.items[0].id",
   295  		},
   296  	}
   297  	for _, tt := range cases {
   298  		t.Run(tt.name, func(t *testing.T) {
   299  			iter := NewNextIterator(0)
   300  			got, err := GetMapValue(tt.reqMap, tt.v, iter)
   301  			if tt.wantErr != "" {
   302  				require.Contains(t, err.Error(), tt.wantErr)
   303  				return
   304  			}
   305  			require.NoError(t, err)
   306  			assert.Equalf(t, tt.want, got, "getValue(%v, %v)", tt.reqMap, tt.v)
   307  		})
   308  	}
   309  }
   310  
   311  func Test_getValue_iterators(t *testing.T) {
   312  	iter := NewNextIterator(0)
   313  
   314  	reqMap := map[string]any{
   315  		"source": map[string]any{
   316  			"items": []any{11, 22, 33},
   317  			"list":  []string{"11", "22", "33"},
   318  		},
   319  	}
   320  	var got any
   321  
   322  	got, _ = GetMapValue(reqMap, "source.list[next]", iter)
   323  	assert.Equal(t, "11", got)
   324  	got, _ = GetMapValue(reqMap, "source.list[next]", iter)
   325  	assert.Equal(t, "22", got)
   326  	got, _ = GetMapValue(reqMap, "source.list[next]", iter)
   327  	assert.Equal(t, "33", got)
   328  	got, _ = GetMapValue(reqMap, "source.list[next]", iter)
   329  	assert.Equal(t, "11", got)
   330  	got, _ = GetMapValue(reqMap, "source.list[next]", iter)
   331  	assert.Equal(t, "22", got)
   332  	got, _ = GetMapValue(reqMap, "source.list[next]", iter)
   333  	assert.Equal(t, "33", got)
   334  
   335  	got, _ = GetMapValue(reqMap, "source.list[last]", iter)
   336  	assert.Equal(t, "33", got)
   337  	got, _ = GetMapValue(reqMap, "source.list[last]", iter)
   338  	assert.Equal(t, "33", got)
   339  	got, _ = GetMapValue(reqMap, "source.list[-2]", iter)
   340  	assert.Equal(t, "22", got)
   341  
   342  	got, _ = GetMapValue(reqMap, "source.items[rand]", iter)
   343  	assert.Equal(t, 11, got)
   344  	got, _ = GetMapValue(reqMap, "source.items[rand]", iter)
   345  	assert.Equal(t, 11, got)
   346  	got, _ = GetMapValue(reqMap, "source.items[rand]", iter)
   347  	assert.Equal(t, 22, got)
   348  	got, _ = GetMapValue(reqMap, "source.items[rand]", iter)
   349  	assert.Equal(t, 22, got)
   350  	got, _ = GetMapValue(reqMap, "source.items[rand]", iter)
   351  	assert.Equal(t, 33, got)
   352  	got, _ = GetMapValue(reqMap, "source.items[rand]", iter)
   353  	assert.Equal(t, 22, got)
   354  
   355  	got, _ = GetMapValue(reqMap, "source.items[next]", iter)
   356  	assert.Equal(t, 11, got)
   357  	got, _ = GetMapValue(reqMap, "source.items[next]", iter)
   358  	assert.Equal(t, 22, got)
   359  	got, _ = GetMapValue(reqMap, "source.items[next]", iter)
   360  	assert.Equal(t, 33, got)
   361  	got, _ = GetMapValue(reqMap, "source.items[next]", iter)
   362  	assert.Equal(t, 11, got)
   363  	got, _ = GetMapValue(reqMap, "source.items[next]", iter)
   364  	assert.Equal(t, 22, got)
   365  
   366  }
   367  
   368  var tmpJSON = `{
   369      "name": "John Doe",
   370      "age": 30,
   371      "isStudent": false,
   372      "address": {
   373          "street": "Main St",
   374          "city": "New York",
   375          "state": "NY",
   376          "postalCode": "10001",
   377          "geolocation": {
   378              "lat": 40.7128,
   379              "lng": 74.0060
   380          }
   381      },
   382      "rates": [1, 2, 3],
   383      "rates2": [1, "c", 3],
   384      "emails": [
   385          "johndoe@gmail.com",
   386          "johndoe@yahoo.com"
   387      ],
   388      "courses": [
   389          {
   390              "courseName": "Math",
   391              "grade": "A+"
   392          },
   393          {
   394              "courseName": "History",
   395              "grade": "B"
   396          }
   397      ],
   398      "skills": [
   399          "Programming",
   400          "Design",
   401          "Management"
   402      ],
   403      "projects": [
   404          {
   405              "title": "Restaurant Web App",
   406              "description": "A full stack web application for managing restaurant reservations.",
   407              "technologies": [
   408                  "HTML",
   409                  "CSS",
   410                  "JavaScript",
   411                  "React",
   412                  "Node.js"
   413              ]
   414          },
   415          {
   416              "title": "E-commerce Website",
   417              "description": "An e-commerce platform for a small business.",
   418              "technologies": [
   419                  "PHP",
   420                  "MySQL",
   421                  "CSS",
   422                  "Bootstrap"
   423              ]
   424          }
   425      ]
   426  }`
   427  
   428  var tmpJSONKeys = []string{
   429  	"name",
   430  	"age",
   431  	"isStudent",
   432  	"address.street",
   433  	"address.city",
   434  	"address.state",
   435  	"address.postalCode",
   436  	"address.geolocation.lat",
   437  	"address.geolocation.lng",
   438  	"rates[0]",
   439  	"rates[1]",
   440  	"rates[2]",
   441  	"rates2[0]",
   442  	"rates2[1]",
   443  	"rates2[2]",
   444  	"emails[0]",
   445  	"emails[1]",
   446  	"courses[0].courseName",
   447  	"courses[0].grade",
   448  	"courses[1].courseName",
   449  	"courses[1].grade",
   450  	"skills[0]",
   451  	"skills[1]",
   452  	"skills[2]",
   453  	"projects[0].title",
   454  	"projects[0].description",
   455  	"projects[0].technologies[next]",
   456  	"projects[0].technologies[next]",
   457  	"projects[0].technologies[next]",
   458  	"projects[1].title",
   459  	"projects[1].description",
   460  	"projects[1].technologies[next]",
   461  	"projects[1].technologies[next]",
   462  	"projects[1].technologies[next]",
   463  }
   464  
   465  func BenchmarkGetMapValue(b *testing.B) {
   466  	var data map[string]any
   467  	err := json.Unmarshal([]byte(tmpJSON), &data)
   468  	require.NoError(b, err)
   469  	iterator := NewNextIterator(0)
   470  
   471  	b.ResetTimer()
   472  	for i := 0; i < b.N; i++ {
   473  		for _, k := range tmpJSONKeys {
   474  			_, err := GetMapValue(data, k, iterator)
   475  			require.NoError(b, err)
   476  		}
   477  	}
   478  }