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 }