feat: replace age integer with date_of_birth across backend and client

Store date_of_birth (DATE) instead of a static age integer so that age
is always computed dynamically from the stored date of birth.

- Migration 011: adds date_of_birth, backfills from age, drops age
- AgeFromDOB helper computes current age from YYYY-MM-DD string
- User model, repository SQL, and service validation updated
- Flutter: User.age becomes a computed getter; profile edit screen
  uses a date picker bounded to [today-120y, today-10y]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dbastrikin
2026-03-09 23:37:58 +02:00
parent 765346d4e4
commit c9ddb708b1
13 changed files with 202 additions and 73 deletions

View File

@@ -94,19 +94,20 @@ func TestUpdateProfile_NameOnly(t *testing.T) {
}
func TestUpdateProfile_WithCaloriesRecalculation(t *testing.T) {
dob := time.Now().AddDate(-30, 0, 0).Format("2006-01-02")
profileReq := UpdateProfileRequest{
HeightCM: ptrInt(180),
WeightKG: ptrFloat(80),
Age: ptrInt(30),
Gender: ptrStr("male"),
Activity: ptrStr("moderate"),
Goal: ptrStr("maintain"),
HeightCM: ptrInt(180),
WeightKG: ptrFloat(80),
DateOfBirth: &dob,
Gender: ptrStr("male"),
Activity: ptrStr("moderate"),
Goal: ptrStr("maintain"),
}
finalUser := &User{
ID: "user-1",
HeightCM: ptrInt(180),
WeightKG: ptrFloat(80),
Age: ptrInt(30),
DateOfBirth: &dob,
Gender: ptrStr("male"),
Activity: ptrStr("moderate"),
Goal: ptrStr("maintain"),
@@ -178,19 +179,31 @@ func TestUpdateProfile_InvalidWeight_TooHigh(t *testing.T) {
}
}
func TestUpdateProfile_InvalidAge_TooLow(t *testing.T) {
func TestUpdateProfile_InvalidDOB_TooRecent(t *testing.T) {
svc := NewService(&mockUserRepo{})
dob := time.Now().AddDate(-5, 0, 0).Format("2006-01-02")
_, err := svc.UpdateProfile(context.Background(), "user-1", UpdateProfileRequest{Age: ptrInt(5)})
_, err := svc.UpdateProfile(context.Background(), "user-1", UpdateProfileRequest{DateOfBirth: &dob})
if err == nil {
t.Fatal("expected validation error")
}
}
func TestUpdateProfile_InvalidAge_TooHigh(t *testing.T) {
func TestUpdateProfile_InvalidDOB_TooOld(t *testing.T) {
svc := NewService(&mockUserRepo{})
dob := time.Now().AddDate(-150, 0, 0).Format("2006-01-02")
_, err := svc.UpdateProfile(context.Background(), "user-1", UpdateProfileRequest{Age: ptrInt(150)})
_, err := svc.UpdateProfile(context.Background(), "user-1", UpdateProfileRequest{DateOfBirth: &dob})
if err == nil {
t.Fatal("expected validation error")
}
}
func TestUpdateProfile_InvalidDOB_BadFormat(t *testing.T) {
svc := NewService(&mockUserRepo{})
dob := "not-a-date"
_, err := svc.UpdateProfile(context.Background(), "user-1", UpdateProfileRequest{DateOfBirth: &dob})
if err == nil {
t.Fatal("expected validation error")
}