Files
gocryptotrader/exchange/websocket/buffer/buffer_test.go
cranktakular fd9aaf00a2 Coinbase: Update exchange implementation (#1480)
* Slight enhance of Coinbase tests

Continual enhance of Coinbase tests

The revamp continues

Oh jeez the Orderbook part's unfinished don't look

Coinbase revamp, Orderbook still unfinished

* Coinbase revamp; CreateReport is still WIP

* More coinbase improvements; onto sandbox testing

* Coinbase revamp continues

* Coinbase revamp continues

* Coinbasepro revamp is ceaseless

* Coinbase revamp, starting on advanced trade API

* Coinbase Advanced Trade Starts in Ernest

V3 done, onto V2

Coinbase revamp nears completion

Coinbase revamp nears completion

Test commit should fail

Coinbase revamp nears completion

* Coinbase revamp stage wrapper

* Coinbase wrapper coherence continues

* Coinbase wrapper continues writhing

* Coinbase wrapper & codebase cleanup

* Coinbase updates & wrap progress

* More Coinbase wrapper progress

* Wrapper is wrapped, kinda

* Test & type checking

* Coinbase REST revamp finished

* Post-merge fix

* WS revamp begins

* WS Main Revamp Done?

* CB websocket tidying up

* Coinbase WS wrapperupperer

* Coinbase revamp done??

* Linter progress

* Continued lint cleanup

* Further lint cleanup

* Increased lint coverage

* Does this fix all sloppy reassigns & shadowing?

* Undoing retry policy change

* Documentation regeneration

* Coinbase code improvements

* Providing warning about known issue

* Updating an error to new format

* Making gocritic happy

* Review adherence

* Endpoints moved to V3 & nil pointer fixes

* Removing seemingly superfluous constant

* Glorious improvements

* Removing unused error

* Partial public endpoint addition

* Slight improvements

* Wrapper improvements; still a few errors left in other packages

* A lil Coinbase progress

* Json cleaning

* Lint appeasement

* Config repair

* Config fix (real)

* Little fix

* New public endpoint incorporation

* Additional fixes

* Improvements & Appeasements

* LineSaver

* Additional fixes

* Another fix

* Fixing picked nits

* Quick fixies

* Lil fixes

* Subscriptions: Add List.Enabled

* CoinbasePro: Add subscription templating

* fixup! CoinbasePro: Add subscription templating

* fixup! CoinbasePro: Add subscription templating

* Comment fix

* Subsequent fixes

* Issues hopefully fixed

* Lint fix

* Glorious fixes

* Json formatting

* ShazNits

* (L/N)i(n/)t

* Adding a test

* Tiny test improvement

* Template patch testing

* Fixes

* Further shaznits

* Lint nit

* JWT move and other fixes

* Small nits

* Shaznit, singular

* Post-merge fix

* Post-merge fixes

* Typo fix

* Some glorious nits

* Required changes

* Stop going

* Alias attempt

* Alias fix & test cleanup

* Test fix

* GetDepositAddress logic improvement

* Status update: Fixed

* Lint fix

* Happy birthday to PR 1480

* Cleanups

* Necessary nit corrections

* Fixing sillybug

* As per request

* Programming progress

* Order fixes

* Further fixies

* Test fix

* Pre-merge fixes

* More shaznits

* Context

* Sonic error handling

* Import fix

* Better Sonic error handling

* Perfect Sonic error handling?

* F purge

* Coinbase improvements

* API Update Conformity

* Coinbase continuation

* Coinbase order improvements

* Coinbase order improvements

* CreateOrderConfig improvements

* Managing API updates

* Coinbase API update progression

* jwt rename

* Comment link fix

* Coinbase v2 cleanup

* Post-merge fixes

* Review fixes

* GK's suggestions

* Linter fix

* Minor gbjk fixes

* Nit fixes

* Merge fix

* Lint fixes

* Coinbase rename stage 1

* Coinbase rename stage 2

* Coinbase rename stage 3

* Coinbase rename stage 4

* Coinbase rename final fix

* Coinbase: PoC on converting to request structs

* Applying requested changes

* Many review fixes, handled

* Thrashed by nits

* More minor modifications

* The last nit!?

---------

Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com>
2025-09-16 13:37:00 +10:00

763 lines
21 KiB
Go

package buffer
import (
"math/rand"
"strconv"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/key"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
)
var (
itemArray = [][]orderbook.Level{
{{Price: 1000, Amount: 1, ID: 1000}},
{{Price: 2000, Amount: 1, ID: 2000}},
{{Price: 3000, Amount: 1, ID: 3000}},
{{Price: 3000, Amount: 2, ID: 4000}},
{{Price: 4000, Amount: 0, ID: 6000}},
{{Price: 5000, Amount: 1, ID: 5000}},
}
offset = common.Counter{}
)
const exchangeName = "exchangeTest"
// getExclusivePair returns a currency pair with a unique ID for testing as books are centralised and changes will affect other tests
func getExclusivePair() (currency.Pair, error) {
return currency.NewPairFromStrings(currency.BTC.String(), currency.USDT.String()+strconv.FormatInt(offset.IncrementAndGet(), 10))
}
func createSnapshot(pair currency.Pair) (holder *Orderbook, asks, bids orderbook.Levels, err error) {
asks = orderbook.Levels{{Price: 4000, Amount: 1, ID: 6}}
bids = orderbook.Levels{{Price: 4000, Amount: 1, ID: 6}}
book := &orderbook.Book{
Exchange: exchangeName,
Asks: asks,
Bids: bids,
Asset: asset.Spot,
Pair: pair,
PriceDuplication: true,
LastUpdated: time.Now(),
LastUpdateID: 69420,
ValidateOrderbook: true,
}
newBook := make(map[key.PairAsset]*orderbookHolder)
ch := make(chan any)
go func(<-chan any) { // reader
for range ch {
continue
}
}(ch)
holder = &Orderbook{
exchangeName: exchangeName,
dataHandler: ch,
ob: newBook,
}
err = holder.LoadSnapshot(book)
return holder, asks, bids, err
}
// BenchmarkBufferPerformance demonstrates buffer more performant than multi
// process calls
// 890016 1688 ns/op 416 B/op 3 allocs/op
func BenchmarkBufferPerformance(b *testing.B) {
cp, err := getExclusivePair()
require.NoError(b, err)
holder, asks, bids, err := createSnapshot(cp)
require.NoError(b, err)
holder.bufferEnabled = true
update := &orderbook.Update{
Bids: bids,
Asks: asks,
Pair: cp,
UpdateTime: time.Now(),
Asset: asset.Spot,
}
for b.Loop() {
randomIndex := rand.Intn(4) //nolint:gosec // no need to import crypo/rand for testing
update.Asks = itemArray[randomIndex]
update.Bids = itemArray[randomIndex]
require.NoError(b, holder.Update(update))
}
}
// BenchmarkBufferSortingPerformance benchmark
//
// 613964 2093 ns/op 440 B/op 4 allocs/op
func BenchmarkBufferSortingPerformance(b *testing.B) {
cp, err := getExclusivePair()
require.NoError(b, err)
holder, asks, bids, err := createSnapshot(cp)
require.NoError(b, err)
holder.bufferEnabled = true
holder.sortBuffer = true
update := &orderbook.Update{
Bids: bids,
Asks: asks,
Pair: cp,
UpdateTime: time.Now(),
Asset: asset.Spot,
}
for b.Loop() {
randomIndex := rand.Intn(4) //nolint:gosec // no need to import crypo/rand for testing
update.Asks = itemArray[randomIndex]
update.Bids = itemArray[randomIndex]
require.NoError(b, holder.Update(update))
}
}
// BenchmarkBufferSortingPerformance benchmark
// 914500 1599 ns/op 440 B/op 4 allocs/op
func BenchmarkBufferSortingByIDPerformance(b *testing.B) {
cp, err := getExclusivePair()
require.NoError(b, err)
holder, asks, bids, err := createSnapshot(cp)
require.NoError(b, err)
holder.bufferEnabled = true
holder.sortBuffer = true
holder.sortBufferByUpdateIDs = true
update := &orderbook.Update{
Bids: bids,
Asks: asks,
Pair: cp,
UpdateTime: time.Now(),
Asset: asset.Spot,
}
for b.Loop() {
randomIndex := rand.Intn(4) //nolint:gosec // no need to import crypo/rand for testing
update.Asks = itemArray[randomIndex]
update.Bids = itemArray[randomIndex]
require.NoError(b, holder.Update(update))
}
}
// BenchmarkNoBufferPerformance demonstrates orderbook process more performant
// than buffer
// 122659 12792 ns/op 972 B/op 7 allocs/op PRIOR
// 1225924 1028 ns/op 240 B/op 2 allocs/op CURRENT
func BenchmarkNoBufferPerformance(b *testing.B) {
cp, err := getExclusivePair()
require.NoError(b, err)
obl, asks, bids, err := createSnapshot(cp)
require.NoError(b, err)
update := &orderbook.Update{
Bids: bids,
Asks: asks,
Pair: cp,
UpdateTime: time.Now(),
Asset: asset.Spot,
}
for b.Loop() {
randomIndex := rand.Intn(4) //nolint:gosec // no need to import crypo/rand for testing
update.Asks = itemArray[randomIndex]
update.Bids = itemArray[randomIndex]
require.NoError(b, obl.Update(update))
}
}
// TestHittingTheBuffer logic test
func TestHittingTheBuffer(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
holder, _, _, err := createSnapshot(cp)
require.NoError(t, err)
holder.bufferEnabled = true
holder.obBufferLimit = 5
for i := range itemArray {
asks := itemArray[i]
bids := itemArray[i]
err = holder.Update(&orderbook.Update{
Bids: bids,
Asks: asks,
Pair: cp,
UpdateTime: time.Now(),
Asset: asset.Spot,
})
require.NoError(t, err)
}
book := holder.ob[key.PairAsset{Base: cp.Base.Item, Quote: cp.Quote.Item, Asset: asset.Spot}]
askLen, err := book.ob.GetAskLength()
require.NoError(t, err)
assert.Equal(t, 3, askLen)
bidLen, err := book.ob.GetBidLength()
require.NoError(t, err)
assert.Equal(t, 3, bidLen)
}
// TestInsertWithIDs logic test
func TestInsertWithIDs(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
holder, _, _, err := createSnapshot(cp)
require.NoError(t, err)
holder.bufferEnabled = true
holder.obBufferLimit = 5
for i := range itemArray {
asks := itemArray[i]
if asks[0].Amount <= 0 {
continue
}
bids := itemArray[i]
err = holder.Update(&orderbook.Update{
Bids: bids,
Asks: asks,
Pair: cp,
UpdateTime: time.Now(),
Asset: asset.Spot,
Action: orderbook.UpdateOrInsertAction,
})
require.NoError(t, err)
}
book := holder.ob[key.PairAsset{Base: cp.Base.Item, Quote: cp.Quote.Item, Asset: asset.Spot}]
askLen, err := book.ob.GetAskLength()
require.NoError(t, err)
assert.Equal(t, 6, askLen)
bidLen, err := book.ob.GetBidLength()
require.NoError(t, err)
assert.Equal(t, 6, bidLen)
holder.obBufferLimit = 1
err = holder.Update(&orderbook.Update{
UpdateTime: time.Now(),
Asset: asset.Spot,
Pair: cp,
})
assert.ErrorIs(t, err, orderbook.ErrEmptyUpdate)
}
// TestSortIDs logic test
func TestSortIDs(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
holder, _, _, err := createSnapshot(cp)
require.NoError(t, err)
holder.bufferEnabled = true
holder.sortBufferByUpdateIDs = true
holder.sortBuffer = true
holder.obBufferLimit = 5
for i := range itemArray {
asks := itemArray[i]
bids := itemArray[i]
err = holder.Update(&orderbook.Update{
Bids: bids,
Asks: asks,
Pair: cp,
UpdateID: int64(i),
Asset: asset.Spot,
UpdateTime: time.Now(),
})
require.NoError(t, err)
}
book := holder.ob[key.PairAsset{Base: cp.Base.Item, Quote: cp.Quote.Item, Asset: asset.Spot}]
askLen, err := book.ob.GetAskLength()
require.NoError(t, err)
assert.Equal(t, 3, askLen)
bidLen, err := book.ob.GetBidLength()
require.NoError(t, err)
assert.Equal(t, 3, bidLen)
}
// TestOutOfOrderIDs logic test
func TestOutOfOrderIDs(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
holder, _, _, err := createSnapshot(cp)
require.NoError(t, err)
outOFOrderIDs := []int64{2, 1, 5, 3, 4, 6, 7}
assert.Equal(t, 1000., itemArray[0][0].Price)
holder.bufferEnabled = true
holder.sortBuffer = true
holder.obBufferLimit = 5
for i := range itemArray {
asks := itemArray[i]
err = holder.Update(&orderbook.Update{
Asks: asks,
Pair: cp,
UpdateID: outOFOrderIDs[i],
Asset: asset.Spot,
UpdateTime: time.Now(),
})
require.NoError(t, err)
}
book := holder.ob[key.PairAsset{Base: cp.Base.Item, Quote: cp.Quote.Item, Asset: asset.Spot}]
cpy, err := book.ob.Retrieve()
require.NoError(t, err)
// Index 1 since index 0 is price 7000
assert.Equal(t, 2000., cpy.Asks[1].Price)
}
func TestOrderbookLastUpdateID(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
holder, _, _, err := createSnapshot(cp)
require.NoError(t, err)
assert.Equal(t, 1000., itemArray[0][0].Price)
// this update invalidates the book
err = holder.Update(&orderbook.Update{
Asks: orderbook.Levels{{Price: 999999}},
Pair: cp,
UpdateID: -1,
Asset: asset.Spot,
UpdateTime: time.Now(),
ExpectedChecksum: 1337,
GenerateChecksum: func(*orderbook.Book) uint32 { return 1336 },
})
require.ErrorIs(t, err, orderbook.ErrOrderbookInvalid)
cp, err = getExclusivePair()
require.NoError(t, err)
holder, _, _, err = createSnapshot(cp)
require.NoError(t, err)
for i := range itemArray {
asks := itemArray[i]
err = holder.Update(&orderbook.Update{
Asks: asks,
Pair: cp,
UpdateID: int64(i) + 1 + 69420,
Asset: asset.Spot,
UpdateTime: time.Now(),
SkipOutOfOrderLastUpdateID: true,
ExpectedChecksum: 1337,
GenerateChecksum: func(*orderbook.Book) uint32 { return 1337 },
})
require.NoError(t, err)
}
// out of order
err = holder.Update(&orderbook.Update{
Asks: orderbook.Levels{{Price: 999999}},
Pair: cp,
UpdateID: 1,
Asset: asset.Spot,
SkipOutOfOrderLastUpdateID: true,
})
require.NoError(t, err, "Out of sequence Update must not error")
ob, err := holder.GetOrderbook(cp, asset.Spot)
require.NoError(t, err, "GetOrderbook must not error")
assert.Equal(t, int64(len(itemArray)+69420), ob.LastUpdateID, "Out of sequence Update should not change LastUpdateID")
}
// TestRunUpdateWithoutSnapshot logic test
func TestRunUpdateWithoutSnapshot(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
var holder Orderbook
asks := []orderbook.Level{{Price: 4000, Amount: 1, ID: 8}}
bids := []orderbook.Level{{Price: 5999, Amount: 1, ID: 8}, {Price: 4000, Amount: 1, ID: 9}}
holder.exchangeName = exchangeName
err = holder.Update(&orderbook.Update{
Bids: bids,
Asks: asks,
Pair: cp,
UpdateTime: time.Now(),
Asset: asset.Spot,
})
require.ErrorIs(t, err, orderbook.ErrDepthNotFound)
}
// TestRunUpdateWithoutAnyUpdates logic test
func TestRunUpdateWithoutAnyUpdates(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
holder, _, _, err := createSnapshot(cp)
require.NoError(t, err)
holder.exchangeName = exchangeName
err = holder.Update(&orderbook.Update{
Bids: orderbook.Levels{},
Asks: orderbook.Levels{},
Pair: cp,
UpdateTime: time.Now(),
Asset: asset.Spot,
})
require.ErrorIs(t, err, orderbook.ErrEmptyUpdate)
}
// TestRunSnapshotWithNoData logic test
func TestRunSnapshotWithNoData(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
var obl Orderbook
obl.ob = make(map[key.PairAsset]*orderbookHolder)
obl.dataHandler = make(chan any, 1)
var snapShot1 orderbook.Book
snapShot1.Asset = asset.Spot
snapShot1.Pair = cp
snapShot1.Exchange = "test"
obl.exchangeName = "test"
snapShot1.LastUpdated = time.Now()
require.NoError(t, obl.LoadSnapshot(&snapShot1))
}
// TestLoadSnapshot logic test
func TestLoadSnapshot(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
var obl Orderbook
obl.dataHandler = make(chan any, 100)
obl.ob = make(map[key.PairAsset]*orderbookHolder)
err = obl.LoadSnapshot(&orderbook.Book{Asks: orderbook.Levels{{Amount: 1}}, ValidateOrderbook: true})
require.ErrorIs(t, err, orderbook.ErrPriceZero)
err = obl.LoadSnapshot(&orderbook.Book{Asks: orderbook.Levels{{Amount: 1}}})
require.ErrorIs(t, err, common.ErrExchangeNameNotSet)
err = obl.LoadSnapshot(&orderbook.Book{Asks: orderbook.Levels{{Amount: 1}}, Exchange: "test", Pair: cp, Asset: asset.Spot})
require.ErrorIs(t, err, orderbook.ErrLastUpdatedNotSet)
var snapShot1 orderbook.Book
snapShot1.Exchange = "SnapshotWithOverride"
asks := []orderbook.Level{{Price: 4000, Amount: 1, ID: 8}}
bids := []orderbook.Level{{Price: 4000, Amount: 1, ID: 9}}
snapShot1.Asks = asks
snapShot1.Bids = bids
snapShot1.Asset = asset.Spot
snapShot1.Pair = cp
snapShot1.LastUpdated = time.Now()
require.NoError(t, obl.LoadSnapshot(&snapShot1))
}
// TestFlushBuffer logic test
func TestFlushBuffer(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
obl, _, _, err := createSnapshot(cp)
require.NoError(t, err, "createSnapshot must not error")
require.NotEmpty(t, obl.ob, "createSnapshot must not return empty")
k := key.PairAsset{Base: cp.Base.Item, Quote: cp.Quote.Item, Asset: asset.Spot}
holder, ok := obl.ob[k]
require.Truef(t, ok, "createSnapshot must return a orderbook for %v", k)
holder.buffer = make([]orderbook.Update, 0, 10)
holder.buffer = append(holder.buffer, orderbook.Update{})
obl.FlushBuffer()
assert.Empty(t, holder.buffer, "FlushBuffer should empty buffer")
assert.Equal(t, 10, cap(holder.buffer), "FlushBuffer should leave the buffer cap to avoid reallocs")
}
// TestInsertingSnapShots logic test
func TestInsertingSnapShots(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
var holder Orderbook
holder.dataHandler = make(chan any, 100)
holder.ob = make(map[key.PairAsset]*orderbookHolder)
var snapShot1 orderbook.Book
snapShot1.Exchange = "WSORDERBOOKTEST1"
asks := []orderbook.Level{
{Price: 6000, Amount: 1, ID: 1},
{Price: 6001, Amount: 0.5, ID: 2},
{Price: 6002, Amount: 2, ID: 3},
{Price: 6003, Amount: 3, ID: 4},
{Price: 6004, Amount: 5, ID: 5},
{Price: 6005, Amount: 2, ID: 6},
{Price: 6006, Amount: 1.5, ID: 7},
{Price: 6007, Amount: 0.5, ID: 8},
{Price: 6008, Amount: 23, ID: 9},
{Price: 6009, Amount: 9, ID: 10},
{Price: 6010, Amount: 7, ID: 11},
}
bids := []orderbook.Level{
{Price: 5999, Amount: 1, ID: 12},
{Price: 5998, Amount: 0.5, ID: 13},
{Price: 5997, Amount: 2, ID: 14},
{Price: 5996, Amount: 3, ID: 15},
{Price: 5995, Amount: 5, ID: 16},
{Price: 5994, Amount: 2, ID: 17},
{Price: 5993, Amount: 1.5, ID: 18},
{Price: 5992, Amount: 0.5, ID: 19},
{Price: 5991, Amount: 23, ID: 20},
{Price: 5990, Amount: 9, ID: 21},
{Price: 5989, Amount: 7, ID: 22},
}
snapShot1.Asks = asks
snapShot1.Bids = bids
snapShot1.Asset = asset.Spot
snapShot1.Pair = cp
snapShot1.LastUpdated = time.Now()
require.NoError(t, holder.LoadSnapshot(&snapShot1))
var snapShot2 orderbook.Book
snapShot2.Exchange = "WSORDERBOOKTEST2"
asks = []orderbook.Level{
{Price: 51, Amount: 1, ID: 1},
{Price: 52, Amount: 0.5, ID: 2},
{Price: 53, Amount: 2, ID: 3},
{Price: 54, Amount: 3, ID: 4},
{Price: 55, Amount: 5, ID: 5},
{Price: 56, Amount: 2, ID: 6},
{Price: 57, Amount: 1.5, ID: 7},
{Price: 58, Amount: 0.5, ID: 8},
{Price: 59, Amount: 23, ID: 9},
{Price: 50, Amount: 9, ID: 10},
{Price: 60, Amount: 7, ID: 11},
}
bids = []orderbook.Level{
{Price: 49, Amount: 1, ID: 12},
{Price: 48, Amount: 0.5, ID: 13},
{Price: 47, Amount: 2, ID: 14},
{Price: 46, Amount: 3, ID: 15},
{Price: 45, Amount: 5, ID: 16},
{Price: 44, Amount: 2, ID: 17},
{Price: 43, Amount: 1.5, ID: 18},
{Price: 42, Amount: 0.5, ID: 19},
{Price: 41, Amount: 23, ID: 20},
{Price: 40, Amount: 9, ID: 21},
{Price: 39, Amount: 7, ID: 22},
}
snapShot2.Asks = asks
snapShot2.Asks.SortAsks()
snapShot2.Bids = bids
snapShot2.Bids.SortBids()
snapShot2.Asset = asset.Spot
snapShot2.Pair, err = getExclusivePair()
require.NoError(t, err)
snapShot2.LastUpdated = time.Now()
require.NoError(t, holder.LoadSnapshot(&snapShot2))
var snapShot3 orderbook.Book
snapShot3.Exchange = "WSORDERBOOKTEST3"
asks = []orderbook.Level{
{Price: 511, Amount: 1, ID: 1},
{Price: 52, Amount: 0.5, ID: 2},
{Price: 53, Amount: 2, ID: 3},
{Price: 54, Amount: 3, ID: 4},
{Price: 55, Amount: 5, ID: 5},
{Price: 56, Amount: 2, ID: 6},
{Price: 57, Amount: 1.5, ID: 7},
{Price: 58, Amount: 0.5, ID: 8},
{Price: 59, Amount: 23, ID: 9},
{Price: 50, Amount: 9, ID: 10},
{Price: 60, Amount: 7, ID: 11},
}
bids = []orderbook.Level{
{Price: 49, Amount: 1, ID: 12},
{Price: 48, Amount: 0.5, ID: 13},
{Price: 47, Amount: 2, ID: 14},
{Price: 46, Amount: 3, ID: 15},
{Price: 45, Amount: 5, ID: 16},
{Price: 44, Amount: 2, ID: 17},
{Price: 43, Amount: 1.5, ID: 18},
{Price: 42, Amount: 0.5, ID: 19},
{Price: 41, Amount: 23, ID: 20},
{Price: 40, Amount: 9, ID: 21},
{Price: 39, Amount: 7, ID: 22},
}
snapShot3.Asks = asks
snapShot3.Asks.SortAsks()
snapShot3.Bids = bids
snapShot3.Bids.SortBids()
snapShot3.Asset = asset.Futures
snapShot3.Pair, err = getExclusivePair()
require.NoError(t, err)
snapShot3.LastUpdated = time.Now()
require.NoError(t, holder.LoadSnapshot(&snapShot3))
ob, err := holder.GetOrderbook(snapShot1.Pair, snapShot1.Asset)
require.NoError(t, err)
assert.Equal(t, snapShot1.Asks[0], ob.Asks[0])
ob, err = holder.GetOrderbook(snapShot2.Pair, snapShot2.Asset)
require.NoError(t, err)
assert.Equal(t, snapShot2.Asks[0], ob.Asks[0])
ob, err = holder.GetOrderbook(snapShot3.Pair, snapShot3.Asset)
require.NoError(t, err)
assert.Equal(t, snapShot3.Asks[0], ob.Asks[0])
}
func TestGetOrderbook(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
holder, _, _, err := createSnapshot(cp)
require.NoError(t, err)
_, err = holder.GetOrderbook(currency.EMPTYPAIR, asset.Spot)
require.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
_, err = holder.GetOrderbook(cp, 0)
require.ErrorIs(t, err, asset.ErrInvalidAsset)
ob, err := holder.GetOrderbook(cp, asset.Spot)
require.NoError(t, err)
bufferOb := holder.ob[key.PairAsset{Base: cp.Base.Item, Quote: cp.Quote.Item, Asset: asset.Spot}]
b, err := bufferOb.ob.Retrieve()
require.NoError(t, err)
askLen, err := bufferOb.ob.GetAskLength()
require.NoError(t, err)
bidLen, err := bufferOb.ob.GetBidLength()
require.NoError(t, err)
assert.Equal(t, askLen, len(ob.Asks), "ask length mismatch")
assert.Equal(t, bidLen, len(ob.Bids), "bid length mismatch")
assert.Equal(t, b.Asset, ob.Asset, "asset mismatch")
assert.Equal(t, b.Exchange, ob.Exchange, "exchange name mismatch")
assert.Equal(t, b.LastUpdateID, ob.LastUpdateID, "last update ID mismatch")
assert.Equal(t, b.PriceDuplication, ob.PriceDuplication, "price duplication mismatch")
assert.Equal(t, b.Pair, ob.Pair, "pair mismatch")
}
func TestLastUpdateID(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
holder, _, _, err := createSnapshot(cp)
require.NoError(t, err)
_, err = holder.LastUpdateID(currency.EMPTYPAIR, asset.Spot)
require.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
_, err = holder.LastUpdateID(cp, 0)
require.ErrorIs(t, err, asset.ErrInvalidAsset)
_, err = holder.LastUpdateID(cp, asset.FutureCombo)
require.ErrorIs(t, err, orderbook.ErrDepthNotFound)
ob, err := holder.LastUpdateID(cp, asset.Spot)
require.NoError(t, err)
require.Equal(t, int64(69420), ob)
}
func TestSetup(t *testing.T) {
t.Parallel()
w := Orderbook{}
err := w.Setup(nil, nil, nil)
require.ErrorIs(t, err, errExchangeConfigNil)
exchangeConfig := &config.Exchange{}
err = w.Setup(exchangeConfig, nil, nil)
require.ErrorIs(t, err, errBufferConfigNil)
bufferConf := &Config{}
err = w.Setup(exchangeConfig, bufferConf, nil)
require.ErrorIs(t, err, errUnsetDataHandler)
exchangeConfig.Orderbook.WebsocketBufferEnabled = true
err = w.Setup(exchangeConfig, bufferConf, make(chan any))
require.ErrorIs(t, err, errIssueBufferEnabledButNoLimit)
exchangeConfig.Orderbook.WebsocketBufferLimit = 1337
exchangeConfig.Orderbook.WebsocketBufferEnabled = true
exchangeConfig.Name = "test"
bufferConf.SortBuffer = true
bufferConf.SortBufferByUpdateIDs = true
err = w.Setup(exchangeConfig, bufferConf, make(chan any))
require.NoError(t, err)
require.Equal(t, 1337, w.obBufferLimit)
require.True(t, w.bufferEnabled)
require.True(t, w.sortBuffer)
require.True(t, w.sortBufferByUpdateIDs)
require.Equal(t, "test", w.exchangeName)
}
func TestInvalidateOrderbook(t *testing.T) {
t.Parallel()
cp, err := getExclusivePair()
require.NoError(t, err)
w := &Orderbook{}
err = w.Setup(&config.Exchange{Name: "test"}, &Config{}, make(chan any, 2))
require.NoError(t, err)
var snapShot1 orderbook.Book
snapShot1.Exchange = "Snapshooooot"
asks := []orderbook.Level{{Price: 4000, Amount: 1, ID: 8}}
bids := []orderbook.Level{{Price: 4000, Amount: 1, ID: 9}}
snapShot1.Asks = asks
snapShot1.Bids = bids
snapShot1.Asset = asset.Spot
snapShot1.Pair = cp
snapShot1.LastUpdated = time.Now()
err = w.InvalidateOrderbook(cp, asset.Spot)
if err == nil {
t.Fatal("book not loaded error cannot be nil")
}
_, err = w.GetOrderbook(cp, asset.Spot)
require.ErrorIs(t, err, orderbook.ErrDepthNotFound)
require.NoError(t, w.LoadSnapshot(&snapShot1))
require.NoError(t, w.InvalidateOrderbook(cp, asset.Spot))
_, err = w.GetOrderbook(cp, asset.Spot)
require.ErrorIs(t, err, orderbook.ErrOrderbookInvalid)
}