From c478a5feb94e12c06d91858a0090351c4a29c0b3 Mon Sep 17 00:00:00 2001 From: dela Date: Sun, 24 May 2026 20:35:39 +0800 Subject: [PATCH] =?UTF-8?q?test(binance):=20mapper.go=20=E5=85=A8=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20100%=20=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 集成测试再补。 --- internal/repo/webapi/binance/mapper_test.go | 231 ++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 internal/repo/webapi/binance/mapper_test.go diff --git a/internal/repo/webapi/binance/mapper_test.go b/internal/repo/webapi/binance/mapper_test.go new file mode 100644 index 0000000..c66c153 --- /dev/null +++ b/internal/repo/webapi/binance/mapper_test.go @@ -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) +}