decred.org/dcrdex@v1.0.5/server/apidata/apidata_test.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package apidata
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"testing"
    10  	"time"
    11  
    12  	"decred.org/dcrdex/dex/candles"
    13  	"decred.org/dcrdex/dex/msgjson"
    14  	"decred.org/dcrdex/server/comms"
    15  	"decred.org/dcrdex/server/matcher"
    16  )
    17  
    18  var dummyErr = fmt.Errorf("dummy error")
    19  
    20  type TMarketSource struct {
    21  	base, quote uint32
    22  }
    23  
    24  func (m *TMarketSource) EpochDuration() uint64 { return 1000 }
    25  func (m *TMarketSource) Base() uint32          { return m.base }
    26  func (m *TMarketSource) Quote() uint32         { return m.quote }
    27  
    28  type TDBSource struct {
    29  	loadEpochErr error
    30  }
    31  
    32  func (db *TDBSource) LoadEpochStats(base, quote uint32, caches []*candles.Cache) error {
    33  	return db.loadEpochErr
    34  }
    35  
    36  func (db *TDBSource) LastCandleEndStamp(base, quote uint32, candleDur uint64) (uint64, error) {
    37  	return 0, nil
    38  }
    39  
    40  func (db *TDBSource) InsertCandles(base, quote uint32, dur uint64, cs []*candles.Candle) error {
    41  	return nil
    42  }
    43  
    44  type TBookSource struct {
    45  	book *msgjson.OrderBook
    46  }
    47  
    48  func (bs *TBookSource) Book(mktName string) (*msgjson.OrderBook, error) {
    49  	return bs.book, nil
    50  }
    51  
    52  type testRig struct {
    53  	db  *TDBSource
    54  	api *DataAPI
    55  }
    56  
    57  func newTestRig() *testRig {
    58  	db := new(TDBSource)
    59  	return &testRig{
    60  		db:  db,
    61  		api: NewDataAPI(db, func(route string, handler comms.HTTPHandler) {}),
    62  	}
    63  }
    64  
    65  func TestAddMarketSource(t *testing.T) {
    66  	rig := newTestRig()
    67  	// initial success
    68  	err := rig.api.AddMarketSource(&TMarketSource{42, 0})
    69  	if err != nil {
    70  		t.Fatalf("AddMarketSource error: %v", err)
    71  	}
    72  	// unknown asset
    73  	err = rig.api.AddMarketSource(&TMarketSource{42, 54321})
    74  	if err == nil {
    75  		t.Fatalf("no error for unknown asset")
    76  	}
    77  	// DB error
    78  	rig.db.loadEpochErr = dummyErr
    79  	err = rig.api.AddMarketSource(&TMarketSource{42, 0})
    80  	if err == nil {
    81  		t.Fatalf("no error for DB error")
    82  	}
    83  	rig.db.loadEpochErr = nil
    84  	// success again
    85  	err = rig.api.AddMarketSource(&TMarketSource{42, 0})
    86  	if err != nil {
    87  		t.Fatalf("AddMarketSource error after: %v", err)
    88  	}
    89  }
    90  
    91  func TestReportEpoch(t *testing.T) {
    92  	rig := newTestRig()
    93  	mktSrc := &TMarketSource{42, 0}
    94  	err := rig.api.AddMarketSource(mktSrc)
    95  	if err != nil {
    96  		t.Fatalf("AddMarketSource error: %v", err)
    97  	}
    98  	epoch := uint64(time.Now().UnixMilli()) / mktSrc.EpochDuration()
    99  	epochsPerDay := uint64(time.Hour*24/time.Millisecond) / mktSrc.EpochDuration()
   100  	epochYesterday := epoch - epochsPerDay - 1
   101  	// initial success
   102  	stats := &matcher.MatchCycleStats{
   103  		MatchVolume: 123,
   104  		QuoteVolume: 2,
   105  		HighRate:    10,
   106  		LowRate:     1,
   107  		StartRate:   4,
   108  		EndRate:     5,
   109  	}
   110  	spot, err := rig.api.ReportEpoch(42, 0, epochYesterday, stats)
   111  	if err != nil {
   112  		t.Fatalf("ReportEpoch yesterday error: %v", err)
   113  	}
   114  	if spot.Vol24 != 0 {
   115  		t.Fatalf("wrong spot Vol24. wanted 0, got %d", spot.Vol24)
   116  	}
   117  
   118  	spot, err = rig.api.ReportEpoch(42, 0, epoch-1, stats)
   119  	if err != nil {
   120  		t.Fatalf("ReportEpoch last epoch error: %v", err)
   121  	}
   122  	if spot.Vol24 != 123 {
   123  		t.Fatalf("wrong spot Vol24. wanted 123, got %d", spot.Vol24)
   124  	}
   125  
   126  	stats.EndRate = 555
   127  
   128  	spot, err = rig.api.ReportEpoch(42, 0, epoch, stats)
   129  	if err != nil {
   130  		t.Fatalf("ReportEpoch error: %v", err)
   131  	}
   132  	if spot.Vol24 != 246 {
   133  		t.Fatalf("wrong spot Vol24. wanted 246, got %d", spot.Vol24)
   134  	}
   135  	if spot.Rate != 555 {
   136  		t.Fatalf("wrong spot Rate. wanted 555, got %d", spot.Rate)
   137  	}
   138  
   139  	// handleSpots should return one spot for the one market.
   140  	spotsI, err := rig.api.handleSpots(nil)
   141  	if err != nil {
   142  		t.Fatalf("handleSpots error: %v", err)
   143  	}
   144  	spotsEnc := spotsI.([]json.RawMessage)
   145  	if spotsEnc == nil {
   146  		t.Fatalf("failed to decode []json.RawMessage. spotsI is %T", spotsI)
   147  	}
   148  	if len(spotsEnc) != 1 {
   149  		t.Fatalf("expected 1 spot, got %d", len(spotsEnc))
   150  	}
   151  	reSpot := new(msgjson.Spot)
   152  	err = json.Unmarshal(spotsEnc[0], reSpot)
   153  	if err != nil {
   154  		t.Fatalf("error encoding spot: %v", err)
   155  	}
   156  	if reSpot.Vol24 != spot.Vol24 {
   157  		t.Fatalf("reSpot Vol24 mismatch, wanted %d, got %d", spot.Vol24, reSpot.Vol24)
   158  	}
   159  
   160  	// handleCandles should return three candles.
   161  	candlesI, err := rig.api.handleCandles(&msgjson.CandlesRequest{
   162  		BaseID:     42,
   163  		QuoteID:    0,
   164  		BinSize:    "1s", // Epoch duration, smallest candle size
   165  		NumCandles: candles.CacheSize,
   166  	})
   167  	if err != nil {
   168  		t.Fatalf("handleCandles error: %v", err)
   169  	}
   170  	wireCandles := candlesI.(*msgjson.WireCandles)
   171  	if wireCandles == nil {
   172  		t.Fatalf("failed to decode *msgjson.WireCandles. candlesI is %T", candlesI)
   173  	}
   174  	if len(wireCandles.StartRates) != 3 {
   175  		t.Fatalf("wrong number of candles. expected 3, got %d", len(wireCandles.StartRates))
   176  	}
   177  }
   178  
   179  func TestOrderBook(t *testing.T) {
   180  	rig := newTestRig()
   181  	book := new(msgjson.OrderBook)
   182  	rig.api.SetBookSource(&TBookSource{book})
   183  	bookI, err := rig.api.handleOrderBook(&msgjson.OrderBookSubscription{
   184  		Base:  42,
   185  		Quote: 0,
   186  	})
   187  	if err != nil {
   188  		t.Fatalf("handleOrderBook error: %v", err)
   189  	}
   190  	reBook := bookI.(*msgjson.OrderBook)
   191  	if reBook == nil {
   192  		t.Fatalf("failed to decode *msgjson.OrderBook. bookI is %T", bookI)
   193  	}
   194  	if reBook != book {
   195  		t.Fatalf("where did this book come from?")
   196  	}
   197  }