github.com/status-im/status-go@v1.1.0/services/wallet/router/filter_test.go (about) 1 package router 2 3 import ( 4 "math/big" 5 "testing" 6 7 "github.com/ethereum/go-ethereum/common/hexutil" 8 9 "github.com/status-im/status-go/params" 10 "github.com/status-im/status-go/services/wallet/router/pathprocessor" 11 "github.com/status-im/status-go/services/wallet/router/routes" 12 13 "github.com/stretchr/testify/assert" 14 ) 15 16 var ( 17 network1 = ¶ms.Network{ChainID: 1} 18 network2 = ¶ms.Network{ChainID: 2} 19 network3 = ¶ms.Network{ChainID: 3} 20 network4 = ¶ms.Network{ChainID: 4} 21 network5 = ¶ms.Network{ChainID: 5} 22 23 amount0 = hexutil.Big(*big.NewInt(0)) 24 amount1 = hexutil.Big(*big.NewInt(100)) 25 amount2 = hexutil.Big(*big.NewInt(200)) 26 amount3 = hexutil.Big(*big.NewInt(300)) 27 amount4 = hexutil.Big(*big.NewInt(400)) 28 amount5 = hexutil.Big(*big.NewInt(500)) 29 30 path0 = &routes.Path{FromChain: network4, AmountIn: &amount0} 31 32 pathC1A1 = &routes.Path{FromChain: network1, AmountIn: &amount1} 33 34 pathC2A1 = &routes.Path{FromChain: network2, AmountIn: &amount1} 35 pathC2A2 = &routes.Path{FromChain: network2, AmountIn: &amount2} 36 37 pathC3A1 = &routes.Path{FromChain: network3, AmountIn: &amount1} 38 pathC3A2 = &routes.Path{FromChain: network3, AmountIn: &amount2} 39 pathC3A3 = &routes.Path{FromChain: network3, AmountIn: &amount3} 40 41 pathC4A1 = &routes.Path{FromChain: network4, AmountIn: &amount1} 42 pathC4A4 = &routes.Path{FromChain: network4, AmountIn: &amount4} 43 44 pathC5A5 = &routes.Path{FromChain: network5, AmountIn: &amount5} 45 ) 46 47 func routesEqual(t *testing.T, expected, actual []routes.Route) bool { 48 if len(expected) != len(actual) { 49 return false 50 } 51 for i := range expected { 52 if !pathsEqual(t, expected[i], actual[i]) { 53 return false 54 } 55 } 56 return true 57 } 58 59 func pathsEqual(t *testing.T, expected, actual routes.Route) bool { 60 if len(expected) != len(actual) { 61 return false 62 } 63 for i := range expected { 64 if !pathEqual(t, expected[i], actual[i]) { 65 return false 66 } 67 } 68 return true 69 } 70 71 func pathEqual(t *testing.T, expected, actual *routes.Path) bool { 72 if expected.FromChain.ChainID != actual.FromChain.ChainID { 73 t.Logf("expected chain ID '%d' , actual chain ID '%d'", expected.FromChain.ChainID, actual.FromChain.ChainID) 74 return false 75 } 76 if expected.AmountIn.ToInt().Cmp(actual.AmountIn.ToInt()) != 0 { 77 t.Logf("expected AmountIn '%d' , actual AmountIn '%d'", expected.AmountIn.ToInt(), actual.AmountIn.ToInt()) 78 return false 79 } 80 if expected.AmountInLocked != actual.AmountInLocked { 81 t.Logf("expected AmountInLocked '%t' , actual AmountInLocked '%t'", expected.AmountInLocked, actual.AmountInLocked) 82 return false 83 } 84 return true 85 } 86 87 func TestSetupRouteValidationMaps(t *testing.T) { 88 tests := []struct { 89 name string 90 fromLockedAmount map[uint64]*hexutil.Big 91 expectedIncluded map[uint64]bool 92 expectedExcluded map[uint64]bool 93 }{ 94 { 95 name: "Mixed zero and non-zero amounts", 96 fromLockedAmount: map[uint64]*hexutil.Big{ 97 1: (*hexutil.Big)(pathprocessor.ZeroBigIntValue), 98 2: (*hexutil.Big)(big.NewInt(200)), 99 3: (*hexutil.Big)(pathprocessor.ZeroBigIntValue), 100 4: (*hexutil.Big)(big.NewInt(400)), 101 }, 102 expectedIncluded: map[uint64]bool{ 103 2: false, 104 4: false, 105 }, 106 expectedExcluded: map[uint64]bool{ 107 1: false, 108 3: false, 109 }, 110 }, 111 { 112 name: "All non-zero amounts", 113 fromLockedAmount: map[uint64]*hexutil.Big{ 114 1: (*hexutil.Big)(big.NewInt(100)), 115 2: (*hexutil.Big)(big.NewInt(200)), 116 }, 117 expectedIncluded: map[uint64]bool{ 118 1: false, 119 2: false, 120 }, 121 expectedExcluded: map[uint64]bool{}, 122 }, 123 { 124 name: "All zero amounts", 125 fromLockedAmount: map[uint64]*hexutil.Big{ 126 1: (*hexutil.Big)(pathprocessor.ZeroBigIntValue), 127 2: (*hexutil.Big)(pathprocessor.ZeroBigIntValue), 128 }, 129 expectedIncluded: map[uint64]bool{}, 130 expectedExcluded: map[uint64]bool{ 131 1: false, 132 2: false, 133 }, 134 }, 135 { 136 name: "Single non-zero amount", 137 fromLockedAmount: map[uint64]*hexutil.Big{ 138 1: (*hexutil.Big)(big.NewInt(100)), 139 }, 140 expectedIncluded: map[uint64]bool{ 141 1: false, 142 }, 143 expectedExcluded: map[uint64]bool{}, 144 }, 145 { 146 name: "Single zero amount", 147 fromLockedAmount: map[uint64]*hexutil.Big{ 148 1: (*hexutil.Big)(pathprocessor.ZeroBigIntValue), 149 }, 150 expectedIncluded: map[uint64]bool{}, 151 expectedExcluded: map[uint64]bool{ 152 1: false, 153 }, 154 }, 155 { 156 name: "Empty map", 157 fromLockedAmount: map[uint64]*hexutil.Big{}, 158 expectedIncluded: map[uint64]bool{}, 159 expectedExcluded: map[uint64]bool{}, 160 }, 161 } 162 163 for _, tt := range tests { 164 t.Run(tt.name, func(t *testing.T) { 165 included, excluded := setupRouteValidationMaps(tt.fromLockedAmount) 166 assert.Equal(t, tt.expectedIncluded, included) 167 assert.Equal(t, tt.expectedExcluded, excluded) 168 }) 169 } 170 } 171 172 func TestCalculateRestAmountIn(t *testing.T) { 173 tests := []struct { 174 name string 175 route routes.Route 176 excludePath *routes.Path 177 expected *big.Int 178 }{ 179 { 180 name: "Exclude pathC1A1", 181 route: routes.Route{pathC1A1, pathC2A2, pathC3A3}, 182 excludePath: pathC1A1, 183 expected: big.NewInt(500), // 200 + 300 184 }, 185 { 186 name: "Exclude pathC2A2", 187 route: routes.Route{pathC1A1, pathC2A2, pathC3A3}, 188 excludePath: pathC2A2, 189 expected: big.NewInt(400), // 100 + 300 190 }, 191 { 192 name: "Exclude pathC3A3", 193 route: routes.Route{pathC1A1, pathC2A2, pathC3A3}, 194 excludePath: pathC3A3, 195 expected: big.NewInt(300), // 100 + 200 196 }, 197 { 198 name: "Single path, exclude that path", 199 route: routes.Route{pathC1A1}, 200 excludePath: pathC1A1, 201 expected: big.NewInt(0), // No other paths 202 }, 203 { 204 name: "Empty route", 205 route: routes.Route{}, 206 excludePath: pathC1A1, 207 expected: big.NewInt(0), // No paths 208 }, 209 { 210 name: "Empty route, with nil exclude", 211 route: routes.Route{}, 212 excludePath: nil, 213 expected: big.NewInt(0), // No paths 214 }, 215 } 216 217 for _, tt := range tests { 218 t.Run(tt.name, func(t *testing.T) { 219 result := calculateRestAmountIn(tt.route, tt.excludePath) 220 assert.Equal(t, tt.expected, result) 221 }) 222 } 223 } 224 225 func TestIsValidForNetworkCompliance(t *testing.T) { 226 tests := []struct { 227 name string 228 route routes.Route 229 fromIncluded map[uint64]bool 230 fromExcluded map[uint64]bool 231 expectedResult bool 232 }{ 233 { 234 name: "Route with all included chain IDs", 235 route: routes.Route{pathC1A1, pathC2A2}, 236 fromIncluded: map[uint64]bool{1: true, 2: true}, 237 fromExcluded: map[uint64]bool{}, 238 expectedResult: true, 239 }, 240 { 241 name: "Route with fromExcluded only", 242 route: routes.Route{pathC1A1, pathC2A2}, 243 fromIncluded: map[uint64]bool{}, 244 fromExcluded: map[uint64]bool{3: false, 4: false}, 245 expectedResult: true, 246 }, 247 { 248 name: "Route without excluded chain IDs", 249 route: routes.Route{pathC1A1, pathC2A2}, 250 fromIncluded: map[uint64]bool{1: false, 2: false}, 251 fromExcluded: map[uint64]bool{3: false, 4: false}, 252 expectedResult: true, 253 }, 254 { 255 name: "Route with an excluded chain ID", 256 route: routes.Route{pathC1A1, pathC3A3}, 257 fromIncluded: map[uint64]bool{1: false, 2: false}, 258 fromExcluded: map[uint64]bool{3: false, 4: false}, 259 expectedResult: false, 260 }, 261 { 262 name: "Route missing one included chain ID", 263 route: routes.Route{pathC1A1}, 264 fromIncluded: map[uint64]bool{1: false, 2: false}, 265 fromExcluded: map[uint64]bool{}, 266 expectedResult: false, 267 }, 268 { 269 name: "Route with no fromIncluded or fromExcluded", 270 route: routes.Route{pathC1A1, pathC2A2}, 271 fromIncluded: map[uint64]bool{}, 272 fromExcluded: map[uint64]bool{}, 273 expectedResult: true, 274 }, 275 { 276 name: "Empty route", 277 route: routes.Route{}, 278 fromIncluded: map[uint64]bool{1: false, 2: false}, 279 fromExcluded: map[uint64]bool{3: false, 4: false}, 280 expectedResult: false, 281 }, 282 } 283 284 for _, tt := range tests { 285 t.Run(tt.name, func(t *testing.T) { 286 result := isValidForNetworkCompliance(tt.route, tt.fromIncluded, tt.fromExcluded) 287 assert.Equal(t, tt.expectedResult, result) 288 }) 289 } 290 } 291 292 func TestHasSufficientCapacity(t *testing.T) { 293 tests := []struct { 294 name string 295 route routes.Route 296 amountIn *big.Int 297 fromLockedAmount map[uint64]*hexutil.Big 298 expected bool 299 }{ 300 { 301 name: "All paths meet required amount", 302 route: routes.Route{pathC1A1, pathC2A2, pathC3A3}, 303 amountIn: big.NewInt(600), 304 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3}, 305 expected: true, 306 }, 307 // TODO: Find out what the expected behaviour for this case should be 308 // I expect false but the test returns true 309 /* 310 { 311 name: "A path does not meet required amount", 312 route: routes.Route{pathC1A1, pathC2A2, pathC3A3}, 313 amountIn: big.NewInt(600), 314 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 4: &amount4}, 315 expected: false, 316 }, 317 */ 318 { 319 name: "No fromLockedAmount", 320 route: routes.Route{pathC1A1, pathC2A2, pathC3A3}, 321 amountIn: big.NewInt(600), 322 fromLockedAmount: map[uint64]*hexutil.Big{}, 323 expected: true, 324 }, 325 { 326 name: "Single path meets required amount", 327 route: routes.Route{pathC1A1}, 328 amountIn: big.NewInt(100), 329 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1}, 330 expected: true, 331 }, 332 { 333 name: "Single path does not meet required amount", 334 route: routes.Route{pathC1A1}, 335 amountIn: big.NewInt(200), 336 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1}, 337 expected: false, 338 }, 339 { 340 name: "Path meets required amount with excess", 341 route: routes.Route{pathC1A1, pathC2A2}, 342 amountIn: big.NewInt(250), 343 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2}, 344 expected: true, 345 }, 346 { 347 name: "Path does not meet required amount due to insufficient rest", 348 route: routes.Route{pathC1A1, pathC2A2, pathC4A4}, 349 amountIn: big.NewInt(800), 350 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 4: &amount4}, 351 expected: false, 352 }, 353 { 354 name: "Empty route", 355 route: routes.Route{}, 356 amountIn: big.NewInt(500), 357 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2}, 358 expected: true, 359 }, 360 } 361 362 for _, tt := range tests { 363 t.Run(tt.name, func(t *testing.T) { 364 result := hasSufficientCapacity(tt.route, tt.amountIn, tt.fromLockedAmount) 365 assert.Equal(t, tt.expected, result) 366 }) 367 } 368 } 369 370 func TestFilterNetworkCompliance(t *testing.T) { 371 tests := []struct { 372 name string 373 routes []routes.Route 374 fromLockedAmount map[uint64]*hexutil.Big 375 expected []routes.Route 376 }{ 377 { 378 name: "Mixed routes with valid and invalid paths", 379 routes: []routes.Route{ 380 { 381 {FromChain: network1}, 382 {FromChain: network3}, 383 }, 384 { 385 {FromChain: network2}, 386 {FromChain: network3}, 387 }, 388 { 389 {FromChain: network1}, 390 {FromChain: network2}, 391 {FromChain: network3}, 392 }, 393 }, 394 fromLockedAmount: map[uint64]*hexutil.Big{ 395 1: (*hexutil.Big)(big.NewInt(100)), 396 2: (*hexutil.Big)(big.NewInt(0)), 397 }, 398 expected: []routes.Route{ 399 { 400 {FromChain: network1}, 401 {FromChain: network3}, 402 }, 403 }, 404 }, 405 { 406 name: "All valid routes", 407 routes: []routes.Route{ 408 { 409 {FromChain: network1}, 410 {FromChain: network3}, 411 }, 412 { 413 {FromChain: network1}, 414 {FromChain: network4}, 415 }, 416 }, 417 fromLockedAmount: map[uint64]*hexutil.Big{ 418 1: (*hexutil.Big)(big.NewInt(100)), 419 }, 420 expected: []routes.Route{ 421 { 422 {FromChain: network1}, 423 {FromChain: network3}, 424 }, 425 { 426 {FromChain: network1}, 427 {FromChain: network4}, 428 }, 429 }, 430 }, 431 { 432 name: "All invalid routes", 433 routes: []routes.Route{ 434 { 435 {FromChain: network2}, 436 {FromChain: network3}, 437 }, 438 { 439 {FromChain: network4}, 440 {FromChain: network5}, 441 }, 442 }, 443 fromLockedAmount: map[uint64]*hexutil.Big{ 444 1: (*hexutil.Big)(big.NewInt(100)), 445 2: (*hexutil.Big)(big.NewInt(0)), 446 }, 447 expected: []routes.Route{}, 448 }, 449 { 450 name: "Empty routes", 451 routes: []routes.Route{}, 452 fromLockedAmount: map[uint64]*hexutil.Big{ 453 1: (*hexutil.Big)(big.NewInt(100)), 454 }, 455 expected: []routes.Route{}, 456 }, 457 { 458 name: "No locked amounts", 459 routes: []routes.Route{ 460 { 461 {FromChain: network1}, 462 {FromChain: network2}, 463 }, 464 { 465 {FromChain: network3}, 466 {FromChain: network4}, 467 }, 468 }, 469 fromLockedAmount: map[uint64]*hexutil.Big{}, 470 expected: []routes.Route{ 471 { 472 {FromChain: network1}, 473 {FromChain: network2}, 474 }, 475 { 476 {FromChain: network3}, 477 {FromChain: network4}, 478 }, 479 }, 480 }, 481 { 482 name: "Single route with mixed valid and invalid paths", 483 routes: []routes.Route{ 484 { 485 {FromChain: network1}, 486 {FromChain: network2}, 487 {FromChain: network3}, 488 }, 489 }, 490 fromLockedAmount: map[uint64]*hexutil.Big{ 491 1: (*hexutil.Big)(big.NewInt(100)), 492 2: (*hexutil.Big)(big.NewInt(0)), 493 }, 494 expected: []routes.Route{}, 495 }, 496 { 497 name: "Routes with duplicate chain IDs", 498 routes: []routes.Route{ 499 { 500 {FromChain: network1}, 501 {FromChain: network1}, 502 {FromChain: network2}, 503 }, 504 }, 505 fromLockedAmount: map[uint64]*hexutil.Big{ 506 1: (*hexutil.Big)(big.NewInt(100)), 507 }, 508 expected: []routes.Route{ 509 { 510 {FromChain: network1}, 511 {FromChain: network1}, 512 {FromChain: network2}, 513 }, 514 }, 515 }, 516 { 517 name: "Minimum and maximum chain IDs", 518 routes: []routes.Route{ 519 { 520 {FromChain: ¶ms.Network{ChainID: 0}}, 521 {FromChain: ¶ms.Network{ChainID: ^uint64(0)}}, 522 }, 523 }, 524 fromLockedAmount: map[uint64]*hexutil.Big{ 525 0: (*hexutil.Big)(big.NewInt(100)), 526 ^uint64(0): (*hexutil.Big)(big.NewInt(100)), 527 }, 528 expected: []routes.Route{ 529 { 530 {FromChain: ¶ms.Network{ChainID: 0}}, 531 {FromChain: ¶ms.Network{ChainID: ^uint64(0)}}, 532 }, 533 }, 534 }, 535 { 536 name: "Large number of routes", 537 routes: func() []routes.Route { 538 var routes1 []routes.Route 539 for i := 0; i < 1000; i++ { 540 routes1 = append(routes1, routes.Route{ 541 {FromChain: ¶ms.Network{ChainID: uint64(i + 1)}}, 542 {FromChain: ¶ms.Network{ChainID: uint64(i + 1001)}}, 543 }) 544 } 545 return routes1 546 }(), 547 fromLockedAmount: map[uint64]*hexutil.Big{ 548 1: (*hexutil.Big)(big.NewInt(100)), 549 1001: (*hexutil.Big)(big.NewInt(100)), 550 }, 551 expected: func() []routes.Route { 552 var routes1 []routes.Route 553 for i := 0; i < 1; i++ { 554 routes1 = append(routes1, routes.Route{ 555 {FromChain: ¶ms.Network{ChainID: uint64(i + 1)}}, 556 {FromChain: ¶ms.Network{ChainID: uint64(i + 1001)}}, 557 }) 558 } 559 return routes1 560 }(), 561 }, 562 { 563 name: "Routes with missing data", 564 routes: []routes.Route{ 565 { 566 {FromChain: nil}, 567 {FromChain: network2}, 568 }, 569 { 570 {FromChain: network1}, 571 {FromChain: nil}, 572 }, 573 }, 574 fromLockedAmount: map[uint64]*hexutil.Big{ 575 1: (*hexutil.Big)(big.NewInt(100)), 576 2: (*hexutil.Big)(big.NewInt(0)), 577 }, 578 expected: []routes.Route{}, 579 }, 580 { 581 name: "Consistency check", 582 routes: []routes.Route{ 583 { 584 {FromChain: network1}, 585 {FromChain: network2}, 586 }, 587 { 588 {FromChain: network1}, 589 {FromChain: network3}, 590 }, 591 }, 592 fromLockedAmount: map[uint64]*hexutil.Big{ 593 1: (*hexutil.Big)(big.NewInt(100)), 594 }, 595 expected: []routes.Route{ 596 { 597 {FromChain: network1}, 598 {FromChain: network2}, 599 }, 600 { 601 {FromChain: network1}, 602 {FromChain: network3}, 603 }, 604 }, 605 }, 606 { 607 name: "Routes without excluded chain IDs, missing included path", 608 routes: []routes.Route{ 609 {pathC1A1, pathC2A2}, 610 {pathC2A2, pathC3A3}, 611 }, 612 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2}, 613 expected: []routes.Route{ 614 {pathC1A1, pathC2A2}, 615 }, 616 }, 617 { 618 name: "Routes with an excluded chain ID", 619 routes: []routes.Route{ 620 {pathC1A1, pathC2A2}, 621 {pathC2A2, pathC3A3, path0}, 622 }, 623 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 4: &amount0}, 624 expected: []routes.Route{ 625 {pathC1A1, pathC2A2}, 626 }, 627 }, 628 { 629 name: "Routes with all included chain IDs", 630 routes: []routes.Route{ 631 {pathC1A1, pathC2A2, pathC3A3}, 632 }, 633 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3}, 634 expected: []routes.Route{ 635 {pathC1A1, pathC2A2, pathC3A3}, 636 }, 637 }, 638 { 639 name: "Routes missing one included chain ID", 640 routes: []routes.Route{ 641 {pathC1A1, pathC2A2}, 642 {pathC1A1}, 643 }, 644 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3}, 645 expected: []routes.Route{}, 646 }, 647 { 648 name: "Routes with no fromLockedAmount", 649 routes: []routes.Route{ 650 {pathC1A1, pathC2A2}, 651 {pathC2A2, pathC3A3}, 652 }, 653 fromLockedAmount: map[uint64]*hexutil.Big{}, 654 expected: []routes.Route{ 655 {pathC1A1, pathC2A2}, 656 {pathC2A2, pathC3A3}, 657 }, 658 }, 659 { 660 name: "Routes with fromExcluded only", 661 routes: []routes.Route{ 662 {pathC1A1, pathC2A2}, 663 {pathC2A2, pathC3A3}, 664 }, 665 fromLockedAmount: map[uint64]*hexutil.Big{4: &amount0}, 666 expected: []routes.Route{ 667 {pathC1A1, pathC2A2}, 668 {pathC2A2, pathC3A3}, 669 }, 670 }, 671 { 672 name: "Routes with all excluded chain IDs", 673 routes: []routes.Route{ 674 {path0, pathC1A1}, 675 {path0, pathC2A2}, 676 }, 677 fromLockedAmount: map[uint64]*hexutil.Big{1: &amount1, 2: &amount2, 3: &amount3, 4: &amount0}, 678 expected: []routes.Route{}, 679 }, 680 } 681 682 for _, tt := range tests { 683 t.Run(tt.name, func(t *testing.T) { 684 t.Logf("Original Routes: %+v\n", tt.routes) 685 filteredRoutes := filterNetworkCompliance(tt.routes, tt.fromLockedAmount) 686 t.Logf("Filtered Routes: %+v\n", filteredRoutes) 687 assert.Equal(t, tt.expected, filteredRoutes) 688 }) 689 } 690 } 691 692 func TestFilterCapacityValidation(t *testing.T) { 693 tests := []struct { 694 name string 695 routes []routes.Route 696 amountIn *big.Int 697 fromLockedAmount map[uint64]*hexutil.Big 698 expectedRoutes []routes.Route 699 }{ 700 { 701 name: "Sufficient capacity with multiple paths", 702 routes: []routes.Route{ 703 { 704 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 705 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 706 {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 707 }, 708 { 709 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 710 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(200))}, 711 }, 712 { 713 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 714 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(200))}, 715 }, 716 }, 717 amountIn: big.NewInt(250), 718 fromLockedAmount: map[uint64]*hexutil.Big{ 719 1: (*hexutil.Big)(big.NewInt(50)), 720 }, 721 expectedRoutes: []routes.Route{ 722 { 723 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 724 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 725 {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 726 }, 727 { 728 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 729 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(200))}, 730 }, 731 }, 732 }, 733 { 734 name: "Insufficient capacity", 735 routes: []routes.Route{ 736 { 737 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 738 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 739 }, 740 }, 741 amountIn: big.NewInt(200), 742 fromLockedAmount: map[uint64]*hexutil.Big{ 743 1: (*hexutil.Big)(big.NewInt(50)), 744 2: (*hexutil.Big)(big.NewInt(50)), 745 }, 746 expectedRoutes: []routes.Route{}, 747 }, 748 { 749 name: "Exact capacity match", 750 routes: []routes.Route{ 751 { 752 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 753 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 754 }, 755 }, 756 amountIn: big.NewInt(150), 757 fromLockedAmount: map[uint64]*hexutil.Big{ 758 1: (*hexutil.Big)(big.NewInt(100)), 759 2: (*hexutil.Big)(big.NewInt(50)), 760 }, 761 expectedRoutes: []routes.Route{ 762 { 763 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 764 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 765 }, 766 }, 767 }, 768 { 769 name: "No locked amounts", 770 routes: []routes.Route{ 771 { 772 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 773 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 774 }, 775 }, 776 amountIn: big.NewInt(150), 777 fromLockedAmount: map[uint64]*hexutil.Big{}, 778 expectedRoutes: []routes.Route{ 779 { 780 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 781 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 782 }, 783 }, 784 }, 785 { 786 name: "Single route with sufficient capacity", 787 routes: []routes.Route{ 788 { 789 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 790 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 791 }, 792 }, 793 amountIn: big.NewInt(150), 794 fromLockedAmount: map[uint64]*hexutil.Big{ 795 1: (*hexutil.Big)(big.NewInt(50)), 796 }, 797 expectedRoutes: []routes.Route{ 798 { 799 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 800 {FromChain: network2, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 801 }, 802 }, 803 }, 804 { 805 name: "Single route with inappropriately locked amount", 806 routes: []routes.Route{ 807 { 808 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 809 }, 810 }, 811 amountIn: big.NewInt(150), 812 fromLockedAmount: map[uint64]*hexutil.Big{ 813 1: (*hexutil.Big)(big.NewInt(50)), 814 }, 815 expectedRoutes: []routes.Route{}, 816 }, 817 { 818 name: "Single route with insufficient capacity", 819 routes: []routes.Route{ 820 { 821 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 822 }, 823 }, 824 amountIn: big.NewInt(150), 825 fromLockedAmount: map[uint64]*hexutil.Big{ 826 1: (*hexutil.Big)(big.NewInt(50)), 827 }, 828 expectedRoutes: []routes.Route{}, 829 }, 830 { 831 name: "Empty routes", 832 routes: []routes.Route{}, 833 amountIn: big.NewInt(150), 834 fromLockedAmount: map[uint64]*hexutil.Big{ 835 1: (*hexutil.Big)(big.NewInt(50)), 836 }, 837 expectedRoutes: []routes.Route{}, 838 }, 839 { 840 name: "Partial locked amounts", 841 routes: []routes.Route{ 842 { 843 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 844 {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 845 {FromChain: network4, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 846 }, 847 }, 848 amountIn: big.NewInt(250), 849 fromLockedAmount: map[uint64]*hexutil.Big{ 850 1: (*hexutil.Big)(big.NewInt(50)), 851 2: (*hexutil.Big)(big.NewInt(0)), // Excluded path 852 3: (*hexutil.Big)(big.NewInt(100)), 853 }, 854 expectedRoutes: []routes.Route{ 855 { 856 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(50))}, 857 {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 858 {FromChain: network4, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 859 }, 860 }, 861 }, 862 { 863 name: "Mixed networks with sufficient capacity", 864 routes: []routes.Route{ 865 { 866 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 867 {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(200))}, 868 }, 869 }, 870 amountIn: big.NewInt(300), 871 fromLockedAmount: map[uint64]*hexutil.Big{ 872 1: (*hexutil.Big)(big.NewInt(100)), 873 3: (*hexutil.Big)(big.NewInt(200)), 874 }, 875 expectedRoutes: []routes.Route{ 876 { 877 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 878 {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(200))}, 879 }, 880 }, 881 }, 882 { 883 name: "Mixed networks with insufficient capacity", 884 routes: []routes.Route{ 885 { 886 {FromChain: network1, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 887 {FromChain: network3, AmountIn: (*hexutil.Big)(big.NewInt(100))}, 888 }, 889 }, 890 amountIn: big.NewInt(250), 891 fromLockedAmount: map[uint64]*hexutil.Big{ 892 1: (*hexutil.Big)(big.NewInt(50)), 893 3: (*hexutil.Big)(big.NewInt(100)), 894 }, 895 expectedRoutes: []routes.Route{}, 896 }, 897 } 898 899 for _, tt := range tests { 900 t.Run(tt.name, func(t *testing.T) { 901 filteredRoutes := filterCapacityValidation(tt.routes, tt.amountIn, tt.fromLockedAmount) 902 if !routesEqual(t, tt.expectedRoutes, filteredRoutes) { 903 t.Errorf("Expected: %+v, Actual: %+v", tt.expectedRoutes, filteredRoutes) 904 } 905 }) 906 } 907 } 908 909 func TestFilterRoutes(t *testing.T) { 910 tests := []struct { 911 name string 912 routes []routes.Route 913 amountIn *big.Int 914 fromLockedAmount map[uint64]*hexutil.Big 915 expectedRoutes []routes.Route 916 }{ 917 { 918 name: "Empty fromLockedAmount and routes don't match amountIn", 919 routes: []routes.Route{ 920 {pathC1A1, pathC2A2}, 921 {pathC3A3, pathC4A4}, 922 }, 923 amountIn: big.NewInt(150), 924 fromLockedAmount: map[uint64]*hexutil.Big{}, 925 expectedRoutes: []routes.Route{}, 926 }, 927 { 928 name: "Empty fromLockedAmount and sigle route match amountIn", 929 routes: []routes.Route{ 930 {pathC1A1, pathC2A2}, 931 {pathC3A3, pathC4A4}, 932 }, 933 amountIn: big.NewInt(300), 934 fromLockedAmount: map[uint64]*hexutil.Big{}, 935 expectedRoutes: []routes.Route{ 936 {pathC1A1, pathC2A2}, 937 }, 938 }, 939 { 940 name: "Empty fromLockedAmount and more routes match amountIn", 941 routes: []routes.Route{ 942 {pathC1A1, pathC2A2}, 943 {pathC3A3, pathC4A4}, 944 {pathC1A1, pathC2A1, pathC3A1}, 945 }, 946 amountIn: big.NewInt(300), 947 fromLockedAmount: map[uint64]*hexutil.Big{}, 948 expectedRoutes: []routes.Route{ 949 {pathC1A1, pathC2A2}, 950 {pathC1A1, pathC2A1, pathC3A1}, 951 }, 952 }, 953 { 954 name: "All paths appear in fromLockedAmount but not within a single route", 955 routes: []routes.Route{ 956 {pathC1A1, pathC3A3}, 957 {pathC2A2, pathC4A4}, 958 }, 959 amountIn: big.NewInt(500), 960 fromLockedAmount: map[uint64]*hexutil.Big{ 961 1: &amount1, 962 2: &amount2, 963 3: &amount3, 964 4: &amount4, 965 }, 966 expectedRoutes: []routes.Route{}, 967 }, 968 { 969 name: "Mixed valid and invalid routes I", 970 routes: []routes.Route{ 971 {pathC1A1, pathC2A2}, 972 {pathC2A2, pathC3A3}, 973 {pathC1A1, pathC4A4}, 974 {pathC1A1, pathC2A1, pathC3A1}, 975 }, 976 amountIn: big.NewInt(300), 977 fromLockedAmount: map[uint64]*hexutil.Big{ 978 1: &amount1, 979 2: &amount2, 980 }, 981 expectedRoutes: []routes.Route{ 982 {pathC1A1, pathC2A2}, 983 }, 984 }, 985 { 986 name: "Mixed valid and invalid routes II", 987 routes: []routes.Route{ 988 {pathC1A1, pathC2A2}, 989 {pathC2A2, pathC3A3}, 990 {pathC1A1, pathC4A4}, 991 {pathC1A1, pathC2A1, pathC3A1}, 992 }, 993 amountIn: big.NewInt(300), 994 fromLockedAmount: map[uint64]*hexutil.Big{ 995 1: &amount1, 996 }, 997 expectedRoutes: []routes.Route{ 998 {pathC1A1, pathC2A2}, 999 {pathC1A1, pathC2A1, pathC3A1}, 1000 }, 1001 }, 1002 { 1003 name: "All invalid routes", 1004 routes: []routes.Route{ 1005 {pathC2A2, pathC3A3}, 1006 {pathC4A4, pathC5A5}, 1007 }, 1008 amountIn: big.NewInt(300), 1009 fromLockedAmount: map[uint64]*hexutil.Big{ 1010 1: &amount1, 1011 }, 1012 expectedRoutes: []routes.Route{}, 1013 }, 1014 { 1015 name: "Single valid route", 1016 routes: []routes.Route{ 1017 {pathC1A1, pathC3A3}, 1018 {pathC2A2, pathC3A3}, 1019 }, 1020 amountIn: big.NewInt(400), 1021 fromLockedAmount: map[uint64]*hexutil.Big{ 1022 1: &amount1, 1023 3: &amount3, 1024 }, 1025 expectedRoutes: []routes.Route{ 1026 {pathC1A1, pathC3A3}, 1027 }, 1028 }, 1029 { 1030 name: "Route with mixed valid and invalid paths I", 1031 routes: []routes.Route{ 1032 {pathC1A1, pathC2A2, pathC3A3}, 1033 }, 1034 amountIn: big.NewInt(300), 1035 fromLockedAmount: map[uint64]*hexutil.Big{ 1036 1: &amount1, 1037 2: &amount0, // This path should be filtered out due to being excluded via a zero amount 1038 }, 1039 expectedRoutes: []routes.Route{}, 1040 }, 1041 { 1042 name: "Route with mixed valid and invalid paths II", 1043 routes: []routes.Route{ 1044 {pathC1A1, pathC3A3}, 1045 }, 1046 amountIn: big.NewInt(400), 1047 fromLockedAmount: map[uint64]*hexutil.Big{ 1048 1: &amount1, 1049 2: &amount0, // This path should be filtered out due to being excluded via a zero amount, 0 value locked means this chain is disabled 1050 }, 1051 expectedRoutes: []routes.Route{ 1052 {pathC1A1, pathC3A3}, 1053 }, 1054 }, 1055 { 1056 name: "Route with mixed valid and invalid paths III", 1057 routes: []routes.Route{ 1058 {pathC1A1, pathC3A3}, 1059 {pathC1A1, pathC3A2, pathC4A1}, 1060 }, 1061 amountIn: big.NewInt(400), 1062 fromLockedAmount: map[uint64]*hexutil.Big{ 1063 1: &amount1, 1064 2: &amount0, // This path should be filtered out due to being excluded via a zero amount, 0 value locked means this chain is disabled 1065 }, 1066 expectedRoutes: []routes.Route{ 1067 {pathC1A1, pathC3A3}, 1068 {pathC1A1, pathC3A2, pathC4A1}, 1069 }, 1070 }, 1071 } 1072 1073 for _, tt := range tests { 1074 t.Run(tt.name, func(t *testing.T) { 1075 t.Logf("Original Routes: %+v\n", tt.routes) 1076 filteredRoutes := filterRoutes(tt.routes, tt.amountIn, tt.fromLockedAmount) 1077 t.Logf("Filtered Routes: %+v\n", filteredRoutes) 1078 assert.Equal(t, tt.expectedRoutes, filteredRoutes) 1079 }) 1080 } 1081 }