from __future__ import annotations import tempfile import unittest from pathlib import Path from unittest.mock import patch from datahub.adapters.base import AdapterError from datahub.adapters.eastmoney import HIS_TRENDS_URL, TRENDS_URL, EastmoneyAdapter from datahub.db import HubDB from datahub.realtime_serve import fetch_intraday class FakeEastmoney(EastmoneyAdapter): def __init__(self) -> None: super().__init__(timeout=2) self.urls: list[str] = [] def _get_json(self, url, params, referer): self.urls.append(f"{url}|{params.get('ndays')}") if url == TRENDS_URL: return {"data": {"name": "中国平安", "code": "601318", "preClose": 56.36, "trends": []}} if url == HIS_TRENDS_URL: return { "data": { "name": "中国平安", "code": "601318", "preClose": 55.8, "trends": [ "2026-09-07 09:30,55.80,55.90,56.00,55.70,100,5580.00,55.900", "2026-09-07 15:00,56.10,56.20,56.30,56.00,200,11240.00,56.150", "2026-09-08 09:30,0,0,0,0,0,0.00,0", ], } } raise AdapterError(f"unexpected url {url}") class EastmoneyIntradayLookbackTests(unittest.TestCase): def test_empty_today_uses_latest_available_session(self): adapter = FakeEastmoney() payload = adapter.fetch_intraday("601318.SH") self.assertEqual(adapter.urls, [f"{TRENDS_URL}|1", f"{TRENDS_URL}|5", f"{HIS_TRENDS_URL}|5"]) self.assertEqual(payload["trade_date"], "2026-09-07") self.assertEqual([point["time"] for point in payload["points"]], ["09:30", "15:00"]) self.assertEqual(payload["points"][0]["close"], 55.9) def test_preferred_date_keeps_that_session(self): adapter = FakeEastmoney() payload = adapter.fetch_intraday("601318.SH", "20260907") self.assertEqual(payload["trade_date"], "2026-09-07") self.assertEqual(len(payload["points"]), 2) class IntradayLkgTests(unittest.TestCase): def setUp(self) -> None: self.tmp = tempfile.TemporaryDirectory() self.db = HubDB(Path(self.tmp.name) / "hub.db") def tearDown(self) -> None: self.tmp.cleanup() def test_source_failure_returns_last_known_good(self): from datahub.realtime_serve import _envelope, _write_cache payload = _envelope( { "entity_type": "stock", "ts_code": "601318.SH", "trade_date": "2026-09-07", "previous_close": 55.8, "points": [{"date": "2026-09-07", "time": "09:30", "close": 55.9}], }, { "tier": "provisional", "trade_date": "20260907", "source": "eastmoney:trends2", "stale": False, }, ) _write_cache(self.db, "intraday:601318.SH:today", payload, 20, "eastmoney:trends2") self.db.execute( "UPDATE rt_cache SET expires_at = ? WHERE cache_key = ?", ("2000-01-01T00:00:00+08:00", "intraday:601318.SH:today"), ) with patch("datahub.realtime_serve.EastmoneyAdapter") as mocked: mocked.return_value.fetch_intraday.side_effect = AdapterError("down") recovered = fetch_intraday(self.db, "601318.SH") self.assertTrue(recovered["meta"]["stale"]) self.assertEqual(recovered["data"]["points"][0]["close"], 55.9) def test_source_failure_without_lkg_raises(self): with patch("datahub.realtime_serve.EastmoneyAdapter") as mocked: mocked.return_value.fetch_intraday.side_effect = AdapterError("down") with self.assertRaises(Exception) as ctx: fetch_intraday(self.db, "000001.SZ") self.assertIn("intraday unavailable", str(ctx.exception)) if __name__ == "__main__": unittest.main()