from datetime import datetime from unittest.mock import MagicMock from hub_volume_rank_lib import ( CACHE_VERSION, LIQUIDITY_RANK_CACHE_VERSION, TOP_N_DEFAULT, _exchange_rank_row_stale, _okx_turnover_usdt, _scores_from_binance, _scores_from_gate, build_usdt_swap_volume_ranks, cache_needs_refresh, format_volume_quote, merge_exchange_rank, rank_date_label, resolve_daily_volume_rank, ) def test_rank_date_label_after_reset(): # 2026-06-08 09:00 北京时间 → 昨日交易日 2026-06-07 dt = datetime(2026, 6, 8, 9, 0, 0) assert rank_date_label(now=dt, reset_hour=8) == "2026-06-07" def test_rank_date_label_before_reset(): # 2026-06-08 07:00 → 当前交易日仍算 2026-06-07,昨日为 2026-06-06 dt = datetime(2026, 6, 8, 7, 0, 0) assert rank_date_label(now=dt, reset_hour=8) == "2026-06-06" def test_format_volume_quote(): assert format_volume_quote(1_500_000_000) == "1.50B" assert format_volume_quote(2_300_000) == "2.30M" assert format_volume_quote(4500) == "4.50K" def test_okx_turnover_usdt(): qv = _okx_turnover_usdt({"volCcy24h": "100", "last": "50"}) assert qv == 5000.0 def test_cache_needs_refresh_and_merge(): cache = {"rank_date": "2026-06-05", "exchanges": {}} assert cache_needs_refresh(cache, expected_rank_date="2026-06-07") is True merged = merge_exchange_rank( cache, "binance", { "ok": True, "rank_date": "2026-06-07", "items": [{"rank": 1, "symbol": "BTC/USDT", "volume_quote": 1.0}], "total_symbols": 100, }, ) assert merged["exchanges"]["binance"]["items"][0]["symbol"] == "BTC/USDT" assert merged["rank_date"] == "2026-06-07" def test_stale_cache_version_forces_refresh(): cache = {"version": CACHE_VERSION - 1, "rank_date": "2026-06-07", "exchanges": {"okx": {"items": [{}]}}} assert cache_needs_refresh(cache) is True def test_short_item_list_is_stale(): items = [{"rank": i, "symbol": f"S{i}/USDT"} for i in range(1, 13)] row = {"items": items, "total_symbols": 12} assert _exchange_rank_row_stale(row) is True full = {"items": items + [{"rank": i, "symbol": f"X{i}/USDT"} for i in range(13, TOP_N_DEFAULT + 1)], "total_symbols": 300} assert _exchange_rank_row_stale(full) is False def test_scores_from_binance_uses_fapi_lightweight_api(): ex = MagicMock() ex.id = "binance" ex.fapiPublicGetTicker24hr.return_value = [ {"symbol": "BTCUSDT", "quoteVolume": "9000000"}, {"symbol": "ETHUSDT", "quoteVolume": "5000000"}, ] scored = _scores_from_binance(ex) assert scored[0][1] == "BTC" assert scored[0][2] == 9000000.0 ex.fetch_tickers.assert_not_called() def test_scores_from_gate_uses_futures_tickers_api(): ex = MagicMock() ex.id = "gateio" ex.publicFuturesGetSettleTickers.return_value = [ {"contract": "BTC_USDT", "volume_24h_quote": "8000000"}, {"contract": "ETH_USDT", "volume_24h_quote": "4000000"}, ] scored = _scores_from_gate(ex) assert scored[0][1] == "BTC" ex.fetch_tickers.assert_not_called() def test_resolve_daily_volume_rank_caches_result(): cache = {"version": 0, "updated_at": 0.0, "ranks": {}, "total": 0} ex = MagicMock() ex.id = "binance" ex.fapiPublicGetTicker24hr.return_value = [ {"symbol": "BTCUSDT", "quoteVolume": "100"}, {"symbol": "ETHUSDT", "quoteVolume": "50"}, ] rank, total = resolve_daily_volume_rank( "BTC", cache, now_ts=1000.0, ttl_sec=60.0, exchange=ex, ensure_markets_loaded=lambda: None, ) assert rank == 1 assert total == 2 assert cache["version"] == LIQUIDITY_RANK_CACHE_VERSION calls = ex.fapiPublicGetTicker24hr.call_count rank2, _ = resolve_daily_volume_rank( "BTC", cache, now_ts=1010.0, ttl_sec=60.0, exchange=ex, ensure_markets_loaded=lambda: None, ) assert rank2 == 1 assert ex.fapiPublicGetTicker24hr.call_count == calls def test_build_usdt_swap_volume_ranks(): ex = MagicMock() ex.id = "binance" ex.fapiPublicGetTicker24hr.return_value = [ {"symbol": "SOLUSDT", "quoteVolume": "200"}, ] ranks, total = build_usdt_swap_volume_ranks(ex, lambda: None) assert ranks["SOL"] == 1 assert total == 1