code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/candles_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 "strconv" 21 "strings" 22 "testing" 23 "time" 24 25 "code.vegaprotocol.io/vega/datanode/candlesv2" 26 "code.vegaprotocol.io/vega/datanode/entities" 27 "code.vegaprotocol.io/vega/datanode/sqlstore" 28 types "code.vegaprotocol.io/vega/protos/vega" 29 30 "github.com/shopspring/decimal" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 ) 34 35 const ( 36 StartTime = 1649116800 37 totalBlocks = 1000 38 tradesPerBlock = 5 39 startPrice = 1 40 size = 10 41 priceIncrement = 1 42 blockIntervalSeconds = 10 43 blockIntervalDur = time.Duration(blockIntervalSeconds) * time.Second 44 ) 45 46 func TestGetExistingCandles(t *testing.T) { 47 ctx := tempTransaction(t) 48 49 candleStore := sqlstore.NewCandles(ctx, connectionSource, candlesv2.NewDefaultConfig().CandleStore) 50 51 candles, err := candleStore.GetCandlesForMarket(ctx, testMarket) 52 if err != nil { 53 t.Fatalf("failed to get candles for market:%s", err) 54 } 55 56 defaultCandles := "block,1 minute,5 minutes,15 minutes,30 minutes,1 hour,4 hours,6 hours,8 hours,12 hours,1 day,7 days" 57 intervals := strings.Split(defaultCandles, ",") 58 assert.Equal(t, len(intervals), len(candles)) 59 60 for _, interval := range intervals { 61 candleID := candles[interval] 62 exists, _ := candleStore.CandleExists(ctx, candleID) 63 assert.True(t, exists) 64 } 65 } 66 67 func TestCandlesPagination(t *testing.T) { 68 ctx := tempTransaction(t) 69 70 candleStore := sqlstore.NewCandles(ctx, connectionSource, candlesv2.NewDefaultConfig().CandleStore) 71 72 tradeStore := sqlstore.NewTrades(connectionSource) 73 74 startTime := time.Unix(StartTime, 0) 75 insertCandlesTestData(t, ctx, tradeStore, startTime, totalBlocks, tradesPerBlock, startPrice, priceIncrement, size, blockIntervalDur) 76 77 _, candleID, _ := candleStore.GetCandleIDForIntervalAndMarket(ctx, "1 Minute", testMarket) 78 first := int32(10) 79 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false) 80 require.NoError(t, err) 81 82 candles, _, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, nil, 83 nil, pagination) 84 if err != nil { 85 t.Fatalf("failed to get candles with pagination:%s", err) 86 } 87 88 assert.Equal(t, 10, len(candles)) 89 lastCandle := candles[9] 90 91 first = int32(5) 92 after := candles[8].Cursor().Encode() 93 94 pagination, _ = entities.NewCursorPagination(&first, &after, nil, nil, false) 95 96 candles, _, err = candleStore.GetCandleDataForTimeSpan(ctx, candleID, nil, 97 nil, pagination) 98 if err != nil { 99 t.Fatalf("failed to get candles with pagination:%s", err) 100 } 101 102 assert.Equal(t, 5, len(candles)) 103 assert.Equal(t, lastCandle, candles[0]) 104 } 105 106 func TestNotional(t *testing.T) { 107 ctx := tempTransaction(t) 108 109 candleStore := sqlstore.NewCandles(ctx, connectionSource, candlesv2.NewDefaultConfig().CandleStore) 110 tradeStore := sqlstore.NewTrades(connectionSource) 111 bs := sqlstore.NewBlocks(connectionSource) 112 113 startTime := time.Unix(StartTime, 0) 114 block := addTestBlockForTime(t, ctx, bs, startTime) 115 116 // Total notional here will be 1*10 + 2*15 = 40 117 insertTestTrade(t, ctx, tradeStore, 1, 10, block, 0) 118 insertTestTrade(t, ctx, tradeStore, 2, 15, block, 3) 119 120 nextTime := time.Unix(StartTime, 0).Add(10 * time.Minute) 121 block = addTestBlockForTime(t, ctx, bs, nextTime) 122 // Total notional here will be 3*20 + 4*25 = 160 123 insertTestTrade(t, ctx, tradeStore, 3, 20, block, 0) 124 insertTestTrade(t, ctx, tradeStore, 4, 25, block, 5) 125 126 _, candleID, err := candleStore.GetCandleIDForIntervalAndMarket(ctx, "1 Minute", testMarket) 127 if err != nil { 128 t.Fatalf("getting existing candleDescriptor id:%s", err) 129 } 130 131 pagination, _ := entities.NewCursorPagination(nil, nil, nil, nil, false) 132 candles, _, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, 133 nil, pagination) 134 if err != nil { 135 t.Fatalf("failed to get candles:%s", err) 136 } 137 138 assert.Equal(t, uint64(40), candles[0].Notional) 139 assert.Equal(t, uint64(160), candles[len(candles)-1].Notional) 140 } 141 142 func TestCandlesGetForEmptyInterval(t *testing.T) { 143 ctx := tempTransaction(t) 144 145 candleStore := sqlstore.NewCandles(ctx, connectionSource, candlesv2.NewDefaultConfig().CandleStore) 146 tradeStore := sqlstore.NewTrades(connectionSource) 147 bs := sqlstore.NewBlocks(connectionSource) 148 149 startTime := time.Unix(StartTime, 0) 150 block := addTestBlockForTime(t, ctx, bs, startTime) 151 152 insertTestTrade(t, ctx, tradeStore, 1, 10, block, 0) 153 insertTestTrade(t, ctx, tradeStore, 2, 10, block, 3) 154 155 nextTime := time.Unix(StartTime, 0).Add(10 * time.Minute) 156 block = addTestBlockForTime(t, ctx, bs, nextTime) 157 insertTestTrade(t, ctx, tradeStore, 3, 20, block, 0) 158 insertTestTrade(t, ctx, tradeStore, 4, 20, block, 5) 159 160 _, candleID, err := candleStore.GetCandleIDForIntervalAndMarket(ctx, "1 Minute", testMarket) 161 if err != nil { 162 t.Fatalf("getting existing candleDescriptor id:%s", err) 163 } 164 165 pagination, _ := entities.NewCursorPagination(nil, nil, nil, nil, false) 166 candles, _, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, 167 nil, pagination) 168 if err != nil { 169 t.Fatalf("failed to get candles:%s", err) 170 } 171 172 assert.Equal(t, 11, len(candles)) // 11 now because we are filling in the gaps 173 174 firstCandle := createCandle(startTime, 175 startTime.Add(3*time.Microsecond), 1, 2, 2, 1, 20, 30) 176 assert.Equal(t, firstCandle, candles[0]) 177 // Fill gaps should carry over the last candle's prices 178 gapPeriodStart := firstCandle.PeriodStart.Add(1 * time.Minute) 179 180 assert.Equal(t, gapPeriodStart, candles[1].PeriodStart) 181 assert.True(t, decimal.Zero.Equal(candles[1].Open)) 182 assert.True(t, decimal.Zero.Equal(candles[1].High)) 183 assert.True(t, decimal.Zero.Equal(candles[1].Low)) 184 assert.True(t, decimal.Zero.Equal(candles[1].Close)) 185 assert.Equal(t, uint64(0), candles[1].Volume) 186 assert.Equal(t, uint64(0), candles[1].Notional) 187 assert.True(t, firstCandle.LastUpdateInPeriod.Equal(candles[1].LastUpdateInPeriod)) 188 189 secondCandle := createCandle(startTime.Add(10*time.Minute), 190 startTime.Add(10*time.Minute).Add(5*time.Microsecond), 3, 4, 4, 3, 40, 140) 191 assert.Equal(t, secondCandle, candles[len(candles)-1]) 192 } 193 194 func TestCandlesGetLatest(t *testing.T) { 195 ctx := tempTransaction(t) 196 197 candleStore := sqlstore.NewCandles(ctx, connectionSource, candlesv2.NewDefaultConfig().CandleStore) 198 tradeStore := sqlstore.NewTrades(connectionSource) 199 200 startTime := time.Unix(StartTime, 0) 201 insertCandlesTestData(t, ctx, tradeStore, startTime, 90, 3, startPrice, priceIncrement, size, 202 1*time.Second) 203 204 last := int32(1) 205 pagination, _ := entities.NewCursorPagination(nil, nil, &last, nil, false) 206 _, candleID, _ := candleStore.GetCandleIDForIntervalAndMarket(ctx, "1 Minute", testMarket) 207 candles, _, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, 208 nil, pagination) 209 if err != nil { 210 t.Fatalf("failed to get candles:%s", err) 211 } 212 213 assert.Equal(t, 1, len(candles)) 214 215 lastCandle := createCandle(startTime.Add(60*time.Second), 216 startTime.Add(89*time.Second).Add(2*time.Microsecond), 181, 270, 270, 181, 900, 202950) 217 assert.Equal(t, lastCandle, candles[0]) 218 } 219 220 func TestCandlesGetForDifferentIntervalAndTimeBounds(t *testing.T) { 221 ctx := tempTransaction(t) 222 223 candleStore := sqlstore.NewCandles(ctx, connectionSource, candlesv2.NewDefaultConfig().CandleStore) 224 tradeStore := sqlstore.NewTrades(connectionSource) 225 226 startTime := time.Unix(StartTime, 0) 227 insertCandlesTestData(t, ctx, tradeStore, startTime, totalBlocks, tradesPerBlock, startPrice, priceIncrement, size, blockIntervalDur) 228 229 testInterval(t, ctx, startTime, nil, nil, candleStore, "1 Minute", 60) 230 testInterval(t, ctx, startTime, nil, nil, candleStore, "5 Minutes", 300) 231 testInterval(t, ctx, startTime, nil, nil, candleStore, "15 Minutes", 900) 232 testInterval(t, ctx, startTime, nil, nil, candleStore, "1 hour", 3600) 233 234 from := startTime.Add(5 * time.Minute) 235 to := startTime.Add(35 * time.Minute) 236 237 testInterval(t, ctx, startTime, &from, &to, candleStore, "1 Minute", 60) 238 testInterval(t, ctx, startTime, &from, &to, candleStore, "5 Minutes", 300) 239 240 testInterval(t, ctx, startTime, nil, &to, candleStore, "1 Minute", 60) 241 testInterval(t, ctx, startTime, nil, &to, candleStore, "5 Minutes", 300) 242 243 testInterval(t, ctx, startTime, &from, nil, candleStore, "1 Minute", 60) 244 testInterval(t, ctx, startTime, &from, nil, candleStore, "5 Minutes", 300) 245 } 246 247 func testInterval(t *testing.T, ctx context.Context, tradeDataStartTime time.Time, fromTime *time.Time, toTime *time.Time, candleStore *sqlstore.Candles, interval string, 248 intervalSeconds int, 249 ) { 250 t.Helper() 251 intervalDur := time.Duration(intervalSeconds) * time.Second 252 253 pagination, _ := entities.NewCursorPagination(nil, nil, nil, nil, false) 254 _, candleID, _ := candleStore.GetCandleIDForIntervalAndMarket(ctx, interval, testMarket) 255 candles, _, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, fromTime, 256 toTime, pagination) 257 if err != nil { 258 t.Fatalf("failed to get candles:%s", err) 259 } 260 261 tradeDataTimeSpanSeconds := totalBlocks * blockIntervalSeconds 262 tradeDataEndTime := tradeDataStartTime.Add(time.Duration(tradeDataTimeSpanSeconds) * time.Second) 263 264 var candlesStartTime time.Time 265 if fromTime != nil && fromTime.After(tradeDataStartTime) { 266 candlesStartTime = *fromTime 267 } else { 268 candlesStartTime = tradeDataStartTime 269 } 270 271 var candlesEndTime time.Time 272 if toTime != nil && toTime.Before(tradeDataEndTime) { 273 candlesEndTime = *toTime 274 } else { 275 candlesEndTime = tradeDataEndTime 276 } 277 278 candleSpan := candlesEndTime.Sub(candlesStartTime) 279 280 expectedNumCandles := int(candleSpan.Seconds() / float64(intervalSeconds)) 281 282 if toTime == nil { 283 expectedNumCandles = expectedNumCandles + 1 284 } 285 286 assert.Equal(t, expectedNumCandles, len(candles)) 287 288 blocksPerInterval := intervalSeconds / blockIntervalSeconds 289 290 skippedTrades := int(candlesStartTime.Sub(tradeDataStartTime).Seconds()/blockIntervalSeconds) * tradesPerBlock 291 292 for idx := 0; idx < expectedNumCandles-1; idx++ { 293 periodStart := candlesStartTime.Add(time.Duration(idx) * intervalDur) 294 tradesAtOpen := skippedTrades + idx*tradesPerBlock*blocksPerInterval 295 tradesAtClose := skippedTrades + (idx+1)*tradesPerBlock*blocksPerInterval 296 expectedVolume := tradesPerBlock * blocksPerInterval * size 297 candle := candles[idx] 298 expectedCandle := createCandle(periodStart, 299 periodStart.Add(time.Duration(tradesPerBlock-1)*time.Microsecond).Add(intervalDur).Add(-1*blockIntervalDur), 300 startPrice+tradesAtOpen, tradesAtClose, tradesAtClose, startPrice+tradesAtOpen, 301 expectedVolume, candle.Notional) 302 assert.Equal(t, expectedCandle, candle) 303 } 304 } 305 306 func createCandle(periodStart time.Time, lastUpdate time.Time, open int, close int, high int, low int, volume int, notional uint64) entities.Candle { 307 return entities.Candle{ 308 PeriodStart: periodStart, 309 LastUpdateInPeriod: lastUpdate, 310 Open: decimal.NewFromInt(int64(open)), 311 Close: decimal.NewFromInt(int64(close)), 312 High: decimal.NewFromInt(int64(high)), 313 Low: decimal.NewFromInt(int64(low)), 314 Volume: uint64(volume), 315 Notional: notional, 316 } 317 } 318 319 //nolint:unparam 320 func insertCandlesTestData(t *testing.T, ctx context.Context, tradeStore *sqlstore.Trades, startTime time.Time, numBlocks int, 321 tradePerBlock int, startPrice int, priceIncrement int, size int, blockIntervalDur time.Duration, 322 ) { 323 t.Helper() 324 bs := sqlstore.NewBlocks(connectionSource) 325 326 var blocks []entities.Block 327 for i := 0; i < numBlocks; i++ { 328 blocks = append(blocks, addTestBlockForTime(t, ctx, bs, startTime.Add(time.Duration(i)*blockIntervalDur))) 329 } 330 331 for _, block := range blocks { 332 for seqNum := 0; seqNum < tradePerBlock; seqNum++ { 333 trade := createTestTrade(t, startPrice, size, block, seqNum) 334 err := tradeStore.Add(trade) 335 if err != nil { 336 t.Fatalf("failed to add trade to store:%s", err) 337 } 338 startPrice = startPrice + priceIncrement 339 } 340 } 341 342 _, err := tradeStore.Flush(ctx) 343 assert.NoError(t, err) 344 } 345 346 func insertTestTrade(t *testing.T, ctx context.Context, tradeStore *sqlstore.Trades, price int, size int, block entities.Block, seqNum int) { 347 t.Helper() 348 trade := createTestTrade(t, price, size, block, seqNum) 349 insertTrade(t, ctx, tradeStore, trade) 350 } 351 352 func insertTrade(t *testing.T, ctx context.Context, tradeStore *sqlstore.Trades, trade *entities.Trade) *entities.Trade { 353 t.Helper() 354 err := tradeStore.Add(trade) 355 tradeStore.Flush(ctx) 356 if err != nil { 357 t.Fatalf("failed to add trade to store:%s", err) 358 } 359 360 return trade 361 } 362 363 func createTestTrade(t *testing.T, price int, size int, block entities.Block, seqNum int) *entities.Trade { 364 t.Helper() 365 proto := &types.Trade{ 366 Type: types.Trade_TYPE_DEFAULT, 367 Id: GenerateID(), 368 Price: strconv.Itoa(price), 369 Size: uint64(size), 370 MarketId: testMarket, 371 Buyer: GenerateID(), 372 Seller: GenerateID(), 373 Aggressor: types.Side_SIDE_SELL, 374 BuyOrder: GenerateID(), 375 SellOrder: GenerateID(), 376 } 377 378 trade, err := entities.TradeFromProto(proto, generateTxHash(), block.VegaTime, uint64(seqNum)) 379 if err != nil { 380 t.Fatalf("failed to create trade from proto:%s", err) 381 } 382 return trade 383 } 384 385 func TestCandlesCursorPagination(t *testing.T) { 386 ctx := tempTransaction(t) 387 388 candleStore := sqlstore.NewCandles(ctx, connectionSource, candlesv2.NewDefaultConfig().CandleStore) 389 tradeStore := sqlstore.NewTrades(connectionSource) 390 391 startTime := time.Unix(StartTime, 0) 392 insertCandlesTestData(t, ctx, tradeStore, startTime, totalBlocks, tradesPerBlock, startPrice, priceIncrement, size, blockIntervalDur) 393 394 _, candleID, err := candleStore.GetCandleIDForIntervalAndMarket(ctx, "1 Minute", testMarket) 395 if err != nil { 396 t.Fatalf("getting existing candleDescriptor id:%s", err) 397 } 398 399 pagination, _ := entities.NewCursorPagination(nil, nil, nil, nil, false) 400 // retrieve all candles without pagination to use for test validation 401 allCandles, _, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, 402 nil, pagination) 403 if err != nil { 404 t.Fatalf("failed to get candles:%s", err) 405 } 406 407 require.Equal(t, 167, len(allCandles)) 408 409 t.Run("should return the first candles when first is provided with no after", func(t *testing.T) { 410 first := int32(10) 411 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false) 412 require.NoError(t, err) 413 candles, pageInfo, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, nil, pagination) 414 require.NoError(t, err) 415 require.Equal(t, 10, len(candles)) 416 assert.Equal(t, allCandles[0], candles[0]) 417 assert.Equal(t, allCandles[9], candles[9]) 418 assert.Equal(t, entities.PageInfo{ 419 HasNextPage: true, 420 HasPreviousPage: false, 421 StartCursor: allCandles[0].Cursor().Encode(), 422 EndCursor: allCandles[9].Cursor().Encode(), 423 }, pageInfo) 424 }) 425 426 t.Run("should return the first candles when first is provided with no after - newest first", func(t *testing.T) { 427 first := int32(10) 428 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, true) 429 require.NoError(t, err) 430 candles, pageInfo, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, nil, pagination) 431 require.NoError(t, err) 432 lastIndex := len(allCandles) - 1 433 require.Equal(t, 10, len(candles)) 434 assert.Equal(t, allCandles[lastIndex], candles[0]) 435 assert.Equal(t, allCandles[lastIndex-9], candles[9]) 436 assert.Equal(t, entities.PageInfo{ 437 HasNextPage: true, 438 HasPreviousPage: false, 439 StartCursor: allCandles[lastIndex].Cursor().Encode(), 440 EndCursor: allCandles[lastIndex-9].Cursor().Encode(), 441 }, pageInfo) 442 }) 443 444 t.Run("should return the last page of candles when last is provided with no before", func(t *testing.T) { 445 last := int32(10) 446 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, false) 447 require.NoError(t, err) 448 candles, pageInfo, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, nil, pagination) 449 require.NoError(t, err) 450 require.Equal(t, 10, len(candles)) 451 assert.Equal(t, allCandles[157], candles[0]) 452 assert.Equal(t, allCandles[166], candles[9]) 453 assert.Equal(t, entities.PageInfo{ 454 HasNextPage: false, 455 HasPreviousPage: true, 456 StartCursor: allCandles[157].Cursor().Encode(), 457 EndCursor: allCandles[166].Cursor().Encode(), 458 }, pageInfo) 459 }) 460 461 t.Run("should return the last page of candles when last is provided with no before - newest first", func(t *testing.T) { 462 last := int32(10) 463 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, true) 464 require.NoError(t, err) 465 candles, pageInfo, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, nil, pagination) 466 require.NoError(t, err) 467 require.Equal(t, 10, len(candles)) 468 assert.Equal(t, allCandles[9], candles[0]) 469 assert.Equal(t, allCandles[0], candles[9]) 470 assert.Equal(t, entities.PageInfo{ 471 HasNextPage: false, 472 HasPreviousPage: true, 473 StartCursor: allCandles[9].Cursor().Encode(), 474 EndCursor: allCandles[0].Cursor().Encode(), 475 }, pageInfo) 476 }) 477 478 t.Run("should return the requested page of candles when first and after are provided", func(t *testing.T) { 479 first := int32(10) 480 after := allCandles[99].Cursor().Encode() 481 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false) 482 require.NoError(t, err) 483 candles, pageInfo, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, nil, pagination) 484 require.NoError(t, err) 485 require.Equal(t, 10, len(candles)) 486 assert.Equal(t, allCandles[100], candles[0]) 487 assert.Equal(t, allCandles[109], candles[9]) 488 assert.Equal(t, entities.PageInfo{ 489 HasNextPage: true, 490 HasPreviousPage: true, 491 StartCursor: allCandles[100].Cursor().Encode(), 492 EndCursor: allCandles[109].Cursor().Encode(), 493 }, pageInfo) 494 }) 495 496 t.Run("should return the requested page of candles when first and after are provided - newest first", func(t *testing.T) { 497 first := int32(10) 498 after := allCandles[99].Cursor().Encode() 499 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, true) 500 require.NoError(t, err) 501 candles, pageInfo, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, nil, pagination) 502 require.NoError(t, err) 503 require.Equal(t, 10, len(candles)) 504 assert.Equal(t, allCandles[98], candles[0]) 505 assert.Equal(t, allCandles[89], candles[9]) 506 assert.Equal(t, entities.PageInfo{ 507 HasNextPage: true, 508 HasPreviousPage: true, 509 StartCursor: allCandles[98].Cursor().Encode(), 510 EndCursor: allCandles[89].Cursor().Encode(), 511 }, pageInfo) 512 }) 513 514 t.Run("Should return the requested page of candles when last and before are provided", func(t *testing.T) { 515 last := int32(10) 516 before := allCandles[100].Cursor().Encode() 517 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, false) 518 require.NoError(t, err) 519 candles, pageInfo, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, nil, pagination) 520 require.NoError(t, err) 521 require.Equal(t, 10, len(candles)) 522 assert.Equal(t, allCandles[90], candles[0]) 523 assert.Equal(t, allCandles[99], candles[9]) 524 assert.Equal(t, entities.PageInfo{ 525 HasNextPage: true, 526 HasPreviousPage: true, 527 StartCursor: allCandles[90].Cursor().Encode(), 528 EndCursor: allCandles[99].Cursor().Encode(), 529 }, pageInfo) 530 }) 531 532 t.Run("Should return the requested page of candles when last and before are provided - newest first", func(t *testing.T) { 533 last := int32(10) 534 before := allCandles[100].Cursor().Encode() 535 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, true) 536 require.NoError(t, err) 537 candles, pageInfo, err := candleStore.GetCandleDataForTimeSpan(ctx, candleID, &startTime, nil, pagination) 538 require.NoError(t, err) 539 require.Equal(t, 10, len(candles)) 540 assert.Equal(t, allCandles[110], candles[0]) 541 assert.Equal(t, allCandles[101], candles[9]) 542 assert.Equal(t, entities.PageInfo{ 543 HasNextPage: true, 544 HasPreviousPage: true, 545 StartCursor: allCandles[110].Cursor().Encode(), 546 EndCursor: allCandles[101].Cursor().Encode(), 547 }, pageInfo) 548 }) 549 } 550 551 func TestCandlesBlockInterval(t *testing.T) { 552 ctx := tempTransaction(t) 553 554 candleStore := sqlstore.NewCandles(ctx, connectionSource, candlesv2.NewDefaultConfig().CandleStore) 555 556 tradeStore := sqlstore.NewTrades(connectionSource) 557 558 startTime := time.Unix(StartTime, 0) 559 insertCandlesTestData(t, ctx, tradeStore, startTime, totalBlocks, tradesPerBlock, startPrice, priceIncrement, size, time.Minute) 560 561 candles, err := candleStore.GetCandlesForMarket(ctx, testMarket) 562 require.NoError(t, err) 563 first := int32(10) 564 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false) 565 require.NoError(t, err) 566 567 candleData, _, err := candleStore.GetCandleDataForTimeSpan(ctx, candles["block"], nil, 568 nil, pagination) 569 if err != nil { 570 t.Fatalf("failed to get candles with pagination:%s", err) 571 } 572 573 assert.Equal(t, 10, len(candleData)) 574 } 575 576 func TestCandlesFillBeforeFirstCandle(t *testing.T) { 577 ctx := tempTransaction(t) 578 579 candleStore := sqlstore.NewCandles(ctx, connectionSource, candlesv2.NewDefaultConfig().CandleStore) 580 581 candles, err := candleStore.GetCandlesForMarket(ctx, testMarket) 582 require.NoError(t, err) 583 first := int32(10) 584 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false) 585 require.NoError(t, err) 586 587 candleData, _, err := candleStore.GetCandleDataForTimeSpan(ctx, candles["block"], nil, 588 nil, pagination) 589 if err != nil { 590 t.Fatalf("failed to get candles with pagination:%s", err) 591 } 592 593 assert.Equal(t, 0, len(candleData)) 594 }