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 }