Two new Profile settings -- "minimum representative pace" and "minimum representative time" -- replace the previous hardcoded 20:00/km cutoff. A stretch of consecutive samples slower than the configured pace is now dropped from the chart (and its Y-axis scale) only if it lasts no longer than the configured time; a longer stretch is kept as a real stop or walk break rather than noise. Defaults to 12:00/km and 3 seconds. Caught a boundary bug while verifying against real data: a run lasting exactly the threshold duration survived filtering because the comparison used strict "<" instead of "<=", contradicting "not lasting more than N seconds" (which should include exactly N).
67 lines
2.0 KiB
Go
67 lines
2.0 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
)
|
|
|
|
func TestProfile_DefaultsThenUpdate(t *testing.T) {
|
|
db := openTestDB(t)
|
|
ctx := context.Background()
|
|
|
|
p, err := db.GetProfile(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetProfile: %v", err)
|
|
}
|
|
if p.RollingWindowDays != 90 {
|
|
t.Errorf("RollingWindowDays = %d, want 90 (migration default)", p.RollingWindowDays)
|
|
}
|
|
if p.HRZone1MinPct != 50 || p.HRZone5MaxPct != 100 {
|
|
t.Errorf("zone defaults = %+v, want Z1 min=50, Z5 max=100", p)
|
|
}
|
|
if p.WarmupMinutes != 10 || p.CooldownMinutes != 5 {
|
|
t.Errorf("phase-minute defaults = %+v, want warmup=10, cooldown=5", p)
|
|
}
|
|
if p.BackfillHorizonDays != 1095 {
|
|
t.Errorf("BackfillHorizonDays = %d, want 1095 (migration default)", p.BackfillHorizonDays)
|
|
}
|
|
if p.MinRepresentativePaceSecPerKm != 720 || p.MinRepresentativeTimeSeconds != 3 {
|
|
t.Errorf("pace artifact filter defaults = %+v, want pace=720, time=3", p)
|
|
}
|
|
|
|
maxHR, restingHR := 190.0, 50.0
|
|
p.GarminEmail = "runner@example.com"
|
|
p.GarminPassword = "hunter2"
|
|
p.RollingWindowDays = 120
|
|
p.MaxHeartRate = &maxHR
|
|
p.RestingHeartRate = &restingHR
|
|
p.WarmupMinutes = 8
|
|
p.BackfillHorizonDays = 14
|
|
p.MinRepresentativePaceSecPerKm = 600
|
|
p.MinRepresentativeTimeSeconds = 5
|
|
|
|
if err := db.UpdateProfile(ctx, p); err != nil {
|
|
t.Fatalf("UpdateProfile: %v", err)
|
|
}
|
|
|
|
got, err := db.GetProfile(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetProfile after update: %v", err)
|
|
}
|
|
if got.GarminEmail != "runner@example.com" || got.RollingWindowDays != 120 {
|
|
t.Errorf("got = %+v, want updated email/window", got)
|
|
}
|
|
if got.MaxHeartRate == nil || *got.MaxHeartRate != 190 {
|
|
t.Errorf("MaxHeartRate = %v, want 190", got.MaxHeartRate)
|
|
}
|
|
if got.WarmupMinutes != 8 {
|
|
t.Errorf("WarmupMinutes = %v, want 8", got.WarmupMinutes)
|
|
}
|
|
if got.BackfillHorizonDays != 14 {
|
|
t.Errorf("BackfillHorizonDays = %v, want 14", got.BackfillHorizonDays)
|
|
}
|
|
if got.MinRepresentativePaceSecPerKm != 600 || got.MinRepresentativeTimeSeconds != 5 {
|
|
t.Errorf("pace artifact filter after update = %+v, want pace=600, time=5", got)
|
|
}
|
|
}
|