268 lines
7.3 KiB
Python
268 lines
7.3 KiB
Python
|
|
"""数据模型测试"""
|
||
|
|
|
||
|
|
from datetime import date, time
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from src.vitals.core.models import (
|
||
|
|
Exercise,
|
||
|
|
Meal,
|
||
|
|
Sleep,
|
||
|
|
Weight,
|
||
|
|
UserConfig,
|
||
|
|
MealType,
|
||
|
|
ExerciseType,
|
||
|
|
ActivityLevel,
|
||
|
|
Goal,
|
||
|
|
User,
|
||
|
|
DataClearRequest,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class TestExercise:
|
||
|
|
"""运动记录模型测试"""
|
||
|
|
|
||
|
|
def test_create_exercise(self):
|
||
|
|
"""测试创建运动记录"""
|
||
|
|
exercise = Exercise(
|
||
|
|
date=date(2026, 1, 18),
|
||
|
|
type="跑步",
|
||
|
|
duration=30,
|
||
|
|
calories=240,
|
||
|
|
)
|
||
|
|
assert exercise.type == "跑步"
|
||
|
|
assert exercise.duration == 30
|
||
|
|
assert exercise.calories == 240
|
||
|
|
|
||
|
|
def test_exercise_to_dict(self, sample_exercise):
|
||
|
|
"""测试转换为字典"""
|
||
|
|
data = sample_exercise.to_dict()
|
||
|
|
assert data["type"] == "跑步"
|
||
|
|
assert data["duration"] == 30
|
||
|
|
assert data["distance"] == 5.0
|
||
|
|
assert data["date"] == "2026-01-18"
|
||
|
|
|
||
|
|
def test_exercise_default_values(self):
|
||
|
|
"""测试默认值"""
|
||
|
|
exercise = Exercise()
|
||
|
|
assert exercise.duration == 0
|
||
|
|
assert exercise.calories == 0
|
||
|
|
assert exercise.source == "手动"
|
||
|
|
|
||
|
|
|
||
|
|
class TestMeal:
|
||
|
|
"""饮食记录模型测试"""
|
||
|
|
|
||
|
|
def test_create_meal(self):
|
||
|
|
"""测试创建饮食记录"""
|
||
|
|
meal = Meal(
|
||
|
|
meal_type="早餐",
|
||
|
|
description="燕麦+鸡蛋",
|
||
|
|
calories=350,
|
||
|
|
)
|
||
|
|
assert meal.meal_type == "早餐"
|
||
|
|
assert meal.calories == 350
|
||
|
|
|
||
|
|
def test_meal_to_dict(self, sample_meal):
|
||
|
|
"""测试转换为字典"""
|
||
|
|
data = sample_meal.to_dict()
|
||
|
|
assert data["meal_type"] == "午餐"
|
||
|
|
assert data["calories"] == 650
|
||
|
|
assert data["protein"] == 25.0
|
||
|
|
|
||
|
|
def test_meal_with_food_items(self):
|
||
|
|
"""测试带食物条目的饮食记录"""
|
||
|
|
meal = Meal(
|
||
|
|
meal_type="午餐",
|
||
|
|
description="测试",
|
||
|
|
food_items=[{"name": "米饭", "calories": 200}],
|
||
|
|
)
|
||
|
|
data = meal.to_dict()
|
||
|
|
assert "米饭" in data["food_items"]
|
||
|
|
|
||
|
|
|
||
|
|
class TestSleep:
|
||
|
|
"""睡眠记录模型测试"""
|
||
|
|
|
||
|
|
def test_create_sleep(self):
|
||
|
|
"""测试创建睡眠记录"""
|
||
|
|
sleep = Sleep(
|
||
|
|
bedtime=time(23, 0),
|
||
|
|
wake_time=time(7, 0),
|
||
|
|
duration=8.0,
|
||
|
|
quality=4,
|
||
|
|
)
|
||
|
|
assert sleep.duration == 8.0
|
||
|
|
assert sleep.quality == 4
|
||
|
|
|
||
|
|
def test_sleep_to_dict(self, sample_sleep):
|
||
|
|
"""测试转换为字典"""
|
||
|
|
data = sample_sleep.to_dict()
|
||
|
|
assert data["duration"] == 7.5
|
||
|
|
assert data["quality"] == 4
|
||
|
|
assert data["bedtime"] == "23:30:00"
|
||
|
|
|
||
|
|
|
||
|
|
class TestWeight:
|
||
|
|
"""体重记录模型测试"""
|
||
|
|
|
||
|
|
def test_create_weight(self):
|
||
|
|
"""测试创建体重记录"""
|
||
|
|
weight = Weight(weight_kg=72.5)
|
||
|
|
assert weight.weight_kg == 72.5
|
||
|
|
|
||
|
|
def test_weight_to_dict(self, sample_weight):
|
||
|
|
"""测试转换为字典"""
|
||
|
|
data = sample_weight.to_dict()
|
||
|
|
assert data["weight_kg"] == 72.5
|
||
|
|
assert data["body_fat_pct"] == 18.5
|
||
|
|
|
||
|
|
|
||
|
|
class TestUserConfig:
|
||
|
|
"""用户配置模型测试"""
|
||
|
|
|
||
|
|
def test_bmr_calculation_male(self):
|
||
|
|
"""测试男性 BMR 计算"""
|
||
|
|
config = UserConfig(
|
||
|
|
age=28,
|
||
|
|
gender="male",
|
||
|
|
height=175.0,
|
||
|
|
weight=72.0,
|
||
|
|
)
|
||
|
|
# Mifflin-St Jeor: 10 * 72 + 6.25 * 175 - 5 * 28 + 5 = 1678
|
||
|
|
assert config.bmr == 1678
|
||
|
|
|
||
|
|
def test_bmr_calculation_female(self):
|
||
|
|
"""测试女性 BMR 计算"""
|
||
|
|
config = UserConfig(
|
||
|
|
age=25,
|
||
|
|
gender="female",
|
||
|
|
height=165.0,
|
||
|
|
weight=55.0,
|
||
|
|
)
|
||
|
|
# Mifflin-St Jeor: 10 * 55 + 6.25 * 165 - 5 * 25 - 161 = 1295
|
||
|
|
assert config.bmr == 1295
|
||
|
|
|
||
|
|
def test_bmr_none_when_incomplete(self):
|
||
|
|
"""测试不完整时 BMR 为 None"""
|
||
|
|
config = UserConfig(age=28)
|
||
|
|
assert config.bmr is None
|
||
|
|
|
||
|
|
def test_tdee_calculation(self, sample_config):
|
||
|
|
"""测试 TDEE 计算"""
|
||
|
|
# BMR * 1.55 (moderate)
|
||
|
|
expected_tdee = int(sample_config.bmr * 1.55)
|
||
|
|
assert sample_config.tdee == expected_tdee
|
||
|
|
|
||
|
|
def test_tdee_different_activity_levels(self):
|
||
|
|
"""测试不同活动水平的 TDEE"""
|
||
|
|
base_config = {
|
||
|
|
"age": 30,
|
||
|
|
"gender": "male",
|
||
|
|
"height": 175.0,
|
||
|
|
"weight": 70.0,
|
||
|
|
}
|
||
|
|
|
||
|
|
sedentary = UserConfig(**base_config, activity_level="sedentary")
|
||
|
|
active = UserConfig(**base_config, activity_level="active")
|
||
|
|
|
||
|
|
assert sedentary.tdee < active.tdee
|
||
|
|
|
||
|
|
def test_config_to_dict(self, sample_config):
|
||
|
|
"""测试转换为字典"""
|
||
|
|
data = sample_config.to_dict()
|
||
|
|
assert data["age"] == 28
|
||
|
|
assert data["gender"] == "male"
|
||
|
|
assert data["bmr"] is not None
|
||
|
|
assert data["tdee"] is not None
|
||
|
|
|
||
|
|
|
||
|
|
class TestEnums:
|
||
|
|
"""枚举类型测试"""
|
||
|
|
|
||
|
|
def test_meal_type_values(self):
|
||
|
|
"""测试餐次类型"""
|
||
|
|
assert MealType.BREAKFAST.value == "早餐"
|
||
|
|
assert MealType.LUNCH.value == "午餐"
|
||
|
|
assert MealType.DINNER.value == "晚餐"
|
||
|
|
assert MealType.SNACK.value == "加餐"
|
||
|
|
|
||
|
|
def test_exercise_type_values(self):
|
||
|
|
"""测试运动类型"""
|
||
|
|
assert ExerciseType.RUNNING.value == "跑步"
|
||
|
|
assert ExerciseType.SWIMMING.value == "游泳"
|
||
|
|
|
||
|
|
def test_activity_level_values(self):
|
||
|
|
"""测试活动水平"""
|
||
|
|
assert ActivityLevel.SEDENTARY.value == "sedentary"
|
||
|
|
assert ActivityLevel.MODERATE.value == "moderate"
|
||
|
|
|
||
|
|
def test_goal_values(self):
|
||
|
|
"""测试目标"""
|
||
|
|
assert Goal.LOSE.value == "lose"
|
||
|
|
assert Goal.MAINTAIN.value == "maintain"
|
||
|
|
assert Goal.GAIN.value == "gain"
|
||
|
|
|
||
|
|
|
||
|
|
class TestUser:
|
||
|
|
"""用户模型测试"""
|
||
|
|
|
||
|
|
def test_user_creation(self):
|
||
|
|
"""测试创建用户"""
|
||
|
|
from datetime import datetime
|
||
|
|
user = User(
|
||
|
|
name="小明",
|
||
|
|
created_at=datetime.now(),
|
||
|
|
)
|
||
|
|
assert user.name == "小明"
|
||
|
|
assert user.is_active == False
|
||
|
|
assert user.id is None
|
||
|
|
|
||
|
|
def test_user_to_dict(self):
|
||
|
|
"""测试用户转换为字典"""
|
||
|
|
from datetime import datetime
|
||
|
|
user = User(
|
||
|
|
id=1,
|
||
|
|
name="小红",
|
||
|
|
created_at=datetime(2026, 1, 19, 10, 0, 0),
|
||
|
|
is_active=True,
|
||
|
|
)
|
||
|
|
d = user.to_dict()
|
||
|
|
assert d["id"] == 1
|
||
|
|
assert d["name"] == "小红"
|
||
|
|
assert d["is_active"] == True
|
||
|
|
assert "created_at" in d
|
||
|
|
|
||
|
|
|
||
|
|
class TestDataClearRequest:
|
||
|
|
"""数据清除请求模型测试"""
|
||
|
|
|
||
|
|
def test_clear_by_range(self):
|
||
|
|
"""测试按时间范围清除"""
|
||
|
|
req = DataClearRequest(
|
||
|
|
user_id=1,
|
||
|
|
mode="range",
|
||
|
|
date_from=date(2026, 1, 1),
|
||
|
|
date_to=date(2026, 1, 15),
|
||
|
|
)
|
||
|
|
assert req.mode == "range"
|
||
|
|
assert req.date_from == date(2026, 1, 1)
|
||
|
|
|
||
|
|
def test_clear_by_type(self):
|
||
|
|
"""测试按类型清除"""
|
||
|
|
req = DataClearRequest(
|
||
|
|
user_id=1,
|
||
|
|
mode="type",
|
||
|
|
data_types=["exercise", "meal"],
|
||
|
|
)
|
||
|
|
assert req.mode == "type"
|
||
|
|
assert "exercise" in req.data_types
|
||
|
|
|
||
|
|
def test_clear_all(self):
|
||
|
|
"""测试清除全部"""
|
||
|
|
req = DataClearRequest(
|
||
|
|
user_id=1,
|
||
|
|
mode="all",
|
||
|
|
)
|
||
|
|
assert req.mode == "all"
|