mirror of
https://github.com/sstent/go-garth.git
synced 2026-01-26 09:03:00 +00:00
336 lines
12 KiB
Python
336 lines
12 KiB
Python
from datetime import date
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
from garth import BodyBatteryData, DailyBodyBatteryStress
|
|
from garth.http import Client
|
|
|
|
|
|
@pytest.mark.vcr
|
|
def test_body_battery_data_get(authed_client: Client):
|
|
body_battery_data = BodyBatteryData.get("2023-07-20", client=authed_client)
|
|
assert isinstance(body_battery_data, list)
|
|
|
|
if body_battery_data:
|
|
# Check first event if available
|
|
event = body_battery_data[0]
|
|
assert event is not None
|
|
|
|
# Test body battery readings property
|
|
readings = event.body_battery_readings
|
|
assert isinstance(readings, list)
|
|
|
|
if readings:
|
|
# Test reading structure
|
|
reading = readings[0]
|
|
assert hasattr(reading, "timestamp")
|
|
assert hasattr(reading, "status")
|
|
assert hasattr(reading, "level")
|
|
assert hasattr(reading, "version")
|
|
|
|
# Test level properties
|
|
assert event.current_level is not None and isinstance(
|
|
event.current_level, int
|
|
)
|
|
assert event.max_level is not None and isinstance(
|
|
event.max_level, int
|
|
)
|
|
assert event.min_level is not None and isinstance(
|
|
event.min_level, int
|
|
)
|
|
|
|
|
|
@pytest.mark.vcr
|
|
def test_body_battery_data_list(authed_client: Client):
|
|
days = 3
|
|
end = date(2023, 7, 20)
|
|
body_battery_data = BodyBatteryData.list(end, days, client=authed_client)
|
|
assert isinstance(body_battery_data, list)
|
|
|
|
# Test that we get data (may be empty if no events)
|
|
assert len(body_battery_data) >= 0
|
|
|
|
|
|
@pytest.mark.vcr
|
|
def test_daily_body_battery_stress_get(authed_client: Client):
|
|
daily_data = DailyBodyBatteryStress.get("2023-07-20", client=authed_client)
|
|
|
|
if daily_data:
|
|
# Test basic structure
|
|
assert daily_data.user_profile_pk
|
|
assert daily_data.calendar_date == date(2023, 7, 20)
|
|
assert daily_data.start_timestamp_gmt
|
|
assert daily_data.end_timestamp_gmt
|
|
|
|
# Test stress data
|
|
assert isinstance(daily_data.max_stress_level, int)
|
|
assert isinstance(daily_data.avg_stress_level, int)
|
|
assert isinstance(daily_data.stress_values_array, list)
|
|
assert isinstance(daily_data.body_battery_values_array, list)
|
|
|
|
# Test stress readings property
|
|
stress_readings = daily_data.stress_readings
|
|
assert isinstance(stress_readings, list)
|
|
|
|
if stress_readings:
|
|
stress_reading = stress_readings[0]
|
|
assert hasattr(stress_reading, "timestamp")
|
|
assert hasattr(stress_reading, "stress_level")
|
|
|
|
# Test body battery readings property
|
|
bb_readings = daily_data.body_battery_readings
|
|
assert isinstance(bb_readings, list)
|
|
|
|
if bb_readings:
|
|
bb_reading = bb_readings[0]
|
|
assert hasattr(bb_reading, "timestamp")
|
|
assert hasattr(bb_reading, "status")
|
|
assert hasattr(bb_reading, "level")
|
|
assert hasattr(bb_reading, "version")
|
|
|
|
# Test computed properties
|
|
assert daily_data.current_body_battery is not None and isinstance(
|
|
daily_data.current_body_battery, int
|
|
)
|
|
assert daily_data.max_body_battery is not None and isinstance(
|
|
daily_data.max_body_battery, int
|
|
)
|
|
assert daily_data.min_body_battery is not None and isinstance(
|
|
daily_data.min_body_battery, int
|
|
)
|
|
|
|
# Test body battery change
|
|
if len(bb_readings) >= 2:
|
|
change = daily_data.body_battery_change
|
|
assert change is not None
|
|
|
|
|
|
@pytest.mark.vcr
|
|
def test_daily_body_battery_stress_get_no_data(authed_client: Client):
|
|
# Test with a date that likely has no data
|
|
daily_data = DailyBodyBatteryStress.get("2020-01-01", client=authed_client)
|
|
# Should return None if no data available
|
|
assert daily_data is None or isinstance(daily_data, DailyBodyBatteryStress)
|
|
|
|
|
|
@pytest.mark.vcr
|
|
def test_daily_body_battery_stress_list(authed_client: Client):
|
|
days = 3
|
|
end = date(2023, 7, 20)
|
|
# Use max_workers=1 to avoid VCR issues with concurrent requests
|
|
daily_data_list = DailyBodyBatteryStress.list(
|
|
end, days, client=authed_client, max_workers=1
|
|
)
|
|
assert isinstance(daily_data_list, list)
|
|
assert (
|
|
len(daily_data_list) <= days
|
|
) # May be less if some days have no data
|
|
|
|
# Test that each item is correct type
|
|
for daily_data in daily_data_list:
|
|
assert isinstance(daily_data, DailyBodyBatteryStress)
|
|
assert isinstance(daily_data.calendar_date, date)
|
|
assert daily_data.user_profile_pk
|
|
|
|
|
|
@pytest.mark.vcr
|
|
def test_body_battery_properties_edge_cases(authed_client: Client):
|
|
# Test empty data handling
|
|
daily_data = DailyBodyBatteryStress.get("2023-07-20", client=authed_client)
|
|
|
|
if daily_data:
|
|
# Test with potentially empty arrays
|
|
if not daily_data.body_battery_values_array:
|
|
assert daily_data.body_battery_readings == []
|
|
assert daily_data.current_body_battery is None
|
|
assert daily_data.max_body_battery is None
|
|
assert daily_data.min_body_battery is None
|
|
assert daily_data.body_battery_change is None
|
|
|
|
if not daily_data.stress_values_array:
|
|
assert daily_data.stress_readings == []
|
|
|
|
|
|
# Error handling tests for BodyBatteryData.get()
|
|
def test_body_battery_data_get_api_error():
|
|
"""Test handling of API errors."""
|
|
mock_client = MagicMock()
|
|
mock_client.connectapi.side_effect = Exception("API Error")
|
|
|
|
result = BodyBatteryData.get("2023-07-20", client=mock_client)
|
|
assert result == []
|
|
|
|
|
|
def test_body_battery_data_get_invalid_response():
|
|
"""Test handling of non-list responses."""
|
|
mock_client = MagicMock()
|
|
mock_client.connectapi.return_value = {"error": "Invalid response"}
|
|
|
|
result = BodyBatteryData.get("2023-07-20", client=mock_client)
|
|
assert result == []
|
|
|
|
|
|
def test_body_battery_data_get_missing_event_data():
|
|
"""Test handling of items with missing event data."""
|
|
mock_client = MagicMock()
|
|
mock_client.connectapi.return_value = [
|
|
{"activityName": "Test", "averageStress": 25} # Missing "event" key
|
|
]
|
|
|
|
result = BodyBatteryData.get("2023-07-20", client=mock_client)
|
|
assert len(result) == 1
|
|
assert result[0].event is None
|
|
|
|
|
|
def test_body_battery_data_get_missing_event_start_time():
|
|
"""Test handling of event data missing eventStartTimeGmt."""
|
|
mock_client = MagicMock()
|
|
mock_client.connectapi.return_value = [
|
|
{
|
|
"event": {"eventType": "sleep"}, # Missing eventStartTimeGmt
|
|
"activityName": "Test",
|
|
"averageStress": 25,
|
|
}
|
|
]
|
|
|
|
result = BodyBatteryData.get("2023-07-20", client=mock_client)
|
|
assert result == [] # Should skip invalid items
|
|
|
|
|
|
def test_body_battery_data_get_invalid_datetime_format():
|
|
"""Test handling of invalid datetime format."""
|
|
mock_client = MagicMock()
|
|
mock_client.connectapi.return_value = [
|
|
{
|
|
"event": {
|
|
"eventType": "sleep",
|
|
"eventStartTimeGmt": "invalid-date",
|
|
},
|
|
"activityName": "Test",
|
|
"averageStress": 25,
|
|
}
|
|
]
|
|
|
|
result = BodyBatteryData.get("2023-07-20", client=mock_client)
|
|
assert result == [] # Should skip invalid items
|
|
|
|
|
|
def test_body_battery_data_get_invalid_field_types():
|
|
"""Test handling of invalid field types."""
|
|
mock_client = MagicMock()
|
|
mock_client.connectapi.return_value = [
|
|
{
|
|
"event": {
|
|
"eventType": "sleep",
|
|
"eventStartTimeGmt": "2023-07-20T10:00:00.000Z",
|
|
"timezoneOffset": "invalid", # Should be number
|
|
"durationInMilliseconds": "invalid", # Should be number
|
|
"bodyBatteryImpact": "invalid", # Should be number
|
|
},
|
|
"activityName": "Test",
|
|
"averageStress": "invalid", # Should be number
|
|
"stressValuesArray": "invalid", # Should be list
|
|
"bodyBatteryValuesArray": "invalid", # Should be list
|
|
}
|
|
]
|
|
|
|
result = BodyBatteryData.get("2023-07-20", client=mock_client)
|
|
assert len(result) == 1
|
|
# Should handle invalid types gracefully
|
|
|
|
|
|
def test_body_battery_data_get_validation_error():
|
|
"""Test handling of validation errors during object creation."""
|
|
mock_client = MagicMock()
|
|
mock_client.connectapi.return_value = [
|
|
{
|
|
"event": {
|
|
"eventType": "sleep",
|
|
"eventStartTimeGmt": "2023-07-20T10:00:00.000Z",
|
|
# Missing required fields for BodyBatteryEvent
|
|
},
|
|
# Missing required fields for BodyBatteryData
|
|
}
|
|
]
|
|
|
|
result = BodyBatteryData.get("2023-07-20", client=mock_client)
|
|
# Should handle validation errors and continue processing
|
|
assert isinstance(result, list)
|
|
assert len(result) == 1 # Should create object with missing fields as None
|
|
assert result[0].event is not None # Event should be created
|
|
assert result[0].activity_name is None # Missing fields should be None
|
|
|
|
|
|
def test_body_battery_data_get_mixed_valid_invalid():
|
|
"""Test processing with mix of valid and invalid items."""
|
|
mock_client = MagicMock()
|
|
mock_client.connectapi.return_value = [
|
|
{
|
|
"event": {
|
|
"eventType": "sleep",
|
|
"eventStartTimeGmt": "2023-07-20T10:00:00.000Z",
|
|
"timezoneOffset": -25200000,
|
|
"durationInMilliseconds": 28800000,
|
|
"bodyBatteryImpact": 35,
|
|
"feedbackType": "good_sleep",
|
|
"shortFeedback": "Good sleep",
|
|
},
|
|
"activityName": None,
|
|
"activityType": None,
|
|
"activityId": None,
|
|
"averageStress": 15.5,
|
|
"stressValuesArray": [[1689811800000, 12]],
|
|
"bodyBatteryValuesArray": [[1689811800000, "charging", 45, 1.0]],
|
|
},
|
|
{
|
|
# Invalid - missing eventStartTimeGmt
|
|
"event": {"eventType": "sleep"},
|
|
"activityName": "Test",
|
|
},
|
|
]
|
|
|
|
result = BodyBatteryData.get("2023-07-20", client=mock_client)
|
|
# Should process valid items and skip invalid ones
|
|
assert len(result) == 1 # Only the valid item should be processed
|
|
assert result[0].event is not None
|
|
|
|
|
|
def test_body_battery_data_get_unexpected_error():
|
|
"""Test handling of unexpected errors during object creation."""
|
|
mock_client = MagicMock()
|
|
|
|
# Create a special object that raises an exception when accessed
|
|
class ExceptionRaisingDict(dict):
|
|
def get(self, key, default=None):
|
|
if key == "activityName":
|
|
raise RuntimeError("Unexpected error during object creation")
|
|
return super().get(key, default)
|
|
|
|
# Create mock data with problematic item
|
|
mock_response_item = ExceptionRaisingDict(
|
|
{
|
|
"event": {
|
|
"eventType": "sleep",
|
|
"eventStartTimeGmt": "2023-07-20T10:00:00.000Z",
|
|
"timezoneOffset": -25200000,
|
|
"durationInMilliseconds": 28800000,
|
|
"bodyBatteryImpact": 35,
|
|
"feedbackType": "good_sleep",
|
|
"shortFeedback": "Good sleep",
|
|
},
|
|
"activityName": None,
|
|
"activityType": None,
|
|
"activityId": None,
|
|
"averageStress": 15.5,
|
|
"stressValuesArray": [[1689811800000, 12]],
|
|
"bodyBatteryValuesArray": [[1689811800000, "charging", 45, 1.0]],
|
|
}
|
|
)
|
|
|
|
mock_client.connectapi.return_value = [mock_response_item]
|
|
|
|
result = BodyBatteryData.get("2023-07-20", client=mock_client)
|
|
# Should handle unexpected errors and return empty list
|
|
assert result == []
|