code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/amm_pool_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package sqlstore_test 17 18 import ( 19 "context" 20 "math/rand" 21 "sort" 22 "testing" 23 "time" 24 25 "code.vegaprotocol.io/vega/datanode/entities" 26 "code.vegaprotocol.io/vega/datanode/sqlstore" 27 "code.vegaprotocol.io/vega/libs/num" 28 "code.vegaprotocol.io/vega/libs/ptr" 29 30 "github.com/georgysavva/scany/pgxscan" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 ) 34 35 func TestAMMPool_Upsert(t *testing.T) { 36 ctx := tempTransaction(t) 37 38 bs := sqlstore.NewBlocks(connectionSource) 39 ps := sqlstore.NewAMMPools(connectionSource) 40 block := addTestBlock(t, ctx, bs) 41 42 partyID := entities.PartyID(GenerateID()) 43 marketID := entities.MarketID(GenerateID()) 44 poolID := entities.AMMPoolID(GenerateID()) 45 ammPartyID := entities.PartyID(GenerateID()) 46 47 t.Run("Upsert statuses", func(t *testing.T) { 48 upsertTests := []struct { 49 Status entities.AMMStatus 50 Reason entities.AMMStatusReason 51 }{ 52 {entities.AMMStatusActive, entities.AMMStatusReasonUnspecified}, 53 {entities.AMMStatusStopped, entities.AMMStatusReasonUnspecified}, 54 {entities.AMMStatusCancelled, entities.AMMStatusReasonUnspecified}, 55 {entities.AMMStatusRejected, entities.AMMStatusReasonCancelledByParty}, 56 {entities.AMMStatusRejected, entities.AMMStatusReasonCannotRebase}, 57 {entities.AMMStatusRejected, entities.AMMStatusReasonMarketClosed}, 58 {entities.AMMStatusRejected, entities.AMMStatusReasonCannotFillCommitment}, 59 {entities.AMMStatusRejected, entities.AMMStatusReasonCommitmentTooLow}, 60 {entities.AMMStatusRejected, entities.AMMStatusReasonPartyAlreadyOwnsAPool}, 61 {entities.AMMStatusRejected, entities.AMMStatusReasonPartyClosedOut}, 62 } 63 64 upsertTime := block.VegaTime 65 for i, test := range upsertTests { 66 upsertTime = upsertTime.Add(time.Duration(i) * time.Minute) 67 pool := entities.AMMPool{ 68 PartyID: partyID, 69 MarketID: marketID, 70 ID: poolID, 71 AmmPartyID: ammPartyID, 72 Commitment: num.DecimalFromInt64(100), 73 Status: test.Status, 74 StatusReason: test.Reason, 75 ParametersBase: num.DecimalFromInt64(100), 76 ParametersLowerBound: ptr.From(num.DecimalFromInt64(100)), 77 ParametersUpperBound: ptr.From(num.DecimalFromInt64(100)), 78 ParametersLeverageAtLowerBound: ptr.From(num.DecimalFromInt64(100)), 79 ParametersLeverageAtUpperBound: ptr.From(num.DecimalFromInt64(100)), 80 CreatedAt: block.VegaTime, 81 LastUpdated: upsertTime, 82 LowerVirtualLiquidity: num.DecimalOne(), 83 UpperVirtualLiquidity: num.DecimalOne(), 84 LowerTheoreticalPosition: num.DecimalOne(), 85 UpperTheoreticalPosition: num.DecimalOne(), 86 MinimumPriceChangeTrigger: num.DecimalOne(), 87 DataSourceID: entities.SpecID(""), 88 } 89 require.NoError(t, ps.Upsert(ctx, pool)) 90 var upserted entities.AMMPool 91 require.NoError(t, pgxscan.Get( 92 ctx, 93 connectionSource, 94 &upserted, 95 `SELECT * FROM amms WHERE party_id = $1 AND market_id = $2 AND id = $3 AND amm_party_id = $4`, 96 partyID, marketID, poolID, ammPartyID)) 97 assert.Equal(t, pool, upserted) 98 } 99 }) 100 101 t.Run("Upsert with different commitments and bounds", func(t *testing.T) { 102 amounts := []num.Decimal{ 103 num.DecimalFromInt64(100), 104 num.DecimalFromInt64(200), 105 num.DecimalFromInt64(300), 106 } 107 upsertTime := block.VegaTime 108 for i, amount := range amounts { 109 upsertTime = upsertTime.Add(time.Duration(i) * time.Minute) 110 pool := entities.AMMPool{ 111 PartyID: partyID, 112 MarketID: marketID, 113 ID: poolID, 114 AmmPartyID: ammPartyID, 115 Commitment: amount, 116 Status: entities.AMMStatusActive, 117 StatusReason: entities.AMMStatusReasonUnspecified, 118 ParametersBase: amount, 119 ParametersLowerBound: ptr.From(amount), 120 ParametersUpperBound: ptr.From(amount), 121 ParametersLeverageAtLowerBound: ptr.From(amount), 122 ParametersLeverageAtUpperBound: ptr.From(amount), 123 CreatedAt: block.VegaTime, 124 LastUpdated: upsertTime, 125 LowerVirtualLiquidity: num.DecimalOne(), 126 UpperVirtualLiquidity: num.DecimalOne(), 127 LowerTheoreticalPosition: num.DecimalOne(), 128 UpperTheoreticalPosition: num.DecimalOne(), 129 MinimumPriceChangeTrigger: num.DecimalOne(), 130 } 131 require.NoError(t, ps.Upsert(ctx, pool)) 132 var upserted entities.AMMPool 133 require.NoError(t, pgxscan.Get( 134 ctx, 135 connectionSource, 136 &upserted, 137 `SELECT * FROM amms WHERE party_id = $1 AND market_id = $2 AND id = $3 AND amm_party_id = $4`, 138 partyID, marketID, poolID, ammPartyID)) 139 assert.Equal(t, pool, upserted) 140 } 141 }) 142 143 t.Run("Upsert with empty leverages", func(t *testing.T) { 144 upsertTime := block.VegaTime 145 146 pool := entities.AMMPool{ 147 PartyID: partyID, 148 MarketID: marketID, 149 ID: poolID, 150 AmmPartyID: ammPartyID, 151 Commitment: num.DecimalFromInt64(100), 152 Status: entities.AMMStatusActive, 153 StatusReason: entities.AMMStatusReasonUnspecified, 154 ParametersBase: num.DecimalFromInt64(1800), 155 ParametersLowerBound: ptr.From(num.DecimalFromInt64(2000)), 156 ParametersUpperBound: ptr.From(num.DecimalFromInt64(2200)), 157 ParametersLeverageAtLowerBound: nil, 158 ParametersLeverageAtUpperBound: nil, 159 CreatedAt: block.VegaTime, 160 LastUpdated: upsertTime, 161 LowerVirtualLiquidity: num.DecimalOne(), 162 UpperVirtualLiquidity: num.DecimalOne(), 163 LowerTheoreticalPosition: num.DecimalOne(), 164 UpperTheoreticalPosition: num.DecimalOne(), 165 MinimumPriceChangeTrigger: num.DecimalOne(), 166 } 167 require.NoError(t, ps.Upsert(ctx, pool)) 168 var upserted entities.AMMPool 169 require.NoError(t, pgxscan.Get( 170 ctx, 171 connectionSource, 172 &upserted, 173 `SELECT * FROM amms WHERE party_id = $1 AND market_id = $2 AND id = $3 AND amm_party_id = $4`, 174 partyID, marketID, poolID, ammPartyID)) 175 assert.Equal(t, pool, upserted) 176 }) 177 } 178 179 type partyAccounts struct { 180 PartyID entities.PartyID 181 AMMPartyID entities.PartyID 182 } 183 184 func setupAMMPoolsTest(ctx context.Context, t *testing.T) ( 185 *sqlstore.AMMPools, []entities.AMMPool, []partyAccounts, []entities.MarketID, []entities.AMMPoolID, 186 ) { 187 t.Helper() 188 const ( 189 partyCount = 5 // every party will have a derived-party associated for AMM and this derived-party underlies all the AMM pools that are created 190 marketCount = 10 191 poolCount = 10 192 ) 193 194 bs := sqlstore.NewBlocks(connectionSource) 195 ps := sqlstore.NewAMMPools(connectionSource) 196 197 block := addTestBlock(t, tempTransaction(t), bs) 198 199 pools := make([]entities.AMMPool, 0, partyCount*marketCount*poolCount) 200 parties := make([]partyAccounts, 0, partyCount) 201 markets := make([]entities.MarketID, 0, marketCount) 202 poolIDs := make([]entities.AMMPoolID, 0, poolCount) 203 204 for i := 0; i < partyCount; i++ { 205 partyID := entities.PartyID(GenerateID()) 206 ammPartyID := entities.PartyID(GenerateID()) 207 208 parties = append(parties, partyAccounts{PartyID: partyID, AMMPartyID: ammPartyID}) 209 for j := 0; j < marketCount; j++ { 210 marketID := entities.MarketID(GenerateID()) 211 markets = append(markets, marketID) 212 for k := 0; k < poolCount; k++ { 213 poolID := entities.AMMPoolID(GenerateID()) 214 poolIDs = append(poolIDs, poolID) 215 status := entities.AMMStatusActive 216 statusReason := entities.AMMStatusReasonUnspecified 217 if (i+j+k)%2 == 0 { 218 status = entities.AMMStatusStopped 219 statusReason = entities.AMMStatusReasonCancelledByParty 220 } 221 pool := entities.AMMPool{ 222 PartyID: partyID, 223 MarketID: marketID, 224 ID: poolID, 225 AmmPartyID: ammPartyID, 226 Commitment: num.DecimalFromInt64(100), 227 Status: status, 228 StatusReason: statusReason, 229 ParametersBase: num.DecimalFromInt64(100), 230 ParametersLowerBound: ptr.From(num.DecimalFromInt64(100)), 231 ParametersUpperBound: ptr.From(num.DecimalFromInt64(100)), 232 ParametersLeverageAtLowerBound: ptr.From(num.DecimalFromInt64(100)), 233 ParametersLeverageAtUpperBound: ptr.From(num.DecimalFromInt64(100)), 234 CreatedAt: block.VegaTime, 235 LastUpdated: block.VegaTime, 236 LowerVirtualLiquidity: num.DecimalOne(), 237 UpperVirtualLiquidity: num.DecimalOne(), 238 LowerTheoreticalPosition: num.DecimalOne(), 239 UpperTheoreticalPosition: num.DecimalOne(), 240 MinimumPriceChangeTrigger: num.DecimalOne(), 241 } 242 require.NoError(t, ps.Upsert(ctx, pool)) 243 pools = append(pools, pool) 244 } 245 } 246 } 247 248 pools = orderPools(pools) 249 250 return ps, pools, parties, markets, poolIDs 251 } 252 253 func orderPools(pools []entities.AMMPool) []entities.AMMPool { 254 sort.Slice(pools, func(i, j int) bool { 255 return pools[i].CreatedAt.After(pools[j].CreatedAt) || 256 (pools[i].CreatedAt == pools[j].CreatedAt && pools[i].PartyID < pools[j].PartyID) || 257 (pools[i].CreatedAt == pools[j].CreatedAt && pools[i].PartyID == pools[j].PartyID && pools[i].AmmPartyID < pools[j].AmmPartyID) || 258 (pools[i].CreatedAt == pools[j].CreatedAt && pools[i].PartyID == pools[j].PartyID && pools[i].AmmPartyID == pools[j].AmmPartyID && pools[i].MarketID < pools[j].MarketID) || 259 (pools[i].CreatedAt == pools[j].CreatedAt && pools[i].PartyID == pools[j].PartyID && pools[i].AmmPartyID == pools[j].AmmPartyID && pools[i].MarketID == pools[j].MarketID && pools[i].ID <= pools[j].ID) 260 }) 261 262 return pools 263 } 264 265 func TestAMMPools_ListAll(t *testing.T) { 266 ctx := tempTransaction(t) 267 268 ps, pools, _, _, _ := setupAMMPoolsTest(ctx, t) 269 270 t.Run("Should return all pools if no pagination is provided", func(t *testing.T) { 271 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 272 require.NoError(t, err) 273 listedPools, pageInfo, err := ps.ListAll(ctx, false, pagination) 274 require.NoError(t, err) 275 assert.Equal(t, len(pools), len(listedPools)) 276 assert.Equal(t, pools, listedPools) 277 assert.Equal(t, entities.PageInfo{ 278 HasNextPage: false, 279 HasPreviousPage: false, 280 StartCursor: pools[0].Cursor().Encode(), 281 EndCursor: pools[len(pools)-1].Cursor().Encode(), 282 }, pageInfo) 283 }) 284 285 t.Run("Should return the first page of pools", func(t *testing.T) { 286 pagination, err := entities.NewCursorPagination(ptr.From(int32(5)), nil, nil, nil, true) 287 require.NoError(t, err) 288 listedPools, pageInfo, err := ps.ListAll(ctx, false, pagination) 289 require.NoError(t, err) 290 assert.Equal(t, 5, len(listedPools)) 291 assert.Equal(t, pools[:5], listedPools) 292 assert.Equal(t, entities.PageInfo{ 293 HasNextPage: true, 294 HasPreviousPage: false, 295 StartCursor: pools[0].Cursor().Encode(), 296 EndCursor: pools[4].Cursor().Encode(), 297 }, pageInfo) 298 }) 299 300 t.Run("Should return the last page of pools", func(t *testing.T) { 301 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(5)), nil, true) 302 require.NoError(t, err) 303 listedPools, pageInfo, err := ps.ListAll(ctx, false, pagination) 304 require.NoError(t, err) 305 assert.Equal(t, 5, len(listedPools)) 306 assert.Equal(t, pools[len(pools)-5:], listedPools) 307 assert.Equal(t, entities.PageInfo{ 308 HasNextPage: false, 309 HasPreviousPage: true, 310 StartCursor: pools[len(pools)-5].Cursor().Encode(), 311 EndCursor: pools[len(pools)-1].Cursor().Encode(), 312 }, pageInfo) 313 }) 314 315 t.Run("Should return the requested page when paging forward", func(t *testing.T) { 316 pagination, err := entities.NewCursorPagination(ptr.From(int32(5)), ptr.From(pools[20].Cursor().Encode()), nil, nil, true) 317 require.NoError(t, err) 318 listedPools, pageInfo, err := ps.ListAll(ctx, false, pagination) 319 require.NoError(t, err) 320 assert.Equal(t, 5, len(listedPools)) 321 assert.Equal(t, pools[21:26], listedPools) 322 assert.Equal(t, entities.PageInfo{ 323 HasNextPage: true, 324 HasPreviousPage: true, 325 StartCursor: pools[21].Cursor().Encode(), 326 EndCursor: pools[25].Cursor().Encode(), 327 }, pageInfo) 328 }) 329 330 t.Run("Should return the request page when paging backward", func(t *testing.T) { 331 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(5)), ptr.From(pools[20].Cursor().Encode()), true) 332 require.NoError(t, err) 333 listedPools, pageInfo, err := ps.ListAll(ctx, false, pagination) 334 require.NoError(t, err) 335 assert.Equal(t, 5, len(listedPools)) 336 assert.Equal(t, pools[15:20], listedPools) 337 assert.Equal(t, entities.PageInfo{ 338 HasNextPage: true, 339 HasPreviousPage: true, 340 StartCursor: pools[15].Cursor().Encode(), 341 EndCursor: pools[19].Cursor().Encode(), 342 }, pageInfo) 343 }) 344 } 345 346 func filterPools(pools []entities.AMMPool, filter func(entities.AMMPool) bool) []entities.AMMPool { 347 filtered := make([]entities.AMMPool, 0, len(pools)) 348 for _, pool := range pools { 349 if filter(pool) { 350 filtered = append(filtered, pool) 351 } 352 } 353 return filtered 354 } 355 356 func TestAMMPools_ListByMarket(t *testing.T) { 357 ctx := tempTransaction(t) 358 359 ps, pools, _, markets, _ := setupAMMPoolsTest(ctx, t) 360 src := rand.NewSource(time.Now().UnixNano()) 361 r := rand.New(src) 362 n := len(markets) 363 364 t.Run("Should return all pools if no pagination is provided", func(t *testing.T) { 365 // Randomly pick a market 366 market := markets[r.Intn(n)] 367 want := orderPools(filterPools(pools, func(pool entities.AMMPool) bool { 368 return pool.MarketID == market 369 })) 370 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 371 require.NoError(t, err) 372 listedPools, pageInfo, err := ps.ListByMarket(ctx, market, false, pagination) 373 require.NoError(t, err) 374 assert.Equal(t, len(want), len(listedPools)) 375 assert.Equal(t, want, listedPools) 376 assert.Equal(t, entities.PageInfo{ 377 HasNextPage: false, 378 HasPreviousPage: false, 379 StartCursor: want[0].Cursor().Encode(), 380 EndCursor: want[len(want)-1].Cursor().Encode(), 381 }, pageInfo) 382 }) 383 384 t.Run("Should return all active pools", func(t *testing.T) { 385 // Randomly pick a market 386 market := markets[r.Intn(n)] 387 want := orderPools(filterPools(pools, func(pool entities.AMMPool) bool { 388 if pool.MarketID != market { 389 return false 390 } 391 return pool.Status == entities.AMMStatusActive || pool.Status == entities.AMMStatusReduceOnly 392 })) 393 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 394 require.NoError(t, err) 395 listedPools, pageInfo, err := ps.ListByMarket(ctx, market, true, pagination) 396 require.NoError(t, err) 397 assert.Equal(t, len(want), len(listedPools)) 398 399 assert.Equal(t, want, listedPools) 400 assert.Equal(t, entities.PageInfo{ 401 HasNextPage: false, 402 HasPreviousPage: false, 403 StartCursor: want[0].Cursor().Encode(), 404 EndCursor: want[len(want)-1].Cursor().Encode(), 405 }, pageInfo) 406 }) 407 408 t.Run("Should return the first page of pools", func(t *testing.T) { 409 // Randomly pick a market 410 market := markets[r.Intn(n)] 411 want := orderPools(filterPools(pools, func(pool entities.AMMPool) bool { 412 return pool.MarketID == market 413 })) 414 pagination, err := entities.NewCursorPagination(ptr.From(int32(3)), nil, nil, nil, true) 415 require.NoError(t, err) 416 listedPools, pageInfo, err := ps.ListByMarket(ctx, market, false, pagination) 417 require.NoError(t, err) 418 assert.Equal(t, 3, len(listedPools)) 419 assert.Equal(t, want[:3], listedPools) 420 assert.Equal(t, entities.PageInfo{ 421 HasNextPage: true, 422 HasPreviousPage: false, 423 StartCursor: want[0].Cursor().Encode(), 424 EndCursor: want[2].Cursor().Encode(), 425 }, pageInfo) 426 }) 427 428 t.Run("Should return the last page of pools", func(t *testing.T) { 429 // Randomly pick a market 430 market := markets[r.Intn(n)] 431 want := orderPools(filterPools(pools, func(pool entities.AMMPool) bool { 432 return pool.MarketID == market 433 })) 434 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(3)), nil, true) 435 require.NoError(t, err) 436 listedPools, pageInfo, err := ps.ListByMarket(ctx, market, false, pagination) 437 require.NoError(t, err) 438 assert.Equal(t, 3, len(listedPools)) 439 assert.Equal(t, want[len(want)-3:], listedPools) 440 assert.Equal(t, entities.PageInfo{ 441 HasNextPage: false, 442 HasPreviousPage: true, 443 StartCursor: want[len(want)-3].Cursor().Encode(), 444 EndCursor: want[len(want)-1].Cursor().Encode(), 445 }, pageInfo) 446 }) 447 448 t.Run("Should return the requested page when paging forward", func(t *testing.T) { 449 // Randomly pick a market 450 market := markets[r.Intn(n)] 451 want := orderPools(filterPools(pools, func(pool entities.AMMPool) bool { 452 return pool.MarketID == market 453 })) 454 pagination, err := entities.NewCursorPagination(ptr.From(int32(3)), ptr.From(want[0].Cursor().Encode()), nil, nil, true) 455 require.NoError(t, err) 456 listedPools, pageInfo, err := ps.ListByMarket(ctx, market, false, pagination) 457 require.NoError(t, err) 458 assert.Equal(t, 3, len(listedPools)) 459 assert.Equal(t, want[1:4], listedPools) 460 assert.Equal(t, entities.PageInfo{ 461 HasNextPage: true, 462 HasPreviousPage: true, 463 StartCursor: want[1].Cursor().Encode(), 464 EndCursor: want[3].Cursor().Encode(), 465 }, pageInfo) 466 }) 467 468 t.Run("Should return the request page when paging backward", func(t *testing.T) { 469 // Randomly pick a market 470 market := markets[r.Intn(n)] 471 want := orderPools(filterPools(pools, func(pool entities.AMMPool) bool { 472 return pool.MarketID == market 473 })) 474 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(3)), ptr.From(want[4].Cursor().Encode()), true) 475 require.NoError(t, err) 476 listedPools, pageInfo, err := ps.ListByMarket(ctx, market, false, pagination) 477 require.NoError(t, err) 478 assert.Equal(t, 3, len(listedPools)) 479 assert.Equal(t, want[1:4], listedPools) 480 assert.Equal(t, entities.PageInfo{ 481 HasNextPage: true, 482 HasPreviousPage: true, 483 StartCursor: want[1].Cursor().Encode(), 484 EndCursor: want[3].Cursor().Encode(), 485 }, pageInfo) 486 }) 487 } 488 489 func TestAMMPools_ListByParty(t *testing.T) { 490 ctx := tempTransaction(t) 491 492 ps, pools, parties, _, _ := setupAMMPoolsTest(ctx, t) 493 src := rand.NewSource(time.Now().UnixNano()) 494 r := rand.New(src) 495 n := len(parties) 496 497 t.Run("Should return all pools if no pagination is provided", func(t *testing.T) { 498 // Randomly pick a party 499 party := parties[r.Intn(n)] 500 want := filterPools(pools, func(pool entities.AMMPool) bool { 501 return pool.PartyID == party.PartyID 502 }) 503 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 504 require.NoError(t, err) 505 listedPools, pageInfo, err := ps.ListByParty(ctx, party.PartyID, false, pagination) 506 require.NoError(t, err) 507 assert.Equal(t, len(want), len(listedPools)) 508 assert.Equal(t, want, listedPools) 509 assert.Equal(t, entities.PageInfo{ 510 HasNextPage: false, 511 HasPreviousPage: false, 512 StartCursor: want[0].Cursor().Encode(), 513 EndCursor: want[len(want)-1].Cursor().Encode(), 514 }, pageInfo) 515 }) 516 517 t.Run("Should return the first page of pools", func(t *testing.T) { 518 // Randomly pick a party 519 party := parties[r.Intn(n)] 520 want := filterPools(pools, func(pool entities.AMMPool) bool { 521 return pool.PartyID == party.PartyID 522 }) 523 pagination, err := entities.NewCursorPagination(ptr.From(int32(5)), nil, nil, nil, true) 524 require.NoError(t, err) 525 listedPools, pageInfo, err := ps.ListByParty(ctx, party.PartyID, false, pagination) 526 require.NoError(t, err) 527 assert.Equal(t, 5, len(listedPools)) 528 assert.Equal(t, want[:5], listedPools) 529 assert.Equal(t, entities.PageInfo{ 530 HasNextPage: true, 531 HasPreviousPage: false, 532 StartCursor: want[0].Cursor().Encode(), 533 EndCursor: want[4].Cursor().Encode(), 534 }, pageInfo) 535 }) 536 537 t.Run("Should return the last page of pools", func(t *testing.T) { 538 // Randomly pick a party 539 party := parties[r.Intn(n)] 540 want := filterPools(pools, func(pool entities.AMMPool) bool { 541 return pool.PartyID == party.PartyID 542 }) 543 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(5)), nil, true) 544 require.NoError(t, err) 545 listedPools, pageInfo, err := ps.ListByParty(ctx, party.PartyID, false, pagination) 546 require.NoError(t, err) 547 assert.Equal(t, 5, len(listedPools)) 548 assert.Equal(t, want[len(want)-5:], listedPools) 549 assert.Equal(t, entities.PageInfo{ 550 HasNextPage: false, 551 HasPreviousPage: true, 552 StartCursor: want[len(want)-5].Cursor().Encode(), 553 EndCursor: want[len(want)-1].Cursor().Encode(), 554 }, pageInfo) 555 }) 556 557 t.Run("Should return the requested page when paging forward", func(t *testing.T) { 558 // Randomly pick a party 559 party := parties[r.Intn(n)] 560 want := filterPools(pools, func(pool entities.AMMPool) bool { 561 return pool.PartyID == party.PartyID 562 }) 563 pagination, err := entities.NewCursorPagination(ptr.From(int32(5)), ptr.From(want[10].Cursor().Encode()), nil, nil, true) 564 require.NoError(t, err) 565 listedPools, pageInfo, err := ps.ListByParty(ctx, party.PartyID, false, pagination) 566 require.NoError(t, err) 567 assert.Equal(t, 5, len(listedPools)) 568 assert.Equal(t, want[11:16], listedPools) 569 assert.Equal(t, entities.PageInfo{ 570 HasNextPage: true, 571 HasPreviousPage: true, 572 StartCursor: want[11].Cursor().Encode(), 573 EndCursor: want[15].Cursor().Encode(), 574 }, pageInfo) 575 }) 576 577 t.Run("Should return the request page when paging backward", func(t *testing.T) { 578 // Randomly pick a party 579 party := parties[r.Intn(n)] 580 want := filterPools(pools, func(pool entities.AMMPool) bool { 581 return pool.PartyID == party.PartyID 582 }) 583 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(5)), ptr.From(want[10].Cursor().Encode()), true) 584 require.NoError(t, err) 585 listedPools, pageInfo, err := ps.ListByParty(ctx, party.PartyID, false, pagination) 586 require.NoError(t, err) 587 assert.Equal(t, 5, len(listedPools)) 588 assert.Equal(t, want[5:10], listedPools) 589 assert.Equal(t, entities.PageInfo{ 590 HasNextPage: true, 591 HasPreviousPage: true, 592 StartCursor: want[5].Cursor().Encode(), 593 EndCursor: want[9].Cursor().Encode(), 594 }, pageInfo) 595 }) 596 } 597 598 func TestAMMPools_ListByPartyMarketStatus(t *testing.T) { 599 ctx := tempTransaction(t) 600 601 ps, pools, parties, _, _ := setupAMMPoolsTest(ctx, t) 602 src := rand.NewSource(time.Now().UnixNano()) 603 r := rand.New(src) 604 n := len(parties) 605 606 t.Run("Should list active only", func(t *testing.T) { 607 // Randomly pick a party 608 party := parties[r.Intn(n)] 609 want := filterPools(pools, func(pool entities.AMMPool) bool { 610 if pool.PartyID != party.PartyID { 611 return false 612 } 613 return pool.Status == entities.AMMStatusActive || pool.Status == entities.AMMStatusReduceOnly 614 }) 615 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 616 require.NoError(t, err) 617 listedPools, pageInfo, err := ps.ListByPartyMarketStatus(ctx, &party.PartyID, nil, nil, true, pagination) 618 require.NoError(t, err) 619 assert.Equal(t, len(want), len(listedPools)) 620 assert.Equal(t, want, listedPools) 621 assert.Equal(t, entities.PageInfo{ 622 HasNextPage: false, 623 HasPreviousPage: false, 624 StartCursor: want[0].Cursor().Encode(), 625 EndCursor: want[len(want)-1].Cursor().Encode(), 626 }, pageInfo) 627 }) 628 } 629 630 func TestAMMPools_ListByPool(t *testing.T) { 631 ctx := tempTransaction(t) 632 633 ps, pools, _, _, poolIDs := setupAMMPoolsTest(ctx, t) 634 src := rand.NewSource(time.Now().UnixNano()) 635 r := rand.New(src) 636 n := len(poolIDs) 637 638 t.Run("Should return the pool if the pool ID exists", func(t *testing.T) { 639 pa := poolIDs[r.Intn(n)] 640 want := filterPools(pools, func(pool entities.AMMPool) bool { 641 return pool.ID == pa 642 }) 643 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 644 require.NoError(t, err) 645 listedPools, pageInfo, err := ps.ListByPool(ctx, pa, false, pagination) 646 require.NoError(t, err) 647 assert.Equal(t, len(want), len(listedPools)) 648 assert.Equal(t, want, listedPools) 649 assert.Equal(t, entities.PageInfo{ 650 HasNextPage: false, 651 HasPreviousPage: false, 652 StartCursor: want[0].Cursor().Encode(), 653 EndCursor: want[len(want)-1].Cursor().Encode(), 654 }, pageInfo) 655 }) 656 } 657 658 func TestAMMPools_ListBySubAccount(t *testing.T) { 659 ctx := tempTransaction(t) 660 661 ps, pools, parties, _, _ := setupAMMPoolsTest(ctx, t) 662 src := rand.NewSource(time.Now().UnixNano()) 663 r := rand.New(src) 664 n := len(parties) 665 666 t.Run("Should return all pools if no pagination is provided", func(t *testing.T) { 667 // Randomly pick a sub account 668 party := parties[r.Intn(n)] 669 want := filterPools(pools, func(pool entities.AMMPool) bool { 670 return pool.AmmPartyID == party.AMMPartyID 671 }) 672 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 673 require.NoError(t, err) 674 listedPools, pageInfo, err := ps.ListBySubAccount(ctx, party.AMMPartyID, false, pagination) 675 require.NoError(t, err) 676 assert.Equal(t, len(want), len(listedPools)) 677 assert.Equal(t, want, listedPools) 678 assert.Equal(t, entities.PageInfo{ 679 HasNextPage: false, 680 HasPreviousPage: false, 681 StartCursor: want[0].Cursor().Encode(), 682 EndCursor: want[len(want)-1].Cursor().Encode(), 683 }, pageInfo) 684 }) 685 686 t.Run("Should return the first page of pools", func(t *testing.T) { 687 // Randomly pick a sub account 688 party := parties[r.Intn(n)] 689 want := filterPools(pools, func(pool entities.AMMPool) bool { 690 return pool.AmmPartyID == party.AMMPartyID 691 }) 692 pagination, err := entities.NewCursorPagination(ptr.From(int32(5)), nil, nil, nil, true) 693 require.NoError(t, err) 694 listedPools, pageInfo, err := ps.ListBySubAccount(ctx, party.AMMPartyID, false, pagination) 695 require.NoError(t, err) 696 assert.Equal(t, 5, len(listedPools)) 697 assert.Equal(t, want[:5], listedPools) 698 assert.Equal(t, entities.PageInfo{ 699 HasNextPage: true, 700 HasPreviousPage: false, 701 StartCursor: want[0].Cursor().Encode(), 702 EndCursor: want[4].Cursor().Encode(), 703 }, pageInfo) 704 }) 705 706 t.Run("Should return the last page of pools", func(t *testing.T) { 707 // Randomly pick a sub account 708 party := parties[r.Intn(n)] 709 want := filterPools(pools, func(pool entities.AMMPool) bool { 710 return pool.AmmPartyID == party.AMMPartyID 711 }) 712 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(5)), nil, true) 713 require.NoError(t, err) 714 listedPools, pageInfo, err := ps.ListBySubAccount(ctx, party.AMMPartyID, false, pagination) 715 require.NoError(t, err) 716 assert.Equal(t, 5, len(listedPools)) 717 assert.Equal(t, want[len(want)-5:], listedPools) 718 assert.Equal(t, entities.PageInfo{ 719 HasNextPage: false, 720 HasPreviousPage: true, 721 StartCursor: want[len(want)-5].Cursor().Encode(), 722 EndCursor: want[len(want)-1].Cursor().Encode(), 723 }, pageInfo) 724 }) 725 726 t.Run("Should return the requested page when paging forward", func(t *testing.T) { 727 // Randomly pick a sub account 728 party := parties[r.Intn(n)] 729 want := filterPools(pools, func(pool entities.AMMPool) bool { 730 return pool.AmmPartyID == party.AMMPartyID 731 }) 732 pagination, err := entities.NewCursorPagination(ptr.From(int32(5)), ptr.From(want[10].Cursor().Encode()), nil, nil, true) 733 require.NoError(t, err) 734 listedPools, pageInfo, err := ps.ListBySubAccount(ctx, party.AMMPartyID, false, pagination) 735 require.NoError(t, err) 736 assert.Equal(t, 5, len(listedPools)) 737 assert.Equal(t, want[11:16], listedPools) 738 assert.Equal(t, entities.PageInfo{ 739 HasNextPage: true, 740 HasPreviousPage: true, 741 StartCursor: want[11].Cursor().Encode(), 742 EndCursor: want[15].Cursor().Encode(), 743 }, pageInfo) 744 }) 745 746 t.Run("Should return the request page when paging backward", func(t *testing.T) { 747 // Randomly pick a sub account 748 party := parties[r.Intn(n)] 749 want := filterPools(pools, func(pool entities.AMMPool) bool { 750 return pool.AmmPartyID == party.AMMPartyID 751 }) 752 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(5)), ptr.From(want[10].Cursor().Encode()), true) 753 require.NoError(t, err) 754 listedPools, pageInfo, err := ps.ListBySubAccount(ctx, party.AMMPartyID, false, pagination) 755 require.NoError(t, err) 756 assert.Equal(t, 5, len(listedPools)) 757 assert.Equal(t, want[5:10], listedPools) 758 assert.Equal(t, entities.PageInfo{ 759 HasNextPage: true, 760 HasPreviousPage: true, 761 StartCursor: want[5].Cursor().Encode(), 762 EndCursor: want[9].Cursor().Encode(), 763 }, pageInfo) 764 }) 765 } 766 767 func TestAMMPools_ListByStatus(t *testing.T) { 768 ctx := tempTransaction(t) 769 770 ps, pools, _, _, _ := setupAMMPoolsTest(ctx, t) 771 772 t.Run("Should return all pools if no pagination is provided", func(t *testing.T) { 773 want := filterPools(pools, func(pool entities.AMMPool) bool { 774 return pool.Status == entities.AMMStatusActive 775 }) 776 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 777 require.NoError(t, err) 778 listedPools, pageInfo, err := ps.ListByStatus(ctx, entities.AMMStatusActive, pagination) 779 require.NoError(t, err) 780 assert.Equal(t, len(want), len(listedPools)) 781 assert.Equal(t, want, listedPools) 782 assert.Equal(t, entities.PageInfo{ 783 HasNextPage: false, 784 HasPreviousPage: false, 785 StartCursor: want[0].Cursor().Encode(), 786 EndCursor: want[len(want)-1].Cursor().Encode(), 787 }, pageInfo) 788 }) 789 790 t.Run("Should return the first page of pools", func(t *testing.T) { 791 want := filterPools(pools, func(pool entities.AMMPool) bool { 792 return pool.Status == entities.AMMStatusActive 793 }) 794 pagination, err := entities.NewCursorPagination(ptr.From(int32(5)), nil, nil, nil, true) 795 require.NoError(t, err) 796 listedPools, pageInfo, err := ps.ListByStatus(ctx, entities.AMMStatusActive, pagination) 797 require.NoError(t, err) 798 assert.Equal(t, 5, len(listedPools)) 799 assert.Equal(t, want[:5], listedPools) 800 assert.Equal(t, entities.PageInfo{ 801 HasNextPage: true, 802 HasPreviousPage: false, 803 StartCursor: want[0].Cursor().Encode(), 804 EndCursor: want[4].Cursor().Encode(), 805 }, pageInfo) 806 }) 807 808 t.Run("Should return the last page of pools", func(t *testing.T) { 809 want := filterPools(pools, func(pool entities.AMMPool) bool { 810 return pool.Status == entities.AMMStatusActive 811 }) 812 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(5)), nil, true) 813 require.NoError(t, err) 814 listedPools, pageInfo, err := ps.ListByStatus(ctx, entities.AMMStatusActive, pagination) 815 require.NoError(t, err) 816 assert.Equal(t, 5, len(listedPools)) 817 assert.Equal(t, want[len(want)-5:], listedPools) 818 assert.Equal(t, entities.PageInfo{ 819 HasNextPage: false, 820 HasPreviousPage: true, 821 StartCursor: want[len(want)-5].Cursor().Encode(), 822 EndCursor: want[len(want)-1].Cursor().Encode(), 823 }, pageInfo) 824 }) 825 826 t.Run("Should return the requested page when paging forward", func(t *testing.T) { 827 want := filterPools(pools, func(pool entities.AMMPool) bool { 828 return pool.Status == entities.AMMStatusActive 829 }) 830 pagination, err := entities.NewCursorPagination(ptr.From(int32(5)), ptr.From(want[10].Cursor().Encode()), nil, nil, true) 831 require.NoError(t, err) 832 listedPools, pageInfo, err := ps.ListByStatus(ctx, entities.AMMStatusActive, pagination) 833 require.NoError(t, err) 834 assert.Equal(t, 5, len(listedPools)) 835 assert.Equal(t, want[11:16], listedPools) 836 assert.Equal(t, entities.PageInfo{ 837 HasNextPage: true, 838 HasPreviousPage: true, 839 StartCursor: want[11].Cursor().Encode(), 840 EndCursor: want[15].Cursor().Encode(), 841 }, pageInfo) 842 }) 843 844 t.Run("Should return the request page when paging backward", func(t *testing.T) { 845 want := filterPools(pools, func(pool entities.AMMPool) bool { 846 return pool.Status == entities.AMMStatusActive 847 }) 848 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(5)), ptr.From(want[10].Cursor().Encode()), true) 849 require.NoError(t, err) 850 listedPools, pageInfo, err := ps.ListByStatus(ctx, entities.AMMStatusActive, pagination) 851 require.NoError(t, err) 852 assert.Equal(t, 5, len(listedPools)) 853 assert.Equal(t, want[5:10], listedPools) 854 assert.Equal(t, entities.PageInfo{ 855 HasNextPage: true, 856 HasPreviousPage: true, 857 StartCursor: want[5].Cursor().Encode(), 858 EndCursor: want[9].Cursor().Encode(), 859 }, pageInfo) 860 }) 861 } 862 863 func TestAMMPools_ListActive(t *testing.T) { 864 ctx := tempTransaction(t) 865 866 ps, in, _, _, _ := setupAMMPoolsTest(ctx, t) 867 var nActive int 868 for _, p := range in { 869 if p.Status == entities.AMMStatusActive || p.Status == entities.AMMStatusReduceOnly { 870 nActive++ 871 } 872 } 873 require.NotEqual(t, 0, nActive) 874 875 out, err := ps.ListActive(ctx) 876 require.NoError(t, err) 877 assert.Equal(t, nActive, len(out)) 878 }