test(binance): mapper.go 全函数 100% 覆盖
- parseKlineRow:正例 + 短数组 + future closeTime(G3 保护) + openTime/ closeTime/tradeCount 各类型错误 - toInt64:float64/int64/int/未知 4 个分支 - mapLongShort:global_account 取 LongAccount/ShortAccount, top_trader_position 取 LongPosition/ShortPosition,空输入 - mapTicker/mapPremiumIndex/mapFundingHistory/mapOpenInterest/ mapOpenInterestHistory/mapTakerVolume:纯字段平移,单 case 验证 mapper.go 9 函数全部 100%。包级 31.6% 是 client.go/market.go/ derivatives.go 走真实 HTTP 拉低;这部分留 v2.1 集成测试再补。
This commit is contained in:
231
internal/repo/webapi/binance/mapper_test.go
Normal file
231
internal/repo/webapi/binance/mapper_test.go
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
package binance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cryptoHermes/internal/entity"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseKlineRow_WellFormed(t *testing.T) {
|
||||||
|
// Binance 12 元数组:[openTime, open, high, low, close, volume,
|
||||||
|
// closeTime, quoteAssetVolume, numberOfTrades,
|
||||||
|
// takerBuyBaseVolume, takerBuyQuoteVolume, ignore]
|
||||||
|
// JSON 解析后数字是 float64
|
||||||
|
row := []any{
|
||||||
|
float64(1700000000000), // openTime
|
||||||
|
"50000.00", // open
|
||||||
|
"50500.00", // high
|
||||||
|
"49800.00", // low
|
||||||
|
"50200.00", // close
|
||||||
|
"123.456", // volume
|
||||||
|
float64(1700003599999), // closeTime(过去)
|
||||||
|
"6190000", // quoteVolume
|
||||||
|
float64(789), // tradeCount
|
||||||
|
"60", // takerBuyBase
|
||||||
|
"3000000", // takerBuyQuote
|
||||||
|
"0", // ignore
|
||||||
|
}
|
||||||
|
k, err := parseKlineRow("BTCUSDT", "1h", row)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "binance", k.Source)
|
||||||
|
require.Equal(t, "BTCUSDT", k.Symbol)
|
||||||
|
require.Equal(t, "1h", k.Interval)
|
||||||
|
require.Equal(t, int64(1700000000000), k.OpenTime)
|
||||||
|
require.Equal(t, int64(1700003599999), k.CloseTime)
|
||||||
|
require.Equal(t, "50000.00", k.Open)
|
||||||
|
require.Equal(t, "50500.00", k.High)
|
||||||
|
require.Equal(t, "49800.00", k.Low)
|
||||||
|
require.Equal(t, "50200.00", k.Close)
|
||||||
|
require.Equal(t, "123.456", k.Volume)
|
||||||
|
require.Equal(t, "6190000", k.QuoteVolume)
|
||||||
|
require.Equal(t, int64(789), k.TradeCount)
|
||||||
|
require.Equal(t, "60", k.TakerBuyBaseVolume)
|
||||||
|
require.Equal(t, "3000000", k.TakerBuyQuoteVolume)
|
||||||
|
require.True(t, k.IsClosed, "closeTime 2023-11 远小于 now → IsClosed=true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseKlineRow_FutureCloseTime(t *testing.T) {
|
||||||
|
future := time.Now().Add(2 * time.Hour).UnixMilli()
|
||||||
|
row := []any{
|
||||||
|
float64(time.Now().UnixMilli()),
|
||||||
|
"100", "101", "99", "100", "1",
|
||||||
|
float64(future),
|
||||||
|
"100", float64(1), "0.5", "50", "0",
|
||||||
|
}
|
||||||
|
k, err := parseKlineRow("BTCUSDT", "1h", row)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, k.IsClosed, "未来 closeTime → 未收线,遵守 G3")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseKlineRow_ShortArray(t *testing.T) {
|
||||||
|
row := []any{float64(0), "1", "2", "3", "4"} // 只有 5 元
|
||||||
|
_, err := parseKlineRow("BTCUSDT", "1h", row)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "5 fields")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseKlineRow_OpenTimeWrongType(t *testing.T) {
|
||||||
|
row := []any{
|
||||||
|
"not-a-number", // openTime 应为数字
|
||||||
|
"1", "2", "3", "4", "5",
|
||||||
|
float64(0), "0", float64(0), "0", "0", "0",
|
||||||
|
}
|
||||||
|
_, err := parseKlineRow("BTCUSDT", "1h", row)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "openTime")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseKlineRow_CloseTimeWrongType(t *testing.T) {
|
||||||
|
row := []any{
|
||||||
|
float64(0),
|
||||||
|
"1", "2", "3", "4", "5",
|
||||||
|
"not-a-number",
|
||||||
|
"0", float64(0), "0", "0", "0",
|
||||||
|
}
|
||||||
|
_, err := parseKlineRow("BTCUSDT", "1h", row)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "closeTime")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseKlineRow_TradeCountWrongType(t *testing.T) {
|
||||||
|
row := []any{
|
||||||
|
float64(0),
|
||||||
|
"1", "2", "3", "4", "5",
|
||||||
|
float64(0), "0",
|
||||||
|
"not-a-number", // tradeCount
|
||||||
|
"0", "0", "0",
|
||||||
|
}
|
||||||
|
_, err := parseKlineRow("BTCUSDT", "1h", row)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "tradeCount")
|
||||||
|
}
|
||||||
|
|
||||||
|
// toInt64 全分支:float64 / int64 / int / 未知
|
||||||
|
func TestToInt64(t *testing.T) {
|
||||||
|
v, err := toInt64(float64(42.7))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, int64(42), v)
|
||||||
|
|
||||||
|
v, err = toInt64(int64(99))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, int64(99), v)
|
||||||
|
|
||||||
|
v, err = toInt64(int(7))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, int64(7), v)
|
||||||
|
|
||||||
|
_, err = toInt64("oops")
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapLongShort:两种 ratioType 取不同字段
|
||||||
|
func TestMapLongShort_GlobalAccount(t *testing.T) {
|
||||||
|
dto := []longShortRatioDTO{{
|
||||||
|
Symbol: "BTCUSDT",
|
||||||
|
LongShortRatio: "1.5",
|
||||||
|
LongAccount: "0.6",
|
||||||
|
ShortAccount: "0.4",
|
||||||
|
LongPosition: "0.7", // 不应被使用
|
||||||
|
ShortPosition: "0.3",
|
||||||
|
Timestamp: 1700000000000,
|
||||||
|
}}
|
||||||
|
out := mapLongShort(dto, "1h", entity.RatioTypeGlobalAccount)
|
||||||
|
require.Len(t, out, 1)
|
||||||
|
require.Equal(t, "0.6", out[0].LongValue)
|
||||||
|
require.Equal(t, "0.4", out[0].ShortValue)
|
||||||
|
require.Equal(t, "binance", out[0].Source)
|
||||||
|
require.Equal(t, "1h", out[0].Period)
|
||||||
|
require.Equal(t, entity.RatioTypeGlobalAccount, out[0].RatioType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapLongShort_TopTraderPosition(t *testing.T) {
|
||||||
|
dto := []longShortRatioDTO{{
|
||||||
|
Symbol: "ETHUSDT",
|
||||||
|
LongShortRatio: "2.0",
|
||||||
|
LongAccount: "0.5", // 不应被使用
|
||||||
|
ShortAccount: "0.5",
|
||||||
|
LongPosition: "0.66",
|
||||||
|
ShortPosition: "0.34",
|
||||||
|
Timestamp: 1700000000000,
|
||||||
|
}}
|
||||||
|
out := mapLongShort(dto, "4h", entity.RatioTypeTopTraderPosition)
|
||||||
|
require.Len(t, out, 1)
|
||||||
|
require.Equal(t, "0.66", out[0].LongValue)
|
||||||
|
require.Equal(t, "0.34", out[0].ShortValue)
|
||||||
|
require.Equal(t, entity.RatioTypeTopTraderPosition, out[0].RatioType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapLongShort_EmptyInput(t *testing.T) {
|
||||||
|
out := mapLongShort(nil, "1h", entity.RatioTypeGlobalAccount)
|
||||||
|
require.NotNil(t, out)
|
||||||
|
require.Empty(t, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 其余 map* 函数(纯字段平移)------------------------------------
|
||||||
|
|
||||||
|
func TestMapTicker(t *testing.T) {
|
||||||
|
d := &tickerDTO{
|
||||||
|
Symbol: "BTCUSDT", LastPrice: "50000", PriceChange: "100", PriceChangePercent: "0.2",
|
||||||
|
HighPrice: "50500", LowPrice: "49000", Volume: "1234", QuoteVolume: "62000000",
|
||||||
|
OpenTime: 1700000000000, CloseTime: 1700003599999,
|
||||||
|
}
|
||||||
|
out := mapTicker(d)
|
||||||
|
require.Equal(t, "BTCUSDT", out.Symbol)
|
||||||
|
require.Equal(t, "50000", out.LastPrice)
|
||||||
|
require.Equal(t, int64(1700003599999), out.CloseTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapPremiumIndex(t *testing.T) {
|
||||||
|
d := &premiumIndexDTO{
|
||||||
|
Symbol: "BTCUSDT", NextFundingTime: 1700003600000,
|
||||||
|
LastFundingRate: "0.0001", MarkPrice: "50100",
|
||||||
|
}
|
||||||
|
out := mapPremiumIndex(d)
|
||||||
|
require.Equal(t, "binance", out.Source)
|
||||||
|
require.Equal(t, "0.0001", out.FundingRate)
|
||||||
|
require.Equal(t, "50100", out.MarkPrice)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapFundingHistory(t *testing.T) {
|
||||||
|
d := []fundingRateDTO{
|
||||||
|
{Symbol: "BTCUSDT", FundingTime: 1, FundingRate: "0.0001", MarkPrice: "50000"},
|
||||||
|
{Symbol: "BTCUSDT", FundingTime: 2, FundingRate: "0.0002", MarkPrice: "50100"},
|
||||||
|
}
|
||||||
|
out := mapFundingHistory(d)
|
||||||
|
require.Len(t, out, 2)
|
||||||
|
require.Equal(t, "0.0002", out[1].FundingRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapOpenInterest(t *testing.T) {
|
||||||
|
d := &openInterestDTO{Symbol: "BTCUSDT", Time: 1700000000000, OpenInterest: "12345"}
|
||||||
|
out := mapOpenInterest(d, "1h")
|
||||||
|
require.Equal(t, "BTCUSDT", out.Symbol)
|
||||||
|
require.Equal(t, "1h", out.Period)
|
||||||
|
require.Equal(t, "12345", out.OpenInterest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapOpenInterestHistory(t *testing.T) {
|
||||||
|
d := []openInterestHistDTO{
|
||||||
|
{Symbol: "BTCUSDT", Timestamp: 1, SumOpenInterest: "100", SumOpenInterestValue: "5000000"},
|
||||||
|
}
|
||||||
|
out := mapOpenInterestHistory(d, "4h")
|
||||||
|
require.Len(t, out, 1)
|
||||||
|
require.Equal(t, "4h", out[0].Period)
|
||||||
|
require.Equal(t, "100", out[0].OpenInterest)
|
||||||
|
require.Equal(t, "5000000", out[0].OpenInterestValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapTakerVolume(t *testing.T) {
|
||||||
|
d := []takerVolumeDTO{
|
||||||
|
{Timestamp: 1, BuySellRatio: "1.2", BuyVol: "60", SellVol: "50"},
|
||||||
|
}
|
||||||
|
out := mapTakerVolume(d, "BTCUSDT", "1h")
|
||||||
|
require.Len(t, out, 1)
|
||||||
|
require.Equal(t, "BTCUSDT", out[0].Symbol)
|
||||||
|
require.Equal(t, "1.2", out[0].BuySellRatio)
|
||||||
|
require.Equal(t, "60", out[0].BuyVolume)
|
||||||
|
require.Equal(t, "50", out[0].SellVolume)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user