"""期权合约列表缓存与限频退避.""" from __future__ import annotations import time import unittest from unittest.mock import MagicMock, patch from lib.exchange import okx_options_lib as m class FetchOptionInstrumentsCacheTests(unittest.TestCase): def setUp(self): m.invalidate_option_instruments_cache() def tearDown(self): m.invalidate_option_instruments_cache() def test_cache_hit_skips_second_api_call(self): ex = MagicMock() ex.public_get_public_instruments.return_value = { "data": [ { "instId": "ETH-USD_UM-260812-2000-C", "state": "live", "expTime": "9999999999999", } ] } a = m.fetch_option_instruments(ex, "ETH-USD_UM") b = m.fetch_option_instruments(ex, "ETH-USD_UM") self.assertEqual(len(a), 1) self.assertEqual(len(b), 1) self.assertEqual(ex.public_get_public_instruments.call_count, 1) @patch("lib.exchange.okx_options_lib.time.sleep", return_value=None) def test_rate_limit_falls_back_to_stale_cache(self, _sleep): ex = MagicMock() ex.public_get_public_instruments.return_value = { "data": [{"instId": "ETH-USD_UM-260812-2000-C", "state": "live"}] } first = m.fetch_option_instruments(ex, "ETH-USD_UM") self.assertEqual(len(first), 1) # 过期 TTL,但仍在 stale 窗口 with m._INSTRUMENTS_CACHE_LOCK: m._INSTRUMENTS_CACHE["ETH-USD_UM"]["updated_at"] = time.time() - 120 ex.public_get_public_instruments.side_effect = Exception( 'okx {"msg":"Too Many Requests","code":"50011"}' ) second = m.fetch_option_instruments(ex, "ETH-USD_UM") self.assertEqual(len(second), 1) self.assertEqual(second[0]["instId"], "ETH-USD_UM-260812-2000-C") if __name__ == "__main__": unittest.main()