--- /dev/null
+package match
+
+import (
+ "testing"
+
+ "github.com/bytom/vapor/application/mov/common"
+ "github.com/bytom/vapor/application/mov/database"
+ "github.com/bytom/vapor/application/mov/mock"
+ "github.com/bytom/vapor/testutil"
+)
+
+var (
+ btc2eth = &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH}
+)
+
+func TestOrderBook(t *testing.T) {
+ cases := []struct {
+ desc string
+ initMovStore database.MovStore
+ initArrivalAddOrders []*common.Order
+ initArrivalDelOrders []*common.Order
+ addOrders []*common.Order
+ popOrders []*common.TradePair
+ wantPeekedOrders map[common.TradePair]*common.Order
+ }{
+ {
+ desc: "no arrival orders, no add order, no pop order",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+ },
+ ),
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[0],
+ },
+ },
+ {
+ desc: "no arrival orders, add lower price order, no pop order",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+ }),
+ addOrders: []*common.Order{mock.Btc2EthOrders[3]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[3],
+ },
+ },
+ {
+ desc: "no arrival orders, no add order, pop one order",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+ }),
+ popOrders: []*common.TradePair{btc2eth},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[2],
+ },
+ },
+ {
+ desc: "has arrival add orders, no add order, no pop order, the arrival add order is lower price",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+ }),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[3]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[3],
+ },
+ },
+ {
+ desc: "has arrival add orders, no add order, no pop order, the db add order is lower price",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
+ }),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[2]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[0],
+ },
+ },
+ {
+ desc: "has arrival add orders, no add order, pop one order, after pop the arrival order is lower price",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ }),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
+ popOrders: []*common.TradePair{btc2eth},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[0],
+ },
+ },
+ {
+ desc: "has arrival delete orders, no add order, no pop order",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ }),
+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[2],
+ },
+ },
+ {
+ desc: "has arrival delete orders and arrival add orders, no add order, no pop order, the arrival order is lower price",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[3], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+ }),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[0],
+ },
+ },
+ {
+ desc: "has arrival delete orders and arrival add orders, no add order, pop one order",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[3], mock.Btc2EthOrders[1],
+ }),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[2]},
+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
+ popOrders: []*common.TradePair{btc2eth},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[2],
+ },
+ },
+ {
+ desc: "has arrival add orders, but db order is empty",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{}),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[2]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[0],
+ },
+ },
+ {
+ desc: "no add orders, and db order is empty",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{}),
+ initArrivalAddOrders: []*common.Order{},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: nil,
+ },
+ },
+ {
+ desc: "has arrival delete orders, no add order, no pop order, need recursive to peek one order",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ }),
+ initArrivalAddOrders: []*common.Order{},
+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3], mock.Btc2EthOrders[0], mock.Btc2EthOrders[2]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[1],
+ },
+ },
+ }
+
+ for i, c := range cases {
+ orderBook := NewOrderBook(c.initMovStore, c.initArrivalAddOrders, c.initArrivalDelOrders)
+ for _, order := range c.addOrders {
+ orderBook.AddOrder(order)
+ }
+
+ for _, tradePair := range c.popOrders {
+ orderBook.PopOrder(tradePair)
+ }
+
+ for tradePair, wantOrder := range c.wantPeekedOrders {
+ gotOrder := orderBook.PeekOrder(&tradePair)
+ if wantOrder == gotOrder && wantOrder == nil {
+ continue
+ }
+
+ if gotOrder.Key() != wantOrder.Key() {
+ t.Fatalf("#%d(%s):the key of got order(%v) is not equals key of want order(%v)", i, c.desc, gotOrder, wantOrder)
+ }
+ }
+ }
+}
+
+func TestPeekArrivalOrder(t *testing.T) {
+ cases := []struct {
+ desc string
+ initArrivalAddOrders []*common.Order
+ initArrivalDelOrders []*common.Order
+ peekTradePair *common.TradePair
+ wantArrivalAddOrders []*common.Order
+ wantOrder *common.Order
+ }{
+ {
+ desc: "empty peek",
+ initArrivalAddOrders: []*common.Order{},
+ initArrivalDelOrders: []*common.Order{},
+ peekTradePair: btc2eth,
+ wantArrivalAddOrders: []*common.Order{},
+ wantOrder: nil,
+ },
+ {
+ desc: "1 element regular peek",
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
+ initArrivalDelOrders: []*common.Order{},
+ peekTradePair: btc2eth,
+ wantArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
+ wantOrder: mock.Btc2EthOrders[0],
+ },
+ {
+ desc: "4 element regular peek with",
+ initArrivalAddOrders: []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ },
+ initArrivalDelOrders: []*common.Order{},
+ peekTradePair: btc2eth,
+ wantArrivalAddOrders: []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ },
+ wantOrder: mock.Btc2EthOrders[3],
+ },
+ {
+ desc: "1 element peek with 1 unrelated deleted order",
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[1]},
+ peekTradePair: btc2eth,
+ wantArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
+ wantOrder: mock.Btc2EthOrders[0],
+ },
+ {
+ desc: "1 element peek with 1 related deleted order",
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[0]},
+ peekTradePair: btc2eth,
+ wantArrivalAddOrders: []*common.Order{},
+ wantOrder: nil,
+ },
+ {
+ desc: "4 element peek with first 3 deleted order",
+ initArrivalAddOrders: []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ },
+ initArrivalDelOrders: []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ },
+ peekTradePair: btc2eth,
+ wantArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[1]},
+ wantOrder: mock.Btc2EthOrders[1],
+ },
+ {
+ desc: "4 element peek with first 1 deleted order",
+ initArrivalAddOrders: []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ },
+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
+ peekTradePair: btc2eth,
+ wantArrivalAddOrders: []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+ },
+ wantOrder: mock.Btc2EthOrders[0],
+ },
+ {
+ desc: "4 element peek with first 2th deleted order",
+ initArrivalAddOrders: []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ },
+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[0]},
+ peekTradePair: btc2eth,
+ wantArrivalAddOrders: []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ },
+ wantOrder: mock.Btc2EthOrders[3],
+ },
+ }
+
+ for i, c := range cases {
+ orderBook := NewOrderBook(mock.NewMovStore(nil, nil), c.initArrivalAddOrders, c.initArrivalDelOrders)
+ gotOrder := orderBook.PeekOrder(c.peekTradePair)
+ if !testutil.DeepEqual(gotOrder, c.wantOrder) {
+ t.Fatalf("#%d(%s):the key of got order(%v) is not equals key of want order(%v)", i, c.desc, gotOrder, c.wantOrder)
+ }
+
+ wantAddOrders, _ := arrangeArrivalAddOrders(c.wantArrivalAddOrders).Load(c.peekTradePair.Key())
+ gotAddOrders := orderBook.getArrivalAddOrders(c.peekTradePair.Key())
+ if !testutil.DeepEqual(gotAddOrders, wantAddOrders) {
+ t.Fatalf("#%d(%s): the got arrivalAddOrders(%v) is differnt than want arrivalAddOrders(%v)", i, c.desc, gotAddOrders, wantAddOrders)
+ }
+ }
+}
+
+func TestAddOrder(t *testing.T) {
+ cases := []struct {
+ initOrders []*common.Order
+ wantOrders []*common.Order
+ addOrder *common.Order
+ }{
+ {
+ initOrders: []*common.Order{},
+ addOrder: &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 53, RatioDenominator: 1},
+ wantOrders: []*common.Order{
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 53, RatioDenominator: 1},
+ },
+ },
+ {
+ initOrders: []*common.Order{
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
+ },
+ addOrder: &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
+ wantOrders: []*common.Order{
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
+ },
+ },
+ {
+ initOrders: []*common.Order{
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
+ },
+ addOrder: &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 53, RatioDenominator: 1},
+ wantOrders: []*common.Order{
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 53, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
+ },
+ },
+ {
+ initOrders: []*common.Order{
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
+ },
+ addOrder: &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 49, RatioDenominator: 1},
+ wantOrders: []*common.Order{
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 49, RatioDenominator: 1},
+ },
+ },
+ {
+ initOrders: []*common.Order{
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 49, RatioDenominator: 1},
+ },
+ addOrder: &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
+ wantOrders: []*common.Order{
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 49, RatioDenominator: 1},
+ },
+ },
+ }
+
+ for i, c := range cases {
+ orderBook := NewOrderBook(mock.NewMovStore(nil, nil), c.initOrders, nil)
+ orderBook.AddOrder(c.addOrder)
+ if gotOrders := orderBook.getArrivalAddOrders(btc2eth.Key()); !testutil.DeepEqual(gotOrders, c.wantOrders) {
+ t.Fatalf("#%d: the gotOrders(%v) is differnt than wantOrders(%v)", i, gotOrders, c.wantOrders)
+ }
+ }
+}