Files
DDUp/tests/test_web.py

341 lines
9.8 KiB
Python
Raw Normal View History

2026-01-22 12:57:26 +08:00
"""Web API 测试"""
from datetime import date, timedelta
import pytest
from fastapi.testclient import TestClient
from src.vitals.core import database as db
from src.vitals.core.models import Exercise, Meal, Sleep, Weight, UserConfig
from src.vitals.core.auth import create_token
2026-01-22 12:57:26 +08:00
@pytest.fixture
def client():
"""创建测试客户端(带认证)"""
2026-01-22 12:57:26 +08:00
from src.vitals.web.app import app
# 确保有测试用户
active_user = db.get_active_user()
if not active_user:
# 获取或创建默认用户
users = db.get_users()
if users:
active_user = users[0]
db.set_active_user(active_user.id)
else:
# 创建默认测试用户
from src.vitals.core.auth import hash_password
user_id = db.create_user("test_user", hash_password("test123"))
db.set_active_user(user_id)
active_user = db.get_user_by_id(user_id)
# 创建认证 token
token = create_token(active_user.id, active_user.name, active_user.is_admin)
# 创建带认证 cookie 的客户端
test_client = TestClient(app)
test_client.cookies.set("auth_token", token)
return test_client
2026-01-22 12:57:26 +08:00
@pytest.fixture
def populated_db():
"""填充测试数据"""
today = date.today()
# 用户配置
config = UserConfig(
age=28, gender="male", height=175.0, weight=72.0,
activity_level="moderate", goal="maintain",
)
db.save_config(config)
# 今日数据
db.add_exercise(Exercise(
date=today, type="跑步", duration=30, calories=240, distance=5.0,
))
db.add_meal(Meal(
date=today, meal_type="午餐",
description="米饭+鸡肉", calories=500,
))
db.add_meal(Meal(
date=today, meal_type="早餐",
description="燕麦+鸡蛋", calories=350,
))
db.add_sleep(Sleep(
date=today, duration=7.5, quality=4,
))
db.add_weight(Weight(
date=today, weight_kg=72.5, body_fat_pct=18.5,
))
return today
class TestRootEndpoint:
"""根路径测试"""
def test_root_returns_html(self, client):
"""测试返回 HTML"""
response = client.get("/")
assert response.status_code == 200
assert "text/html" in response.headers["content-type"]
def test_root_contains_dashboard(self, client):
"""测试包含仪表盘内容"""
response = client.get("/")
assert "Vitals" in response.text
assert "健康管理" in response.text
class TestConfigEndpoint:
"""配置接口测试"""
def test_get_config(self, client, populated_db):
"""测试获取配置"""
response = client.get("/api/config")
assert response.status_code == 200
data = response.json()
assert data["age"] == 28
assert data["gender"] == "male"
assert data["bmr"] is not None
assert data["tdee"] is not None
def test_get_config_empty(self, client):
"""测试空配置"""
response = client.get("/api/config")
assert response.status_code == 200
data = response.json()
assert data["activity_level"] == "moderate"
class TestTodayEndpoint:
"""今日概览接口测试"""
def test_get_today(self, client, populated_db):
"""测试获取今日数据"""
response = client.get("/api/today")
assert response.status_code == 200
data = response.json()
assert data["date"] == date.today().isoformat()
assert data["calories_intake"] > 0
assert data["exercise_count"] == 1
def test_today_has_meals(self, client, populated_db):
"""测试包含饮食"""
response = client.get("/api/today")
data = response.json()
assert len(data["meals"]) == 2
assert data["meals"][0]["meal_type"] in ["午餐", "早餐"]
def test_today_has_exercises(self, client, populated_db):
"""测试包含运动"""
response = client.get("/api/today")
data = response.json()
assert len(data["exercises"]) == 1
assert data["exercises"][0]["type"] == "跑步"
def test_today_empty(self, client):
"""测试空数据"""
response = client.get("/api/today")
assert response.status_code == 200
data = response.json()
assert data["calories_intake"] == 0
assert data["exercise_count"] == 0
class TestWeekEndpoint:
"""本周汇总接口测试"""
def test_get_week(self, client, populated_db):
"""测试获取本周数据"""
response = client.get("/api/week")
assert response.status_code == 200
data = response.json()
assert "start_date" in data
assert "end_date" in data
assert "daily_stats" in data
def test_week_has_daily_stats(self, client, populated_db):
"""测试包含每日统计"""
response = client.get("/api/week")
data = response.json()
assert len(data["daily_stats"]) == 7
for stat in data["daily_stats"]:
assert "date" in stat
assert "weekday" in stat
def test_week_date_range(self, client, populated_db):
"""测试日期范围"""
response = client.get("/api/week")
data = response.json()
start = date.fromisoformat(data["start_date"])
end = date.fromisoformat(data["end_date"])
assert (end - start).days == 6
class TestExercisesEndpoint:
"""运动记录接口测试"""
def test_get_exercises(self, client, populated_db):
"""测试获取运动记录"""
response = client.get("/api/exercises")
assert response.status_code == 200
data = response.json()
assert len(data) >= 1
def test_exercises_with_days_param(self, client, populated_db):
"""测试天数参数"""
response = client.get("/api/exercises?days=7")
assert response.status_code == 200
def test_exercises_invalid_days(self, client):
"""测试无效天数"""
response = client.get("/api/exercises?days=0")
assert response.status_code == 422 # Validation error
def test_exercises_empty(self, client):
"""测试空数据"""
response = client.get("/api/exercises")
assert response.status_code == 200
assert response.json() == []
class TestMealsEndpoint:
"""饮食记录接口测试"""
def test_get_meals(self, client, populated_db):
"""测试获取饮食记录"""
response = client.get("/api/meals")
assert response.status_code == 200
data = response.json()
assert len(data) >= 2
def test_meals_structure(self, client, populated_db):
"""测试数据结构"""
response = client.get("/api/meals")
data = response.json()
meal = data[0]
assert "id" in meal
assert "date" in meal
assert "meal_type" in meal
assert "description" in meal
assert "calories" in meal
class TestSleepEndpoint:
"""睡眠记录接口测试"""
def test_get_sleep(self, client, populated_db):
"""测试获取睡眠记录"""
response = client.get("/api/sleep")
assert response.status_code == 200
data = response.json()
assert len(data) >= 1
def test_sleep_structure(self, client, populated_db):
"""测试数据结构"""
response = client.get("/api/sleep")
data = response.json()
record = data[0]
assert "duration" in record
assert "quality" in record
class TestWeightEndpoint:
"""体重记录接口测试"""
def test_get_weight(self, client, populated_db):
"""测试获取体重记录"""
response = client.get("/api/weight")
assert response.status_code == 200
data = response.json()
assert len(data) >= 1
def test_weight_default_days(self, client, populated_db):
"""测试默认天数"""
response = client.get("/api/weight")
assert response.status_code == 200
def test_weight_structure(self, client, populated_db):
"""测试数据结构"""
response = client.get("/api/weight")
data = response.json()
record = data[0]
assert "weight_kg" in record
assert record["weight_kg"] == 72.5
class TestCORS:
"""CORS 测试"""
def test_cors_headers(self, client):
"""测试 CORS 头"""
response = client.options("/api/config")
# FastAPI with CORSMiddleware should handle OPTIONS
assert response.status_code in [200, 405]
class TestErrorHandling:
"""错误处理测试"""
def test_not_found(self, client):
"""测试 404"""
response = client.get("/api/nonexistent")
assert response.status_code == 404
def test_invalid_query_params(self, client):
"""测试无效参数"""
response = client.get("/api/exercises?days=abc")
assert response.status_code == 422
def test_bmi_analysis_endpoint(client):
"""测试 BMI 分析 API"""
response = client.get("/api/bmi/analysis")
assert response.status_code == 200
data = response.json()
assert "current_weight" in data
assert "current_bmi" in data
assert "bmi_status" in data
assert "target_weight" in data
assert "estimated_days" in data
def test_nutrition_recommendations_endpoint(client):
"""测试营养建议 API"""
response = client.get("/api/nutrition/recommendations")
assert response.status_code == 200
data = response.json()
assert "daily_targets" in data
assert "today_intake" in data
assert "gaps" in data
assert "suggestions" in data
def test_sleep_assessment_endpoint(client):
"""测试睡眠评估 API"""
response = client.get("/api/sleep/assessment")
assert response.status_code == 200
data = response.json()
assert "avg_duration" in data
assert "status" in data
assert "health_impacts" in data
assert "ideal_days_count" in data