git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/netmap/selector_test.go (about) 1 package netmap 2 3 import ( 4 "cmp" 5 "crypto/rand" 6 "encoding/binary" 7 "fmt" 8 mrand "math/rand" 9 "reflect" 10 "slices" 11 "strconv" 12 "strings" 13 "testing" 14 15 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" 16 "git.frostfs.info/TrueCloudLab/hrw" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 ) 20 21 func BenchmarkHRWSort(b *testing.B) { 22 const netmapSize = 1000 23 24 vectors := make([]nodes, netmapSize) 25 weights := make([]float64, netmapSize) 26 for i := range vectors { 27 key := make([]byte, 33) 28 rand.Read(key) 29 30 var node NodeInfo 31 node.SetPrice(1) 32 node.SetCapacity(100) 33 node.SetPublicKey(key) 34 35 vectors[i] = nodes{node} 36 weights[i] = float64(mrand.Uint32()%10) / 10.0 37 } 38 39 pivot := mrand.Uint64() 40 b.Run("sort by index, no weight", func(b *testing.B) { 41 realNodes := make([]nodes, netmapSize) 42 b.ResetTimer() 43 for range b.N { 44 b.StopTimer() 45 copy(realNodes, vectors) 46 b.StartTimer() 47 48 hrw.SortSliceByIndex(realNodes, pivot) 49 } 50 }) 51 b.Run("sort by value, no weight", func(b *testing.B) { 52 realNodes := make([]nodes, netmapSize) 53 b.ResetTimer() 54 for range b.N { 55 b.StopTimer() 56 copy(realNodes, vectors) 57 b.StartTimer() 58 59 hrw.SortHasherSliceByValue(realNodes, pivot) 60 } 61 }) 62 b.Run("only sort by index", func(b *testing.B) { 63 realNodes := make([]nodes, netmapSize) 64 b.ResetTimer() 65 for range b.N { 66 b.StopTimer() 67 copy(realNodes, vectors) 68 b.StartTimer() 69 70 hrw.SortSliceByWeightIndex(realNodes, weights, pivot) 71 } 72 }) 73 b.Run("sort by value", func(b *testing.B) { 74 realNodes := make([]nodes, netmapSize) 75 b.ResetTimer() 76 for range b.N { 77 b.StopTimer() 78 copy(realNodes, vectors) 79 b.StartTimer() 80 81 hrw.SortHasherSliceByWeightValue(realNodes, weights, pivot) 82 } 83 }) 84 b.Run("sort by ID, then by index (deterministic)", func(b *testing.B) { 85 realNodes := make([]nodes, netmapSize) 86 b.ResetTimer() 87 for range b.N { 88 b.StopTimer() 89 copy(realNodes, vectors) 90 b.StartTimer() 91 92 slices.SortFunc(vectors, func(vi, vj nodes) int { 93 return cmp.Compare(vi[0].Hash(), vj[0].Hash()) 94 }) 95 hrw.SortSliceByWeightIndex(realNodes, weights, pivot) 96 } 97 }) 98 } 99 100 func BenchmarkPolicyHRWType(b *testing.B) { 101 const netmapSize = 100 102 103 p := newPlacementPolicy(1, 104 []ReplicaDescriptor{ 105 newReplica(1, "loc1"), 106 newReplica(1, "loc2"), 107 }, 108 []Selector{ 109 newSelector("loc1", "Location", 1, "loc1", (*Selector).SelectSame), 110 newSelector("loc2", "Location", 1, "loc2", (*Selector).SelectSame), 111 }, 112 []Filter{ 113 newFilter("loc1", "Location", "Shanghai", netmap.EQ), 114 newFilter("loc2", "Location", "Shanghai", netmap.NE), 115 }) 116 117 nodes := make([]NodeInfo, netmapSize) 118 for i := range nodes { 119 var loc string 120 switch i % 20 { 121 case 0: 122 loc = "Shanghai" 123 default: 124 loc = strconv.Itoa(i % 20) 125 } 126 127 // Having the same price and capacity ensures equal weights for all nodes. 128 // This way placement is more dependent on the initial order. 129 nodes[i] = nodeInfoFromAttributes("Location", loc, "Price", "1", "Capacity", "10") 130 pub := make([]byte, 33) 131 pub[0] = byte(i) 132 nodes[i].SetPublicKey(pub) 133 } 134 135 var nm NetMap 136 nm.SetNodes(nodes) 137 138 b.ResetTimer() 139 for range b.N { 140 _, err := nm.ContainerNodes(p, []byte{1}) 141 if err != nil { 142 b.Fatal() 143 } 144 } 145 } 146 147 func TestPlacementPolicy_DeterministicOrder(t *testing.T) { 148 const netmapSize = 100 149 150 p := newPlacementPolicy(1, 151 []ReplicaDescriptor{ 152 newReplica(1, "loc1"), 153 newReplica(1, "loc2"), 154 }, 155 []Selector{ 156 newSelector("loc1", "Location", 1, "loc1", (*Selector).SelectSame), 157 newSelector("loc2", "Location", 1, "loc2", (*Selector).SelectSame), 158 }, 159 []Filter{ 160 newFilter("loc1", "Location", "Shanghai", netmap.EQ), 161 newFilter("loc2", "Location", "Shanghai", netmap.NE), 162 }) 163 164 nodeList := make([]NodeInfo, netmapSize) 165 for i := range nodeList { 166 var loc string 167 switch i % 20 { 168 case 0: 169 loc = "Shanghai" 170 default: 171 loc = strconv.Itoa(i % 20) 172 } 173 174 // Having the same price and capacity ensures equal weights for all nodes. 175 // This way placement is more dependent on the initial order. 176 nodeList[i] = nodeInfoFromAttributes("Location", loc, "Price", "1", "Capacity", "10") 177 pub := make([]byte, 33) 178 pub[0] = byte(i) 179 nodeList[i].SetPublicKey(pub) 180 } 181 182 var nm NetMap 183 nm.SetNodes(nodeList) 184 185 getIndices := func(t *testing.T) (uint64, uint64) { 186 v, err := nm.ContainerNodes(p, []byte{1}) 187 require.NoError(t, err) 188 189 nss := make([]nodes, len(v)) 190 for i := range v { 191 nss[i] = v[i] 192 } 193 194 ns := flattenNodes(nss) 195 require.Equal(t, 2, len(ns)) 196 return ns[0].Hash(), ns[1].Hash() 197 } 198 199 a, b := getIndices(t) 200 for range 10 { 201 x, y := getIndices(t) 202 require.Equal(t, a, x) 203 require.Equal(t, b, y) 204 } 205 } 206 207 func TestPlacementPolicy_ProcessSelectors(t *testing.T) { 208 p := newPlacementPolicy(2, nil, 209 []Selector{ 210 newSelector("SameRU", "City", 2, "FromRU", (*Selector).SelectSame), 211 newSelector("DistinctRU", "City", 2, "FromRU", (*Selector).SelectDistinct), 212 newSelector("Good", "Country", 2, "Good", (*Selector).SelectDistinct), 213 newSelector("Main", "Country", 3, "*", (*Selector).SelectDistinct), 214 }, 215 []Filter{ 216 newFilter("FromRU", "Country", "Russia", netmap.EQ), 217 newFilter("Good", "Rating", "4", netmap.GE), 218 }) 219 nodes := []NodeInfo{ 220 nodeInfoFromAttributes("Country", "Russia", "Rating", "1", "City", "SPB"), 221 nodeInfoFromAttributes("Country", "Germany", "Rating", "5", "City", "Berlin"), 222 nodeInfoFromAttributes("Country", "Russia", "Rating", "6", "City", "Moscow"), 223 nodeInfoFromAttributes("Country", "France", "Rating", "4", "City", "Paris"), 224 nodeInfoFromAttributes("Country", "France", "Rating", "1", "City", "Lyon"), 225 nodeInfoFromAttributes("Country", "Russia", "Rating", "5", "City", "SPB"), 226 nodeInfoFromAttributes("Country", "Russia", "Rating", "7", "City", "Moscow"), 227 nodeInfoFromAttributes("Country", "Germany", "Rating", "3", "City", "Darmstadt"), 228 nodeInfoFromAttributes("Country", "Germany", "Rating", "7", "City", "Frankfurt"), 229 nodeInfoFromAttributes("Country", "Russia", "Rating", "9", "City", "SPB"), 230 nodeInfoFromAttributes("Country", "Russia", "Rating", "9", "City", "SPB"), 231 } 232 233 var nm NetMap 234 nm.SetNodes(nodes) 235 c := newContext(nm) 236 c.setCBF(p.backupFactor) 237 require.NoError(t, c.processFilters(p)) 238 require.NoError(t, c.processSelectors(p)) 239 240 for _, s := range p.selectors { 241 sel := c.selections[s.GetName()] 242 s := c.processedSelectors[s.GetName()] 243 bucketCount, nodesInBucket := calcNodesCount(*s) 244 nodesInBucket *= int(c.cbf) 245 targ := fmt.Sprintf("selector '%s'", s.GetName()) 246 require.Equal(t, bucketCount, len(sel), targ) 247 fName := s.GetFilter() 248 for _, res := range sel { 249 require.Equal(t, nodesInBucket, len(res), targ) 250 for j := range res { 251 require.True(t, fName == mainFilterName || c.match(c.processedFilters[fName], res[j]), targ) 252 } 253 } 254 } 255 } 256 257 func TestPlacementPolicy_Like(t *testing.T) { 258 nodes := []NodeInfo{ 259 nodeInfoFromAttributes("Country", "Russia"), 260 nodeInfoFromAttributes("Country", "Germany"), 261 nodeInfoFromAttributes("Country", "Belarus"), 262 } 263 264 var nm NetMap 265 nm.SetNodes(nodes) 266 267 t.Run("LIKE all", func(t *testing.T) { 268 ssNamed := []Selector{newSelector("X", "Country", 4, "FromRU", (*Selector).SelectDistinct)} 269 fsNamed := []Filter{newFilter("FromRU", "Country", "*", netmap.LIKE)} 270 rsNamed := []ReplicaDescriptor{newReplica(4, "X")} 271 pNamed := newPlacementPolicy(0, rsNamed, ssNamed, fsNamed) 272 273 n, err := nm.ContainerNodes(pNamed, []byte{1}) 274 require.NoError(t, err) 275 276 require.Equal(t, 3, len(n[0])) 277 for _, n := range n[0] { 278 require.True(t, strings.Contains("GermanyRussiaBelarus", n.Attribute("Country"))) 279 } 280 }) 281 282 t.Run("LIKE no wildcard", func(t *testing.T) { 283 ssNamed := []Selector{newSelector("X", "Country", 1, "FromRU", (*Selector).SelectDistinct)} 284 fsNamed := []Filter{newFilter("FromRU", "Country", "Russia", netmap.LIKE)} 285 rsNamed := []ReplicaDescriptor{newReplica(1, "X")} 286 pNamed := newPlacementPolicy(0, rsNamed, ssNamed, fsNamed) 287 288 n, err := nm.ContainerNodes(pNamed, []byte{1}) 289 require.NoError(t, err) 290 291 require.Equal(t, 1, len(n[0])) 292 for _, n := range n[0] { 293 require.True(t, n.Attribute("Country") == "Russia") 294 } 295 }) 296 297 t.Run("LIKE prefix", func(t *testing.T) { 298 ssNamed := []Selector{newSelector("X", "Country", 1, "FromRU", (*Selector).SelectDistinct)} 299 fsNamed := []Filter{newFilter("FromRU", "Country", "Ge*", netmap.LIKE)} 300 rsNamed := []ReplicaDescriptor{newReplica(1, "X")} 301 pNamed := newPlacementPolicy(0, rsNamed, ssNamed, fsNamed) 302 303 n, err := nm.ContainerNodes(pNamed, []byte{1}) 304 require.NoError(t, err) 305 306 require.Equal(t, 1, len(n[0])) 307 for _, n := range n[0] { 308 require.True(t, n.Attribute("Country") == "Germany") 309 } 310 }) 311 312 t.Run("LIKE suffix", func(t *testing.T) { 313 ssNamed := []Selector{newSelector("X", "Country", 1, "FromRU", (*Selector).SelectDistinct)} 314 fsNamed := []Filter{newFilter("FromRU", "Country", "*sia", netmap.LIKE)} 315 rsNamed := []ReplicaDescriptor{newReplica(1, "X")} 316 pNamed := newPlacementPolicy(0, rsNamed, ssNamed, fsNamed) 317 318 n, err := nm.ContainerNodes(pNamed, []byte{1}) 319 require.NoError(t, err) 320 321 require.Equal(t, 1, len(n[0])) 322 for _, n := range n[0] { 323 require.True(t, n.Attribute("Country") == "Russia") 324 } 325 }) 326 327 t.Run("LIKE middle", func(t *testing.T) { 328 ssNamed := []Selector{newSelector("X", "Country", 2, "FromRU", (*Selector).SelectDistinct)} 329 fsNamed := []Filter{newFilter("FromRU", "Country", "*us*", netmap.LIKE)} 330 rsNamed := []ReplicaDescriptor{newReplica(2, "X")} 331 pNamed := newPlacementPolicy(0, rsNamed, ssNamed, fsNamed) 332 333 n, err := nm.ContainerNodes(pNamed, []byte{1}) 334 require.NoError(t, err) 335 336 require.Equal(t, 2, len(n[0])) 337 for _, n := range n[0] { 338 require.True(t, strings.Contains("RussiaBelarus", n.Attribute("Country"))) 339 } 340 }) 341 } 342 343 func TestPlacementPolicy_Unique(t *testing.T) { 344 p := newPlacementPolicy(2, 345 []ReplicaDescriptor{ 346 newReplica(1, "S"), 347 newReplica(1, "S"), 348 }, 349 []Selector{ 350 newSelector("S", "City", 1, "*", (*Selector).SelectSame), 351 }, 352 []Filter{}) 353 p.unique = true 354 355 var nodes []NodeInfo 356 for i, city := range []string{"Moscow", "Berlin", "Shenzhen"} { 357 for j := range 3 { 358 node := nodeInfoFromAttributes("City", city) 359 node.SetPublicKey(binary.BigEndian.AppendUint16(nil, uint16(i*4+j))) 360 nodes = append(nodes, node) 361 } 362 } 363 364 var nm NetMap 365 nm.SetNodes(nodes) 366 367 v, err := nm.ContainerNodes(p, nil) 368 require.NoError(t, err) 369 for i, vi := range v { 370 for _, ni := range vi { 371 for j := range i { 372 for _, nj := range v[j] { 373 require.NotEqual(t, ni.hash, nj.hash) 374 } 375 } 376 } 377 } 378 } 379 380 func TestPlacementPolicy_SingleOmitNames(t *testing.T) { 381 nodes := []NodeInfo{ 382 nodeInfoFromAttributes("ID", "1", "Country", "Russia", "City", "SPB"), 383 nodeInfoFromAttributes("ID", "2", "Country", "Germany", "City", "Berlin"), 384 nodeInfoFromAttributes("ID", "3", "Country", "Russia", "City", "Moscow"), 385 nodeInfoFromAttributes("ID", "4", "Country", "France", "City", "Paris"), 386 nodeInfoFromAttributes("ID", "5", "Country", "France", "City", "Lyon"), 387 nodeInfoFromAttributes("ID", "6", "Country", "Russia", "City", "SPB"), 388 nodeInfoFromAttributes("ID", "7", "Country", "Russia", "City", "Moscow"), 389 nodeInfoFromAttributes("ID", "8", "Country", "Germany", "City", "Darmstadt"), 390 nodeInfoFromAttributes("ID", "9", "Country", "Germany", "City", "Frankfurt"), 391 nodeInfoFromAttributes("ID", "10", "Country", "Russia", "City", "SPB"), 392 nodeInfoFromAttributes("ID", "11", "Country", "Russia", "City", "Moscow"), 393 nodeInfoFromAttributes("ID", "12", "Country", "Germany", "City", "London"), 394 } 395 for i := range nodes { 396 pub := make([]byte, 33) 397 rand.Read(pub) 398 nodes[i].SetPublicKey(pub) 399 } 400 401 var nm NetMap 402 nm.SetNodes(nodes) 403 404 for _, unique := range []bool{false, true} { 405 t.Run(fmt.Sprintf("unique=%t", unique), func(t *testing.T) { 406 ssNamed := []Selector{newSelector("X", "City", 2, "FromRU", (*Selector).SelectDistinct)} 407 fsNamed := []Filter{newFilter("FromRU", "Country", "Russia", netmap.EQ)} 408 rsNamed := []ReplicaDescriptor{newReplica(1, "X")} 409 pNamed := newPlacementPolicy(3, rsNamed, ssNamed, fsNamed) 410 pNamed.unique = unique 411 412 vNamed, err := nm.ContainerNodes(pNamed, []byte{1}) 413 require.NoError(t, err) 414 415 ssUnnamed := []Selector{newSelector("", "City", 2, "FromRU", (*Selector).SelectDistinct)} 416 fsUnnamed := []Filter{newFilter("FromRU", "Country", "Russia", netmap.EQ)} 417 rsUnnamed := []ReplicaDescriptor{newReplica(1, "")} 418 pUnnamed := newPlacementPolicy(3, rsUnnamed, ssUnnamed, fsUnnamed) 419 pUnnamed.unique = unique 420 421 vUnnamed, err := nm.ContainerNodes(pUnnamed, []byte{1}) 422 require.NoError(t, err) 423 424 require.Equal(t, vNamed, vUnnamed) 425 }) 426 } 427 } 428 429 func TestPlacementPolicy_MultiREP(t *testing.T) { 430 nodes := []NodeInfo{ 431 nodeInfoFromAttributes("ID", "1", "Country", "Russia", "City", "SPB"), 432 nodeInfoFromAttributes("ID", "2", "Country", "Germany", "City", "Berlin"), 433 nodeInfoFromAttributes("ID", "3", "Country", "Russia", "City", "Moscow"), 434 nodeInfoFromAttributes("ID", "4", "Country", "France", "City", "Paris"), 435 nodeInfoFromAttributes("ID", "5", "Country", "France", "City", "Lyon"), 436 nodeInfoFromAttributes("ID", "6", "Country", "Russia", "City", "SPB"), 437 nodeInfoFromAttributes("ID", "7", "Country", "Russia", "City", "Moscow"), 438 nodeInfoFromAttributes("ID", "8", "Country", "Germany", "City", "Darmstadt"), 439 nodeInfoFromAttributes("ID", "9", "Country", "Germany", "City", "Frankfurt"), 440 nodeInfoFromAttributes("ID", "10", "Country", "Russia", "City", "SPB"), 441 nodeInfoFromAttributes("ID", "11", "Country", "Russia", "City", "Moscow"), 442 nodeInfoFromAttributes("ID", "12", "Country", "Germany", "City", "London"), 443 } 444 for i := range nodes { 445 pub := make([]byte, 33) 446 rand.Read(pub) 447 nodes[i].SetPublicKey(pub) 448 } 449 450 var nm NetMap 451 nm.SetNodes(nodes) 452 453 ss := []Selector{newSelector("SameRU", "City", 2, "FromRU", (*Selector).SelectDistinct)} 454 fs := []Filter{newFilter("FromRU", "Country", "Russia", netmap.EQ)} 455 456 for _, unique := range []bool{false, true} { 457 for _, additional := range []int{0, 1, 2} { 458 t.Run(fmt.Sprintf("unique=%t, additional=%d", unique, additional), func(t *testing.T) { 459 rs := []ReplicaDescriptor{newReplica(1, "SameRU")} 460 for range additional { 461 rs = append(rs, newReplica(1, "")) 462 } 463 464 p := newPlacementPolicy(3, rs, ss, fs) 465 p.unique = unique 466 467 v, err := nm.ContainerNodes(p, []byte{1}) 468 require.NoError(t, err) 469 require.Equal(t, 1+additional, len(v)) 470 require.Equal(t, 6, len(v[0])) 471 472 for i := 1; i < additional; i++ { 473 require.Equal(t, 3, len(v[i])) 474 if !unique { 475 require.Equal(t, v[1], v[i]) 476 } 477 } 478 479 if unique { 480 seen := make(map[string]bool) 481 for i := range v { 482 for j := range v[i] { 483 attr := v[i][j].Attribute("ID") 484 require.NotEmpty(t, attr) 485 require.False(t, seen[attr]) 486 487 seen[attr] = true 488 } 489 } 490 } 491 }) 492 } 493 } 494 } 495 496 func TestPlacementPolicy_ProcessSelectorsExceptForNodes(t *testing.T) { 497 p := newPlacementPolicy(1, nil, 498 []Selector{ 499 newSelector("ExceptRU", "City", 2, "ExceptRU", (*Selector).SelectSame), 500 }, 501 []Filter{ 502 newFilter("ExceptRU", "", "", netmap.NOT, 503 newFilter("", "", "", netmap.AND, 504 newFilter("", "City", "Lyon", netmap.EQ), 505 newFilter("", "Rating", "10", netmap.LE), 506 ), 507 ), 508 }) 509 nodes := []NodeInfo{ 510 nodeInfoFromAttributes("Country", "Germany", "Rating", "1", "City", "Berlin"), 511 nodeInfoFromAttributes("Country", "Germany", "Rating", "5", "City", "Berlin"), 512 nodeInfoFromAttributes("Country", "Russia", "Rating", "6", "City", "Moscow"), 513 nodeInfoFromAttributes("Country", "France", "Rating", "4", "City", "Paris"), 514 nodeInfoFromAttributes("Country", "France", "Rating", "1", "City", "Lyon"), 515 nodeInfoFromAttributes("Country", "France", "Rating", "5", "City", "Lyon"), 516 nodeInfoFromAttributes("Country", "Russia", "Rating", "7", "City", "Moscow"), 517 nodeInfoFromAttributes("Country", "Germany", "Rating", "3", "City", "Darmstadt"), 518 nodeInfoFromAttributes("Country", "Germany", "Rating", "7", "City", "Frankfurt"), 519 nodeInfoFromAttributes("Country", "Russia", "Rating", "9", "City", "SPB"), 520 nodeInfoFromAttributes("Country", "Russia", "Rating", "9", "City", "SPB"), 521 } 522 523 var nm NetMap 524 nm.SetNodes(nodes) 525 c := newContext(nm) 526 c.setCBF(p.backupFactor) 527 require.NoError(t, c.processFilters(p)) 528 require.NoError(t, c.processSelectors(p)) 529 530 for _, s := range p.selectors { 531 sel := c.selections[s.GetName()] 532 s := c.processedSelectors[s.GetName()] 533 bucketCount, nodesInBucket := calcNodesCount(*s) 534 nodesInBucket *= int(c.cbf) 535 targ := fmt.Sprintf("selector '%s'", s.GetName()) 536 require.Equal(t, bucketCount, len(sel), targ) 537 fName := s.GetFilter() 538 for _, res := range sel { 539 require.Equal(t, nodesInBucket, len(res), targ) 540 for j := range res { 541 require.True(t, fName == mainFilterName || c.match(c.processedFilters[fName], res[j]), targ) 542 } 543 } 544 } 545 } 546 547 func TestPlacementPolicy_NonAsciiAttributes(t *testing.T) { 548 p := newPlacementPolicy( 549 1, 550 []ReplicaDescriptor{ 551 newReplica(2, "Nodes"), 552 newReplica(2, "Nodes"), 553 }, 554 []Selector{ 555 newSelector("Nodes", "Цвет", 2, "Colorful", (*Selector).SelectSame), 556 }, 557 []Filter{ 558 newFilter("Colorful", "", "", netmap.OR, 559 newFilter("", "Цвет", "Красный", netmap.EQ), 560 newFilter("", "Цвет", "Синий", netmap.EQ), 561 ), 562 }, 563 ) 564 p.SetUnique(true) 565 566 nodes := []NodeInfo{ 567 nodeInfoFromAttributes("Цвет", "Красный", "Форма", "Треугольник"), 568 nodeInfoFromAttributes("Цвет", "Красный", "Форма", "Круг"), 569 nodeInfoFromAttributes("Цвет", "Синий", "Форма", "Треугольник"), 570 nodeInfoFromAttributes("Цвет", "Синий", "Форма", "Круг"), 571 nodeInfoFromAttributes("Свойство", "Мягкий", "Форма", "Треугольник"), 572 nodeInfoFromAttributes("Свойство", "Теплый", "Форма", "Круг"), 573 } 574 for i := range nodes { 575 nodes[i].SetPublicKey([]byte{byte(i)}) 576 } 577 578 redNodes := nodes[:2] 579 blueNodes := nodes[2:4] 580 581 var nm NetMap 582 nm.SetNodes(nodes) 583 584 pivot := make([]byte, 42) 585 _, _ = rand.Read(pivot) 586 587 nodesPerReplica, err := nm.ContainerNodes(p, pivot) 588 require.NoError(t, err) 589 require.Len(t, nodesPerReplica, 2) 590 591 for i := range nodesPerReplica { 592 slices.SortFunc(nodesPerReplica[i], func(n1, n2 NodeInfo) int { 593 pk1, pk2 := string(n1.PublicKey()), string(n2.PublicKey()) 594 return cmp.Compare(pk1, pk2) 595 }) 596 } 597 598 redMatchFirst := reflect.DeepEqual(redNodes, nodesPerReplica[0]) 599 blueMatchFirst := reflect.DeepEqual(blueNodes, nodesPerReplica[0]) 600 601 redMatchSecond := reflect.DeepEqual(redNodes, nodesPerReplica[1]) 602 blueMatchSecond := reflect.DeepEqual(blueNodes, nodesPerReplica[1]) 603 604 assert.True(t, redMatchFirst && blueMatchSecond || blueMatchFirst && redMatchSecond) 605 } 606 607 func TestSelector_SetName(t *testing.T) { 608 const name = "some name" 609 var s Selector 610 611 require.Zero(t, s.m.GetName()) 612 613 s.SetName(name) 614 require.Equal(t, name, s.m.GetName()) 615 } 616 617 func TestSelector_SetNumberOfNodes(t *testing.T) { 618 const num = 3 619 var s Selector 620 621 require.Zero(t, s.m.GetCount()) 622 623 s.SetNumberOfNodes(num) 624 625 require.EqualValues(t, num, s.m.GetCount()) 626 } 627 628 func TestSelectorClauses(t *testing.T) { 629 var s Selector 630 631 require.Equal(t, netmap.UnspecifiedClause, s.m.GetClause()) 632 633 s.SelectDistinct() 634 require.Equal(t, netmap.Distinct, s.m.GetClause()) 635 636 s.SelectSame() 637 require.Equal(t, netmap.Same, s.m.GetClause()) 638 } 639 640 func TestSelector_SelectByBucketAttribute(t *testing.T) { 641 const attr = "some attribute" 642 var s Selector 643 644 require.Zero(t, s.m.GetAttribute()) 645 646 s.SelectByBucketAttribute(attr) 647 require.Equal(t, attr, s.m.GetAttribute()) 648 } 649 650 func TestSelector_SetFilterName(t *testing.T) { 651 const fName = "some filter" 652 var s Selector 653 654 require.Zero(t, s.m.GetFilter()) 655 656 s.SetFilterName(fName) 657 require.Equal(t, fName, s.m.GetFilter()) 658 }