Skip to content

Commit 00267a3

Browse files
author
Kaoru
committed
fix: bars sync 已是最新数据时不抛 ValueError 静默 proc=0
fast_mode 下库中最新 timestamp 已到今天时,_get_fetch_date_range 算出 start_date=明天 > end_date=今天,_validate_and_set_date_range 抛 ValueError 被 sync_smart except 吞成 ServiceResult.error,sync_history 记 proc=0 无说明。 修复: sync_smart 在 _get_fetch_date_range 后加守卫,start_date>end_date 时 返回 success + '已是最新' message + metadata(reason=already_up_to_date), 不再让空范围 ValueError 冒泡。
1 parent 5a53c99 commit 00267a3

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

src/ginkgo/data/services/bar_service.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,22 @@ def sync_smart(
287287
# Determine date range based on fast_mode
288288
start_date, end_date = self._get_fetch_date_range(code, fast_mode, frequency)
289289

290+
# #5450: fast_mode 下若库中数据已是最新(latest_timestamp 已到今天,
291+
# 致 start_date=明天 > end_date=今天),明确返回"已是最新",避免
292+
# _validate_and_set_date_range 抛 ValueError 被 except 吞成失败 proc=0。
293+
# 验收标准2: 数据已最新时说明为何 0 records。
294+
if fast_mode and start_date > end_date:
295+
up_to_date_result = DataSyncResult.create_for_entity(
296+
entity_type="bars",
297+
entity_identifier=code,
298+
sync_strategy="smart",
299+
)
300+
up_to_date_result.set_metadata("reason", "already_up_to_date")
301+
return ServiceResult.success(
302+
data=up_to_date_result,
303+
message=f"{code} 数据已是最新,无需同步",
304+
)
305+
290306
# Call the new date range method
291307
sync_result = self.sync_range(code, start_date, end_date, frequency)
292308

tests/unit/data/services/test_bar_service_mock.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,50 @@ def test_sync_range_idempotent_path_records_processed_gt_zero(self, service, moc
341341
assert result.data.records_processed == 3 # 幂等路径也非 0
342342
assert result.data.records_skipped == 3
343343
assert result.data.is_idempotent is True
344+
345+
346+
# ============================================================
347+
# sync_smart 测试
348+
# ============================================================
349+
350+
class TestBarServiceSyncSmart:
351+
"""sync_smart 方法测试"""
352+
353+
@pytest.mark.unit
354+
def test_fast_mode_data_up_to_date_returns_already_latest(self, service, mock_deps):
355+
"""
356+
#5450: fast_mode 下库中最新数据已是今天时,sync_smart 应返回 success +
357+
'已是最新'说明,而非让 start>end 的 ValueError 被 _validate_and_set_date_range
358+
抛出后吞成失败 proc=0。
359+
360+
复现:000001.SZ 最新 bars timestamp=今天 → _get_fetch_date_range 算出
361+
start=明天 > end=今天 → 旧代码 sync_range 抛 ValueError → sync_smart except
362+
返回 error,sync_history 记 proc=0 无说明。
363+
"""
364+
# 库中最新 bars 已是今天 → start_date = 明天 > end_date = 今天
365+
mock_record = MagicMock()
366+
mock_record.timestamp = datetime.now()
367+
mock_deps["crud_repo"].find.return_value = [mock_record]
368+
369+
result = service.sync_smart("000001.SZ", fast_mode=True)
370+
371+
# 验收标准2: 数据已最新时明确说明为何 0 records
372+
assert result.success is True, f"应成功返回'已是最新',实际 error: {getattr(result, 'error', None)}"
373+
assert "最新" in (result.message or ""), f"message 应说明已最新,实际: {result.message}"
374+
# 守卫提前返回,不应进 sync_range 调数据源
375+
mock_deps["data_source"].fetch_cn_stock_daybar.assert_not_called()
376+
# data 是 DataSyncResult,records_processed=0(已最新无新数据)
377+
assert result.data is not None
378+
assert result.data.records_processed == 0
379+
380+
@pytest.mark.unit
381+
def test_fast_mode_data_up_to_date_metadata_reason(self, service, mock_deps):
382+
"""#5450: '已是最新'结果应带 metadata(reason=already_up_to_date) 供调用方区分"""
383+
mock_record = MagicMock()
384+
mock_record.timestamp = datetime.now()
385+
mock_deps["crud_repo"].find.return_value = [mock_record]
386+
387+
result = service.sync_smart("000001.SZ", fast_mode=True)
388+
389+
assert result.success is True
390+
assert result.data.metadata.get("reason") == "already_up_to_date"

0 commit comments

Comments
 (0)