159 lines
3.9 KiB
Python
159 lines
3.9 KiB
Python
import pytest
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
from app.db.base import Base
|
|
from app.db.models import BodyMetric, FitnessProfile, User
|
|
from app.fitness.body_composition import (
|
|
compute_body_composition,
|
|
ffmi,
|
|
lean_body_mass,
|
|
navy_body_fat_pct,
|
|
whr,
|
|
)
|
|
|
|
|
|
def test_navy_male_reasonable_range():
|
|
bf = navy_body_fat_pct(
|
|
sex="male",
|
|
height_cm=180,
|
|
neck_cm=38,
|
|
waist_cm=84,
|
|
)
|
|
assert bf == pytest.approx(15.3)
|
|
|
|
|
|
def test_navy_female_requires_hip():
|
|
assert navy_body_fat_pct(
|
|
sex="female",
|
|
height_cm=165,
|
|
neck_cm=34,
|
|
waist_cm=72,
|
|
hip_cm=None,
|
|
) is None
|
|
|
|
bf = navy_body_fat_pct(
|
|
sex="female",
|
|
height_cm=165,
|
|
neck_cm=34,
|
|
waist_cm=72,
|
|
hip_cm=98,
|
|
)
|
|
assert bf == pytest.approx(26.4)
|
|
|
|
|
|
def test_navy_invalid_waist_neck():
|
|
assert navy_body_fat_pct(
|
|
sex="male",
|
|
height_cm=180,
|
|
neck_cm=40,
|
|
waist_cm=39,
|
|
) is None
|
|
|
|
|
|
def test_whr():
|
|
assert whr(80, 100) == 0.8
|
|
|
|
|
|
def test_lean_body_mass_and_ffmi():
|
|
lbm = lean_body_mass(80, 20)
|
|
assert lbm == 64.0
|
|
score = ffmi(80, 180, 20)
|
|
assert score is not None
|
|
assert 15 <= score <= 25
|
|
|
|
|
|
def test_compute_manual_body_fat():
|
|
result = compute_body_composition(
|
|
sex="male",
|
|
height_cm=180,
|
|
weight_kg=80,
|
|
body_fat_pct=18,
|
|
waist_cm=84,
|
|
hip_cm=100,
|
|
)
|
|
assert result["body_fat_pct"] == 18.0
|
|
assert result["body_fat_method"] == "manual"
|
|
assert result["whr"] == 0.84
|
|
assert result["lbm_kg"] == 65.6
|
|
assert result["ffmi"] is not None
|
|
|
|
|
|
def test_compute_navy_auto():
|
|
result = compute_body_composition(
|
|
sex="male",
|
|
height_cm=180,
|
|
weight_kg=82,
|
|
neck_cm=38,
|
|
waist_cm=84,
|
|
)
|
|
assert result["body_fat_pct"] is not None
|
|
assert result["body_fat_method"] == "navy"
|
|
assert result["lbm_kg"] is not None
|
|
|
|
|
|
def test_compute_female_warning_without_hip():
|
|
result = compute_body_composition(
|
|
sex="female",
|
|
height_cm=165,
|
|
weight_kg=60,
|
|
neck_cm=34,
|
|
waist_cm=72,
|
|
)
|
|
assert result["body_fat_pct"] is None
|
|
assert any("бёдер" in w for w in result["warnings"])
|
|
|
|
|
|
def test_update_body_metric_recomputes_composition_and_latest_profile_weight():
|
|
from app.fitness.service import FitnessService
|
|
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(engine)
|
|
Session = sessionmaker(bind=engine)
|
|
with Session() as db:
|
|
user = User(username="tester", api_token_hash="x")
|
|
db.add(user)
|
|
db.flush()
|
|
db.add(
|
|
FitnessProfile(
|
|
user_id=user.id,
|
|
sex="male",
|
|
age=30,
|
|
height_cm=180,
|
|
weight_kg=82,
|
|
goal="maintain",
|
|
)
|
|
)
|
|
metric = BodyMetric(
|
|
user_id=user.id,
|
|
weight_kg=82,
|
|
body_fat_pct=9.0,
|
|
body_fat_method="navy",
|
|
waist_cm=84,
|
|
neck_cm=38,
|
|
)
|
|
db.add(metric)
|
|
db.commit()
|
|
metric_id = metric.id
|
|
user_id = user.id
|
|
|
|
result = FitnessService(db, user_id).update_body_metric(
|
|
metric_id,
|
|
{"weight_kg": 83, "waist_cm": 86, "neck_cm": 38},
|
|
)
|
|
|
|
assert result is not None
|
|
expected = compute_body_composition(
|
|
sex="male",
|
|
height_cm=180,
|
|
weight_kg=83,
|
|
waist_cm=86,
|
|
neck_cm=38,
|
|
)
|
|
assert result["metric"]["weight_kg"] == 83
|
|
assert result["metric"]["body_fat_method"] == "navy"
|
|
assert result["metric"]["body_fat_pct"] == expected["body_fat_pct"]
|
|
assert result["metric"]["lbm_kg"] == expected["lbm_kg"]
|
|
assert result["metric"]["ffmi"] == expected["ffmi"]
|
|
assert db.query(FitnessProfile).one().weight_kg == 83
|