code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/funding_period_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 "sort" 21 "testing" 22 "time" 23 24 "code.vegaprotocol.io/vega/datanode/entities" 25 "code.vegaprotocol.io/vega/datanode/sqlstore" 26 "code.vegaprotocol.io/vega/datanode/sqlstore/helpers" 27 "code.vegaprotocol.io/vega/libs/num" 28 "code.vegaprotocol.io/vega/libs/ptr" 29 eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" 30 31 "github.com/georgysavva/scany/pgxscan" 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 ) 35 36 type fundingPeriodTestStores struct { 37 bs *sqlstore.Blocks 38 ms *sqlstore.Markets 39 fp *sqlstore.FundingPeriods 40 41 blocks []entities.Block 42 markets []entities.Market 43 periods []entities.FundingPeriod 44 dataPoints []entities.FundingPeriodDataPoint 45 } 46 47 func setupFundingPeriodTests(ctx context.Context, t *testing.T) *fundingPeriodTestStores { 48 t.Helper() 49 bs := sqlstore.NewBlocks(connectionSource) 50 ms := sqlstore.NewMarkets(connectionSource) 51 fp := sqlstore.NewFundingPeriods(connectionSource) 52 53 return newFundingPeriodTestStores(bs, ms, fp).Initialize(ctx, t) 54 } 55 56 func newFundingPeriodTestStores(bs *sqlstore.Blocks, ms *sqlstore.Markets, fp *sqlstore.FundingPeriods) *fundingPeriodTestStores { 57 return &fundingPeriodTestStores{ 58 bs: bs, 59 ms: ms, 60 fp: fp, 61 } 62 } 63 64 func (s *fundingPeriodTestStores) Initialize(ctx context.Context, t *testing.T) *fundingPeriodTestStores { 65 t.Helper() 66 s.blocks = make([]entities.Block, 0, 10) 67 s.markets = make([]entities.Market, 0, 3) 68 69 for i := 0; i < 10; i++ { 70 block := addTestBlock(t, ctx, s.bs) 71 s.blocks = append(s.blocks, block) 72 if i < 3 { 73 s.markets = append(s.markets, helpers.AddTestMarket(t, ctx, s.ms, block)) 74 } 75 } 76 77 return s 78 } 79 80 func TestFundingPeriod_AddFundingPeriod(t *testing.T) { 81 t.Run("should add funding period if the market exists and the sequence number does not exist", testAddFundingPeriodShouldSucceedIfMarketExistsAndSequenceDoesNotExist) 82 t.Run("should update funding period if the market exists and the sequence number already exists", testAddFundingPeriodShouldUpdateIfMarketExistsAndSequenceExists) 83 } 84 85 func testAddFundingPeriodShouldSucceedIfMarketExistsAndSequenceDoesNotExist(t *testing.T) { 86 ctx := tempTransaction(t) 87 88 stores := setupFundingPeriodTests(ctx, t) 89 90 period := entities.FundingPeriod{ 91 MarketID: stores.markets[0].ID, 92 FundingPeriodSeq: 1, 93 StartTime: stores.blocks[3].VegaTime, 94 EndTime: nil, 95 FundingPayment: nil, 96 FundingRate: nil, 97 VegaTime: stores.blocks[3].VegaTime, 98 TxHash: generateTxHash(), 99 } 100 101 err := stores.fp.AddFundingPeriod(ctx, &period) 102 require.NoError(t, err) 103 } 104 105 func testAddFundingPeriodShouldUpdateIfMarketExistsAndSequenceExists(t *testing.T) { 106 ctx := tempTransaction(t) 107 108 stores := setupFundingPeriodTests(ctx, t) 109 110 period := entities.FundingPeriod{ 111 MarketID: stores.markets[0].ID, 112 FundingPeriodSeq: 1, 113 StartTime: stores.blocks[3].VegaTime, 114 EndTime: nil, 115 FundingPayment: nil, 116 FundingRate: nil, 117 ExternalTwap: nil, 118 InternalTwap: nil, 119 VegaTime: stores.blocks[3].VegaTime, 120 TxHash: generateTxHash(), 121 } 122 123 err := stores.fp.AddFundingPeriod(ctx, &period) 124 require.NoError(t, err) 125 126 var dbResult entities.FundingPeriod 127 err = pgxscan.Get(ctx, stores.fp, &dbResult, `select * from funding_period where market_id = $1 and funding_period_seq = $2`, stores.markets[0].ID, 1) 128 require.NoError(t, err) 129 assert.Equal(t, period, dbResult) 130 131 period.EndTime = &stores.blocks[9].VegaTime 132 period.FundingPayment = ptr.From(num.DecimalFromFloat(1.0)) 133 period.FundingRate = ptr.From(num.DecimalFromFloat(1.0)) 134 period.ExternalTwap = ptr.From(num.DecimalFromFloat(1.0)) 135 period.InternalTwap = ptr.From(num.DecimalFromFloat(1.1)) 136 period.VegaTime = stores.blocks[9].VegaTime 137 period.TxHash = generateTxHash() 138 139 err = stores.fp.AddFundingPeriod(ctx, &period) 140 require.NoError(t, err) 141 142 err = pgxscan.Get(ctx, stores.fp, &dbResult, `select * from funding_period where market_id = $1 and funding_period_seq = $2`, stores.markets[0].ID, 1) 143 require.NoError(t, err) 144 assert.Equal(t, period, dbResult) 145 } 146 147 func TestFundingPeriod_AddFundingPeriodDataPoint(t *testing.T) { 148 t.Run("should add data points for existing funding periods", testAddForExistingFundingPeriods) 149 t.Run("should not error if the funding period does not exist", testShouldNotErrorIfNoFundingPeriod) 150 t.Run("should update the data point if multiple data points for the same source is received in the same block", testShouldUpdateDataPointInSameBlock) 151 } 152 153 func testAddForExistingFundingPeriods(t *testing.T) { 154 ctx := tempTransaction(t) 155 156 stores := setupFundingPeriodTests(ctx, t) 157 158 period := entities.FundingPeriod{ 159 MarketID: stores.markets[0].ID, 160 FundingPeriodSeq: 1, 161 StartTime: stores.blocks[3].VegaTime, 162 EndTime: nil, 163 FundingPayment: nil, 164 FundingRate: nil, 165 VegaTime: stores.blocks[3].VegaTime, 166 TxHash: generateTxHash(), 167 } 168 169 err := stores.fp.AddFundingPeriod(ctx, &period) 170 require.NoError(t, err) 171 172 dataPoint := entities.FundingPeriodDataPoint{ 173 MarketID: stores.markets[0].ID, 174 FundingPeriodSeq: 1, 175 DataPointType: entities.FundingPeriodDataPointSourceExternal, 176 Price: num.DecimalFromFloat(1.0), 177 Timestamp: stores.blocks[4].VegaTime, 178 VegaTime: stores.blocks[4].VegaTime, 179 TxHash: generateTxHash(), 180 } 181 182 err = stores.fp.AddDataPoint(ctx, &dataPoint) 183 require.NoError(t, err) 184 } 185 186 func testShouldNotErrorIfNoFundingPeriod(t *testing.T) { 187 // Note: this test was changed from should error to should not error as we can not rely on the 188 // foreign key constraint to the funding_period table which has been dropped due to the 189 // funding_period_data_point table being migrated to a TimescaleDB hypertable. 190 ctx := tempTransaction(t) 191 192 stores := setupFundingPeriodTests(ctx, t) 193 194 dataPoint := entities.FundingPeriodDataPoint{ 195 MarketID: stores.markets[0].ID, 196 FundingPeriodSeq: 2, 197 DataPointType: entities.FundingPeriodDataPointSourceExternal, 198 Price: num.DecimalFromFloat(100.0), 199 Timestamp: stores.blocks[4].VegaTime, 200 VegaTime: stores.blocks[4].VegaTime, 201 TxHash: generateTxHash(), 202 } 203 204 err := stores.fp.AddDataPoint(ctx, &dataPoint) 205 require.NoError(t, err) 206 } 207 208 func testShouldUpdateDataPointInSameBlock(t *testing.T) { 209 ctx := tempTransaction(t) 210 211 stores := setupFundingPeriodTests(ctx, t) 212 213 period := entities.FundingPeriod{ 214 MarketID: stores.markets[0].ID, 215 FundingPeriodSeq: 1, 216 StartTime: stores.blocks[3].VegaTime, 217 EndTime: nil, 218 FundingPayment: nil, 219 FundingRate: nil, 220 ExternalTwap: nil, 221 InternalTwap: nil, 222 VegaTime: stores.blocks[3].VegaTime, 223 TxHash: generateTxHash(), 224 } 225 226 err := stores.fp.AddFundingPeriod(ctx, &period) 227 require.NoError(t, err) 228 229 dp1 := entities.FundingPeriodDataPoint{ 230 MarketID: stores.markets[0].ID, 231 FundingPeriodSeq: 1, 232 DataPointType: entities.FundingPeriodDataPointSourceExternal, 233 Price: num.DecimalFromFloat(1.0), 234 Twap: num.DecimalFromFloat(1.0), 235 Timestamp: stores.blocks[4].VegaTime, 236 VegaTime: stores.blocks[4].VegaTime, 237 TxHash: generateTxHash(), 238 } 239 240 err = stores.fp.AddDataPoint(ctx, &dp1) 241 require.NoError(t, err) 242 243 var inserted []entities.FundingPeriodDataPoint 244 err = pgxscan.Select(ctx, connectionSource, &inserted, 245 `SELECT * FROM funding_period_data_points where market_id = $1 and funding_period_seq = $2 and data_point_type = $3 and vega_time = $4`, 246 stores.markets[0].ID, 1, entities.FundingPeriodDataPointSourceExternal, stores.blocks[4].VegaTime) 247 require.NoError(t, err) 248 assert.Len(t, inserted, 1) 249 assert.Equal(t, dp1, inserted[0]) 250 251 dp2 := entities.FundingPeriodDataPoint{ 252 MarketID: stores.markets[0].ID, 253 FundingPeriodSeq: 1, 254 DataPointType: entities.FundingPeriodDataPointSourceExternal, 255 Price: num.DecimalFromFloat(2.0), 256 Twap: num.DecimalFromFloat(2.0), 257 Timestamp: stores.blocks[4].VegaTime.Add(100 * time.Microsecond), 258 VegaTime: stores.blocks[4].VegaTime, 259 TxHash: generateTxHash(), 260 } 261 262 err = stores.fp.AddDataPoint(ctx, &dp2) 263 require.NoError(t, err) 264 265 err = pgxscan.Select(ctx, connectionSource, &inserted, 266 `SELECT * FROM funding_period_data_points where market_id = $1 and funding_period_seq = $2 and data_point_type = $3 and vega_time = $4`, 267 stores.markets[0].ID, 1, entities.FundingPeriodDataPointSourceExternal, stores.blocks[4].VegaTime) 268 require.NoError(t, err) 269 assert.Len(t, inserted, 1) 270 assert.Equal(t, dp2, inserted[0]) 271 } 272 273 func addFundingPeriodsAndDataPoints(t *testing.T, ctx context.Context, stores *fundingPeriodTestStores) { 274 t.Helper() 275 stores.periods = []entities.FundingPeriod{ 276 { 277 MarketID: stores.markets[0].ID, 278 FundingPeriodSeq: 1, 279 StartTime: stores.blocks[1].VegaTime, 280 EndTime: nil, 281 FundingPayment: ptr.From(num.DecimalFromFloat(1)), 282 FundingRate: ptr.From(num.DecimalFromFloat(1)), 283 ExternalTwap: ptr.From(num.DecimalFromFloat(1)), 284 InternalTwap: ptr.From(num.DecimalFromFloat(1)), 285 VegaTime: stores.blocks[1].VegaTime, 286 TxHash: generateTxHash(), 287 }, 288 { 289 MarketID: stores.markets[0].ID, 290 FundingPeriodSeq: 2, 291 StartTime: stores.blocks[3].VegaTime, 292 EndTime: nil, 293 FundingPayment: ptr.From(num.DecimalFromFloat(2)), 294 FundingRate: ptr.From(num.DecimalFromFloat(2)), 295 ExternalTwap: ptr.From(num.DecimalFromFloat(2)), 296 InternalTwap: ptr.From(num.DecimalFromFloat(2)), 297 VegaTime: stores.blocks[3].VegaTime, 298 TxHash: generateTxHash(), 299 }, 300 { 301 MarketID: stores.markets[0].ID, 302 FundingPeriodSeq: 3, 303 StartTime: stores.blocks[5].VegaTime, 304 EndTime: nil, 305 FundingPayment: ptr.From(num.DecimalFromFloat(3)), 306 FundingRate: ptr.From(num.DecimalFromFloat(3)), 307 ExternalTwap: ptr.From(num.DecimalFromFloat(3)), 308 InternalTwap: ptr.From(num.DecimalFromFloat(3)), 309 VegaTime: stores.blocks[5].VegaTime, 310 TxHash: generateTxHash(), 311 }, 312 { 313 MarketID: stores.markets[0].ID, 314 FundingPeriodSeq: 4, 315 StartTime: stores.blocks[7].VegaTime, 316 EndTime: nil, 317 FundingPayment: ptr.From(num.DecimalFromFloat(5)), 318 FundingRate: ptr.From(num.DecimalFromFloat(5)), 319 ExternalTwap: ptr.From(num.DecimalFromFloat(5)), 320 InternalTwap: ptr.From(num.DecimalFromFloat(5)), 321 VegaTime: stores.blocks[7].VegaTime, 322 TxHash: generateTxHash(), 323 }, 324 { 325 MarketID: stores.markets[0].ID, 326 FundingPeriodSeq: 5, 327 StartTime: stores.blocks[9].VegaTime, 328 EndTime: nil, 329 FundingPayment: ptr.From(num.DecimalFromFloat(5)), 330 FundingRate: ptr.From(num.DecimalFromFloat(5)), 331 ExternalTwap: ptr.From(num.DecimalFromFloat(5)), 332 InternalTwap: ptr.From(num.DecimalFromFloat(5)), 333 VegaTime: stores.blocks[9].VegaTime, 334 TxHash: generateTxHash(), 335 }, 336 } 337 338 stores.dataPoints = []entities.FundingPeriodDataPoint{ 339 { 340 MarketID: stores.markets[0].ID, 341 FundingPeriodSeq: 1, 342 DataPointType: entities.FundingPeriodDataPointSourceExternal, 343 Price: num.DecimalFromFloat(1.0), 344 Twap: num.DecimalFromFloat(1.0), 345 Timestamp: stores.blocks[2].VegaTime, 346 VegaTime: stores.blocks[2].VegaTime, 347 TxHash: generateTxHash(), 348 }, 349 { 350 MarketID: stores.markets[0].ID, 351 FundingPeriodSeq: 1, 352 DataPointType: entities.FundingPeriodDataPointSourceExternal, 353 Price: num.DecimalFromFloat(1.0), 354 Twap: num.DecimalFromFloat(1.0), 355 Timestamp: stores.blocks[3].VegaTime, 356 VegaTime: stores.blocks[3].VegaTime, 357 TxHash: generateTxHash(), 358 }, 359 { 360 MarketID: stores.markets[0].ID, 361 FundingPeriodSeq: 1, 362 DataPointType: entities.FundingPeriodDataPointSourceInternal, 363 Price: num.DecimalFromFloat(1.0), 364 Twap: num.DecimalFromFloat(1.0), 365 Timestamp: stores.blocks[4].VegaTime, 366 VegaTime: stores.blocks[4].VegaTime, 367 TxHash: generateTxHash(), 368 }, 369 { 370 MarketID: stores.markets[0].ID, 371 FundingPeriodSeq: 2, 372 DataPointType: entities.FundingPeriodDataPointSourceExternal, 373 Price: num.DecimalFromFloat(1.0), 374 Twap: num.DecimalFromFloat(1.0), 375 Timestamp: stores.blocks[5].VegaTime, 376 VegaTime: stores.blocks[5].VegaTime, 377 TxHash: generateTxHash(), 378 }, 379 { 380 MarketID: stores.markets[0].ID, 381 FundingPeriodSeq: 3, 382 DataPointType: entities.FundingPeriodDataPointSourceExternal, 383 Price: num.DecimalFromFloat(1.0), 384 Twap: num.DecimalFromFloat(1.0), 385 Timestamp: stores.blocks[6].VegaTime, 386 VegaTime: stores.blocks[6].VegaTime, 387 TxHash: generateTxHash(), 388 }, 389 } 390 391 for _, period := range stores.periods { 392 err := stores.fp.AddFundingPeriod(ctx, &period) 393 require.NoError(t, err) 394 } 395 396 for _, dataPoint := range stores.dataPoints { 397 err := stores.fp.AddDataPoint(ctx, &dataPoint) 398 require.NoError(t, err) 399 } 400 401 // Let's make sure the data is ordered correctly just in case we add data points, but not in order 402 sort.Slice(stores.periods, func(i, j int) bool { 403 return stores.periods[i].VegaTime.After(stores.periods[j].VegaTime) || 404 stores.periods[i].MarketID < stores.periods[j].MarketID || 405 stores.periods[i].FundingPeriodSeq < stores.periods[j].FundingPeriodSeq 406 }) 407 408 sort.Slice(stores.dataPoints, func(i, j int) bool { 409 return stores.dataPoints[i].VegaTime.After(stores.dataPoints[j].VegaTime) || 410 stores.dataPoints[i].MarketID < stores.dataPoints[j].MarketID || 411 stores.dataPoints[i].FundingPeriodSeq < stores.dataPoints[j].FundingPeriodSeq || 412 stores.dataPoints[i].DataPointType < stores.dataPoints[j].DataPointType 413 }) 414 } 415 416 func TestFundingPeriodListFundingPeriods(t *testing.T) { 417 ctx := tempTransaction(t) 418 419 stores := setupFundingPeriodTests(ctx, t) 420 421 addFundingPeriodsAndDataPoints(t, ctx, stores) 422 423 t.Run("should return the first page of funding periods when no sequence number is given", func(t *testing.T) { 424 testListFundingPeriodsMarketNoSequence(t, ctx, stores) 425 }) 426 t.Run("should return the specific funding period when the market and sequence number is given", func(t *testing.T) { 427 testListFundingPeriodForMarketSequence(t, ctx, stores) 428 }) 429 t.Run("should return the page of funding periods when pagination control is provided", func(t *testing.T) { 430 testListFundingPeriodPagination(t, ctx, stores) 431 }) 432 } 433 434 func testListFundingPeriodsMarketNoSequence(t *testing.T, ctx context.Context, stores *fundingPeriodTestStores) { 435 t.Helper() 436 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 437 require.NoError(t, err) 438 439 dateRange := entities.DateRange{} 440 441 got, pageInfo, err := stores.fp.ListFundingPeriods(ctx, stores.markets[0].ID, dateRange, pagination) 442 require.NoError(t, err) 443 want := stores.periods 444 445 assert.Equal(t, want, got) 446 assert.Equal(t, entities.PageInfo{ 447 HasNextPage: false, 448 HasPreviousPage: false, 449 StartCursor: want[0].Cursor().Encode(), 450 EndCursor: want[len(want)-1].Cursor().Encode(), 451 }, pageInfo) 452 } 453 454 func testListFundingPeriodForMarketSequence(t *testing.T, ctx context.Context, stores *fundingPeriodTestStores) { 455 t.Helper() 456 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 457 require.NoError(t, err) 458 459 dateRange := entities.DateRange{ 460 End: ptr.From(stores.periods[3].StartTime), 461 } 462 got, pageInfo, err := stores.fp.ListFundingPeriods(ctx, stores.markets[0].ID, dateRange, pagination) 463 require.NoError(t, err) 464 want := stores.periods[4:] 465 466 assert.Equal(t, want, got) 467 assert.Equal(t, entities.PageInfo{ 468 HasNextPage: false, 469 HasPreviousPage: false, 470 StartCursor: want[0].Cursor().Encode(), 471 EndCursor: want[len(want)-1].Cursor().Encode(), 472 }, pageInfo) 473 } 474 475 func testListFundingPeriodPagination(t *testing.T, ctx context.Context, stores *fundingPeriodTestStores) { 476 t.Helper() 477 var first, last int32 478 var after, before string 479 480 t.Run("should return the first page when first is specified with no cursor", func(t *testing.T) { 481 first = 2 482 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, true) 483 require.NoError(t, err) 484 485 dateRange := entities.DateRange{} 486 got, pageInfo, err := stores.fp.ListFundingPeriods(ctx, stores.markets[0].ID, dateRange, pagination) 487 require.NoError(t, err) 488 want := stores.periods[:2] 489 490 assert.Equal(t, want, got) 491 assert.Equal(t, entities.PageInfo{ 492 HasNextPage: true, 493 HasPreviousPage: false, 494 StartCursor: want[0].Cursor().Encode(), 495 EndCursor: want[len(want)-1].Cursor().Encode(), 496 }, pageInfo) 497 }) 498 499 t.Run("should return the first page after the cursor when first is specified with a cursor", func(t *testing.T) { 500 first = 2 501 after = stores.periods[1].Cursor().Encode() 502 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, true) 503 require.NoError(t, err) 504 dateRange := entities.DateRange{} 505 got, pageInfo, err := stores.fp.ListFundingPeriods(ctx, stores.markets[0].ID, dateRange, pagination) 506 require.NoError(t, err) 507 want := stores.periods[2:4] 508 assert.Equal(t, want, got) 509 assert.Equal(t, entities.PageInfo{ 510 HasNextPage: true, 511 HasPreviousPage: true, 512 StartCursor: want[0].Cursor().Encode(), 513 EndCursor: want[len(want)-1].Cursor().Encode(), 514 }, pageInfo) 515 }) 516 517 t.Run("should return the last page when last is specified with no cursor", func(t *testing.T) { 518 last = 2 519 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, true) 520 require.NoError(t, err) 521 dateRange := entities.DateRange{} 522 got, pageInfo, err := stores.fp.ListFundingPeriods(ctx, stores.markets[0].ID, dateRange, pagination) 523 require.NoError(t, err) 524 want := stores.periods[len(stores.periods)-2:] 525 assert.Equal(t, want, got) 526 assert.Equal(t, entities.PageInfo{ 527 HasNextPage: false, 528 HasPreviousPage: true, 529 StartCursor: want[0].Cursor().Encode(), 530 EndCursor: want[len(want)-1].Cursor().Encode(), 531 }, pageInfo) 532 }) 533 534 t.Run("should return the last page before the cursor when last is specified with a cursor", func(t *testing.T) { 535 last = 2 536 before = stores.periods[3].Cursor().Encode() 537 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, true) 538 require.NoError(t, err) 539 dateRange := entities.DateRange{} 540 got, pageInfo, err := stores.fp.ListFundingPeriods(ctx, stores.markets[0].ID, dateRange, pagination) 541 require.NoError(t, err) 542 want := stores.periods[1:3] 543 assert.Equal(t, want, got) 544 assert.Equal(t, entities.PageInfo{ 545 HasNextPage: true, 546 HasPreviousPage: true, 547 StartCursor: want[0].Cursor().Encode(), 548 EndCursor: want[len(want)-1].Cursor().Encode(), 549 }, pageInfo) 550 }) 551 } 552 553 func TestFundingPeriod_ListDataPoints(t *testing.T) { 554 ctx := tempTransaction(t) 555 556 stores := setupFundingPeriodTests(ctx, t) 557 558 addFundingPeriodsAndDataPoints(t, ctx, stores) 559 560 t.Run("Should return the first page of data points for all sequences if none are specified", func(t *testing.T) { 561 testListDataPointsAllSequences(t, ctx, stores) 562 }) 563 t.Run("Should return the first page of data points for specified sequences only", func(t *testing.T) { 564 testListDataPointsSpecifiedSequences(t, ctx, stores) 565 }) 566 t.Run("should return the first page of data points when no source is specified", func(t *testing.T) { 567 testListDataPointsNoSource(t, ctx, stores) 568 }) 569 t.Run("should return the first page of data points for the specified source", func(t *testing.T) { 570 testListDataPointsForSource(t, ctx, stores) 571 }) 572 t.Run("should return the page of data points when pagination control is provided", func(t *testing.T) { 573 testListDataPointsPagination(t, ctx, stores) 574 }) 575 } 576 577 func testListDataPointsAllSequences(t *testing.T, ctx context.Context, stores *fundingPeriodTestStores) { 578 t.Helper() 579 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 580 require.NoError(t, err) 581 dateRange := entities.DateRange{} 582 got, pageInfo, err := stores.fp.ListFundingPeriodDataPoints(ctx, stores.markets[0].ID, dateRange, nil, nil, pagination) 583 require.NoError(t, err) 584 want := stores.dataPoints[:] 585 assert.Equal(t, want, got) 586 assert.Equal(t, entities.PageInfo{ 587 HasNextPage: false, 588 HasPreviousPage: false, 589 StartCursor: want[0].Cursor().Encode(), 590 EndCursor: want[len(want)-1].Cursor().Encode(), 591 }, pageInfo) 592 } 593 594 func testListDataPointsSpecifiedSequences(t *testing.T, ctx context.Context, stores *fundingPeriodTestStores) { 595 t.Helper() 596 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 597 require.NoError(t, err) 598 dateRange := entities.DateRange{} 599 got, pageInfo, err := stores.fp.ListFundingPeriodDataPoints(ctx, stores.markets[0].ID, dateRange, nil, nil, pagination) 600 require.NoError(t, err) 601 want := stores.dataPoints[:] 602 assert.Equal(t, want, got) 603 assert.Equal(t, entities.PageInfo{ 604 HasNextPage: false, 605 HasPreviousPage: false, 606 StartCursor: want[0].Cursor().Encode(), 607 EndCursor: want[len(want)-1].Cursor().Encode(), 608 }, pageInfo) 609 } 610 611 func testListDataPointsNoSource(t *testing.T, ctx context.Context, stores *fundingPeriodTestStores) { 612 t.Helper() 613 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 614 require.NoError(t, err) 615 want := stores.dataPoints[2:] 616 wantPageInfo := entities.PageInfo{ 617 HasNextPage: false, 618 HasPreviousPage: false, 619 StartCursor: want[0].Cursor().Encode(), 620 EndCursor: want[len(want)-1].Cursor().Encode(), 621 } 622 // Seq 1 623 dateRange := entities.DateRange{ 624 Start: ptr.From(stores.dataPoints[4].Timestamp), 625 End: ptr.From(stores.dataPoints[1].Timestamp), 626 } 627 testFundingPeriodsListDataPoints(t, ctx, stores.fp, stores.markets[0].ID, dateRange, nil, nil, pagination, want, wantPageInfo) 628 want = stores.dataPoints[1:] 629 wantPageInfo = entities.PageInfo{ 630 HasNextPage: false, 631 HasPreviousPage: false, 632 StartCursor: want[0].Cursor().Encode(), 633 EndCursor: want[len(want)-1].Cursor().Encode(), 634 } 635 // Seq 1 and 2 636 dateRange = entities.DateRange{ 637 Start: ptr.From(stores.dataPoints[4].Timestamp), 638 End: ptr.From(stores.dataPoints[0].Timestamp), 639 } 640 testFundingPeriodsListDataPoints(t, ctx, stores.fp, stores.markets[0].ID, dateRange, nil, nil, pagination, want, wantPageInfo) 641 } 642 643 func testFundingPeriodsListDataPoints(t *testing.T, ctx context.Context, fp *sqlstore.FundingPeriods, marketID entities.MarketID, dateRange entities.DateRange, 644 source *entities.FundingPeriodDataPointSource, seq *uint64, pagination entities.CursorPagination, want []entities.FundingPeriodDataPoint, 645 wantPageInfo entities.PageInfo, 646 ) { 647 t.Helper() 648 got, pageInfo, err := fp.ListFundingPeriodDataPoints(ctx, marketID, dateRange, source, seq, pagination) 649 require.NoError(t, err) 650 assert.Equal(t, want, got) 651 assert.Equal(t, wantPageInfo, pageInfo) 652 } 653 654 func testListDataPointsForSource(t *testing.T, ctx context.Context, stores *fundingPeriodTestStores) { 655 t.Helper() 656 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 657 require.NoError(t, err) 658 659 t.Run("should filter by just source", func(t *testing.T) { 660 want := []entities.FundingPeriodDataPoint{} 661 want = append(want, stores.dataPoints[:2]...) 662 want = append(want, stores.dataPoints[3:]...) 663 wantPageInfo := entities.PageInfo{ 664 HasNextPage: false, 665 HasPreviousPage: false, 666 StartCursor: want[0].Cursor().Encode(), 667 EndCursor: want[len(want)-1].Cursor().Encode(), 668 } 669 dateRange := entities.DateRange{} 670 testFundingPeriodsListDataPoints(t, ctx, stores.fp, stores.markets[0].ID, dateRange, 671 ptr.From(entities.FundingPeriodDataPointSourceExternal), nil, pagination, want, wantPageInfo) 672 }) 673 674 t.Run("should filter by date range and source", func(t *testing.T) { 675 want := []entities.FundingPeriodDataPoint{} 676 want = append(want, stores.dataPoints[1]) 677 want = append(want, stores.dataPoints[3:]...) 678 wantPageInfo := entities.PageInfo{ 679 HasNextPage: false, 680 HasPreviousPage: false, 681 StartCursor: want[0].Cursor().Encode(), 682 EndCursor: want[len(want)-1].Cursor().Encode(), 683 } 684 // sequence 1 and 2 685 dateRange := entities.DateRange{ 686 Start: ptr.From(stores.dataPoints[4].Timestamp), 687 End: ptr.From(stores.dataPoints[0].Timestamp), 688 } 689 testFundingPeriodsListDataPoints(t, ctx, stores.fp, stores.markets[0].ID, dateRange, 690 ptr.From(entities.FundingPeriodDataPointSourceExternal), nil, pagination, want, wantPageInfo) 691 }) 692 t.Run("should filter by seq", func(t *testing.T) { 693 want := []entities.FundingPeriodDataPoint{} 694 want = append(want, stores.dataPoints[1]) 695 wantPageInfo := entities.PageInfo{ 696 HasNextPage: false, 697 HasPreviousPage: false, 698 StartCursor: want[0].Cursor().Encode(), 699 EndCursor: want[len(want)-1].Cursor().Encode(), 700 } 701 dateRange := entities.DateRange{} 702 // sequence 2 only, which is one point 703 testFundingPeriodsListDataPoints(t, ctx, stores.fp, stores.markets[0].ID, dateRange, 704 nil, ptr.From(uint64(2)), pagination, want, wantPageInfo) 705 }) 706 } 707 708 func testListDataPointsPagination(t *testing.T, ctx context.Context, stores *fundingPeriodTestStores) { 709 t.Helper() 710 var first, last int32 711 var after, before string 712 713 t.Run("should return the first page when first is specified with no cursor", func(t *testing.T) { 714 first = 2 715 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, true) 716 require.NoError(t, err) 717 718 want := stores.dataPoints[:2] 719 wantPageInfo := entities.PageInfo{ 720 HasNextPage: true, 721 HasPreviousPage: false, 722 StartCursor: want[0].Cursor().Encode(), 723 EndCursor: want[len(want)-1].Cursor().Encode(), 724 } 725 dateRange := entities.DateRange{} 726 testFundingPeriodsListDataPoints(t, ctx, stores.fp, stores.markets[0].ID, dateRange, 727 nil, nil, pagination, want, wantPageInfo) 728 }) 729 730 t.Run("should return the first page after the cursor when first is specified with a cursor", func(t *testing.T) { 731 first = 2 732 after = stores.dataPoints[1].Cursor().Encode() 733 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, true) 734 require.NoError(t, err) 735 want := stores.dataPoints[2:4] 736 wantPageInfo := entities.PageInfo{ 737 HasNextPage: true, 738 HasPreviousPage: true, 739 StartCursor: want[0].Cursor().Encode(), 740 EndCursor: want[len(want)-1].Cursor().Encode(), 741 } 742 dateRange := entities.DateRange{} 743 testFundingPeriodsListDataPoints(t, ctx, stores.fp, stores.markets[0].ID, dateRange, 744 nil, nil, pagination, want, wantPageInfo) 745 }) 746 747 t.Run("should return the last page when last is specified with no cursor", func(t *testing.T) { 748 last = 2 749 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, true) 750 require.NoError(t, err) 751 want := stores.dataPoints[len(stores.dataPoints)-2:] 752 wantPageInfo := entities.PageInfo{ 753 HasNextPage: false, 754 HasPreviousPage: true, 755 StartCursor: want[0].Cursor().Encode(), 756 EndCursor: want[len(want)-1].Cursor().Encode(), 757 } 758 dateRange := entities.DateRange{} 759 testFundingPeriodsListDataPoints(t, ctx, stores.fp, stores.markets[0].ID, dateRange, 760 nil, nil, pagination, want, wantPageInfo) 761 }) 762 763 t.Run("should return the last page before the cursor when last is specified with a cursor", func(t *testing.T) { 764 last = 2 765 before = stores.dataPoints[3].Cursor().Encode() 766 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, true) 767 require.NoError(t, err) 768 want := stores.dataPoints[1:3] 769 wantPageInfo := entities.PageInfo{ 770 HasNextPage: true, 771 HasPreviousPage: true, 772 StartCursor: want[0].Cursor().Encode(), 773 EndCursor: want[len(want)-1].Cursor().Encode(), 774 } 775 dateRange := entities.DateRange{} 776 testFundingPeriodsListDataPoints(t, ctx, stores.fp, stores.markets[0].ID, dateRange, 777 nil, nil, pagination, want, wantPageInfo) 778 }) 779 } 780 781 func TestFundingPeriodDataPointSource(t *testing.T) { 782 var fundingPeriodDataPointSource eventspb.FundingPeriodDataPoint_Source 783 sources := getEnums(t, fundingPeriodDataPointSource) 784 assert.Equal(t, 3, len(sources)) 785 for s, source := range sources { 786 t.Run(source, func(t *testing.T) { 787 ctx := tempTransaction(t) 788 789 stores := setupFundingPeriodTests(ctx, t) 790 dp := entities.FundingPeriodDataPoint{ 791 MarketID: stores.markets[0].ID, 792 FundingPeriodSeq: 1, 793 DataPointType: entities.FundingPeriodDataPointSource(s), 794 Price: num.DecimalFromFloat(1.0), 795 Twap: num.DecimalFromFloat(1.0), 796 Timestamp: stores.blocks[2].VegaTime, 797 VegaTime: stores.blocks[2].VegaTime, 798 TxHash: generateTxHash(), 799 } 800 require.NoError(t, stores.fp.AddDataPoint(ctx, &dp)) 801 got := entities.FundingPeriodDataPoint{} 802 require.NoError(t, pgxscan.Get(ctx, stores.fp, &got, 803 `select * from funding_period_data_points where market_id = $1 and funding_period_seq = $2 and data_point_type = $3`, 804 dp.MarketID, dp.FundingPeriodSeq, dp.DataPointType), 805 ) 806 assert.Equal(t, dp, got) 807 }) 808 } 809 }