Files
cryptoHermes/internal/repo/webapi/binance/mapper_test.go
dela c478a5feb9 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 集成测试再补。
2026-05-24 20:35:39 +08:00

232 lines
7.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}