Files
mi-fitness-health/tests/test_mi_fitness_cloud.py
T

103 lines
3.5 KiB
Python

import pytest
from mi_fitness_mcp.adapters.mi_fitness_cloud import (
MiFitnessCloudAdapter,
_is_authentication_error,
)
async def _collect(async_iterable):
items = []
async for item in async_iterable:
items.append(item)
return items
def test_optional_number_helpers():
adapter = MiFitnessCloudAdapter(user_id="u1", pass_token="p1")
assert adapter._optional_float(0) is None
assert adapter._optional_float("1.5") == 1.5
assert adapter._optional_int(0) is None
assert adapter._optional_int("7") == 7
def test_parse_value_dict_and_json():
adapter = MiFitnessCloudAdapter(user_id="u1", pass_token="p1")
assert adapter._parse_value({"value": {"steps": 1}}) == {"steps": 1}
assert adapter._parse_value({"value": '{"steps": 2}'}) == {"steps": 2}
def test_record_datetime_uses_zone_offset():
adapter = MiFitnessCloudAdapter(user_id="u1", pass_token="p1")
dt = adapter._record_datetime({"time": 0, "zone_offset": 10800})
assert dt.isoformat().startswith("1970-01-01T03:00:00")
@pytest.mark.asyncio
async def test_iter_daily_activity_aggregates_steps_and_calories(monkeypatch):
adapter = MiFitnessCloudAdapter(user_id="u1", pass_token="p1")
adapter._connected = True
adapter._client = object()
async def fake_fetch(key, start_date, end_date, region=None):
if key == "steps":
return [
{
"time": 1743467400,
"zone_offset": 0,
"value": '{"steps": 10, "distance": 8, "calories": 1}',
},
{
"time": 1743467460,
"zone_offset": 0,
"value": '{"steps": 20, "distance": 16, "calories": 2}',
},
]
if key == "calories":
return [
{"time": 1743467400, "zone_offset": 0, "value": '{"calories": 5}'},
{"time": 1743467460, "zone_offset": 0, "value": '{"calories": 7}'},
]
return []
monkeypatch.setattr(adapter, "_fetch_key", fake_fetch)
items = await _collect(adapter.iter_daily_activity("2025-04-01", "2025-04-01"))
assert len(items) == 1
assert items[0].steps == 30
assert items[0].distance_m == 24
assert items[0].active_kcal == 12
@pytest.mark.asyncio
async def test_fetch_key_rejects_repeated_pagination_cursor(monkeypatch):
adapter = MiFitnessCloudAdapter(user_id="u1", pass_token="p1", region="cn")
adapter._client = object()
async def fake_request(base_url, api_path, payload):
return {"data_list": [], "has_more": True, "next_key": "same"}
monkeypatch.setattr(adapter, "_request", fake_request)
with pytest.raises(RuntimeError, match="cursor loop"):
await adapter._fetch_key("steps", "2026-07-06", "2026-07-12")
@pytest.mark.asyncio
async def test_connect_failure_closes_client(monkeypatch):
adapter = MiFitnessCloudAdapter(user_id="u1", pass_token="bad", region="cn")
async def failed_login(user_id, pass_token):
raise RuntimeError("invalid credentials")
monkeypatch.setattr(adapter, "_login_with_token", failed_login)
assert await adapter.connect() is False
assert adapter._client is None
assert "invalid credentials" in adapter.last_error
def test_authentication_error_detection():
assert _is_authentication_error(401, "denied")
assert _is_authentication_error(0, "session expired")
assert _is_authentication_error(-10001, "unknown")
assert not _is_authentication_error(500, "temporary server failure")