feat: improve cloud sync reliability and background status

This commit is contained in:
binglua committed 2026-07-13 04:03:57 +00:00
1 parent ebc94b50e9
commit 7fcd06980f
12 files changed
+711 -82

No files matched your search

+130
View File
@@ -0,0 +1,130 @@
import pytest
from mi_fitness_mcp.services.sync_service import SyncService
class FakeAdapter:
def is_connected(self):
return True
async def iter_daily_activity(self, start_date, end_date):
if False:
yield None
class FakeDatabase:
def get_sync_state(self, data_type):
return None
def update_sync_state(self, data_type, timestamp):
raise AssertionError("empty sync must not advance cursor")
@pytest.mark.asyncio
async def test_default_lookback_and_date_validation():
service = SyncService(FakeAdapter(), FakeDatabase(), default_lookback_days=7)
result = await service.sync_data_type("daily_activity", end_date="2026-07-12")
assert result["start_date"] == "2026-07-06"
assert result["end_date"] == "2026-07-12"
with pytest.raises(ValueError, match="start_date"):
await service.sync_data_type(
"daily_activity", start_date="2026-07-13", end_date="2026-07-12"
)
@pytest.mark.asyncio
async def test_sync_lock_rejects_overlap():
service = SyncService(FakeAdapter(), FakeDatabase())
await service._sync_lock.acquire()
try:
assert service.sync_in_progress is True
with pytest.raises(RuntimeError, match="already in progress"):
await service.sync_data_type(
"daily_activity", start_date="2026-07-12", end_date="2026-07-12"
)
finally:
service._sync_lock.release()
assert service.sync_in_progress is False
@pytest.mark.asyncio
async def test_sync_splits_date_range_into_chunks(monkeypatch):
service = SyncService(FakeAdapter(), FakeDatabase(), chunk_days=3)
calls = []
async def fake_range(data_type, start_date, end_date):
calls.append((start_date, end_date))
return {
"data_type": data_type,
"added": 1,
"updated": 0,
"skipped": 0,
"start_date": start_date,
"end_date": end_date,
}
monkeypatch.setattr(service, "_sync_range", fake_range)
result = await service.sync_data_type(
"daily_activity", start_date="2026-07-06", end_date="2026-07-12"
)
assert calls == [
("2026-07-06", "2026-07-08"),
("2026-07-09", "2026-07-11"),
("2026-07-12", "2026-07-12"),
]
assert result["added"] == 3
assert len(result["chunks"]) == 3
@pytest.mark.asyncio
async def test_chunk_failure_returns_partial_statistics(monkeypatch):
service = SyncService(FakeAdapter(), FakeDatabase(), chunk_days=2)
calls = 0
async def fake_range(data_type, start_date, end_date):
nonlocal calls
calls += 1
if calls == 2:
raise RuntimeError("cloud unavailable")
return {
"data_type": data_type,
"added": 2,
"updated": 0,
"skipped": 0,
"start_date": start_date,
"end_date": end_date,
}
monkeypatch.setattr(service, "_sync_range", fake_range)
result = await service.sync_data_type(
"daily_activity", start_date="2026-07-06", end_date="2026-07-09"
)
assert result["status"] == "partial"
assert result["added"] == 2
assert result["chunks"][0]["status"] == "ok"
assert result["chunks"][1]["status"] == "error"
@pytest.mark.asyncio
async def test_concurrent_sync_is_rejected_instead_of_queued(monkeypatch):
service = SyncService(FakeAdapter(), FakeDatabase())
entered = __import__("asyncio").Event()
release = __import__("asyncio").Event()
async def blocked_range(data_type, start_date, end_date):
entered.set()
await release.wait()
return {"data_type": data_type, "added": 0, "updated": 0, "skipped": 0}
monkeypatch.setattr(service, "_sync_range", blocked_range)
first = __import__("asyncio").create_task(
service.sync_data_type("daily_activity", "2026-07-12", "2026-07-12")
)
await entered.wait()
with pytest.raises(RuntimeError, match="already in progress"):
await service.sync_data_type(
"daily_activity", "2026-07-12", "2026-07-12"
)
release.set()
await first