2026-07-17 18:41:37 +02:00
|
|
|
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)
|
|
|
|
|
}
|
2026-07-17 19:06:01 +02:00
|
|
|
if p.HRZone1MinPct != 50 || p.HRZone5MaxPct != 100 {
|
|
|
|
|
t.Errorf("zone defaults = %+v, want Z1 min=50, Z5 max=100", p)
|
2026-07-17 18:41:37 +02:00
|
|
|
}
|
2026-07-18 08:53:21 +02:00
|
|
|
if p.WarmupMinutes != 10 || p.CooldownMinutes != 5 {
|
|
|
|
|
t.Errorf("phase-minute defaults = %+v, want warmup=10, cooldown=5", p)
|
|
|
|
|
}
|
2026-07-19 12:57:18 +02:00
|
|
|
if p.BackfillHorizonDays != 1095 {
|
|
|
|
|
t.Errorf("BackfillHorizonDays = %d, want 1095 (migration default)", p.BackfillHorizonDays)
|
|
|
|
|
}
|
2026-07-19 17:38:50 +02:00
|
|
|
if p.MinRepresentativePaceSecPerKm != 720 || p.MinRepresentativeTimeSeconds != 3 {
|
|
|
|
|
t.Errorf("pace artifact filter defaults = %+v, want pace=720, time=3", p)
|
|
|
|
|
}
|
Dedup Garmin data storage, configurable chart colors, taxonomy fixes, sync/progression fixes
- Remove duplicated Garmin fields from storage; decode display-only fields
(activity name/type, lap duration/HR, structured workout raw JSON) from
RawJSON at API-response time instead of storing redundant columns
- Add a fully configurable chart color system (pace/HR main-line colors, 4
effort-kind colors, tint/darken/brighten intensity knobs) under Profile >
Chart colors
- Rename training types and fix their display order (Easy, Long, 60'/30'
Threshold, Tempo, Intervals, MAS Test, Race) everywhere they're listed
- Add an Efficiency Factor progression metric; fix Progression chart axes to
use tight non-zero-based domains, m:ss/km pace formatting, and rounded
ticks instead of raw floating-point labels
- Expose the raw get_workout_by_id() payload in the raw-data viewer
alongside activity/lap/detail JSON; enlarge the modal and shrink array
indentation for readability
- Fix "last sync" reporting a meaningless activity count: record one
combined sync run per manual "Sync now" and count genuinely new
activities instead of re-listing whatever Garmin returned for the queried
window
- Let a Review Queue activity be manually cleared back to Unclassified, and
make "Reset all" available even while disconnected from Garmin
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 06:33:47 +02:00
|
|
|
if p.Name != "Default" {
|
|
|
|
|
t.Errorf("Name = %q, want %q (migration default)", p.Name, "Default")
|
|
|
|
|
}
|
|
|
|
|
if p.PaceColor != "#3b82f6" || p.HeartRateColor != "#ef4444" {
|
|
|
|
|
t.Errorf("main line color defaults = %+v, want pace=#3b82f6, heartRate=#ef4444", p)
|
|
|
|
|
}
|
|
|
|
|
if p.WarmupColor != "#c2410c" || p.EffortColor != "#7c3aed" || p.RecoveryColor != "#15803d" || p.CooldownColor != "#fb923c" {
|
|
|
|
|
t.Errorf("effort color defaults = %+v", p)
|
|
|
|
|
}
|
|
|
|
|
if p.MainLineTintPct != 20 {
|
|
|
|
|
t.Errorf("MainLineTintPct = %v, want 20 (migration default)", p.MainLineTintPct)
|
|
|
|
|
}
|
|
|
|
|
if p.BackgroundDarkenPct != 35 {
|
|
|
|
|
t.Errorf("BackgroundDarkenPct = %v, want 35 (migration default)", p.BackgroundDarkenPct)
|
|
|
|
|
}
|
|
|
|
|
if p.TargetBrightenPct != 20 {
|
|
|
|
|
t.Errorf("TargetBrightenPct = %v, want 20 (migration default)", p.TargetBrightenPct)
|
|
|
|
|
}
|
2026-07-17 18:41:37 +02:00
|
|
|
|
|
|
|
|
maxHR, restingHR := 190.0, 50.0
|
Dedup Garmin data storage, configurable chart colors, taxonomy fixes, sync/progression fixes
- Remove duplicated Garmin fields from storage; decode display-only fields
(activity name/type, lap duration/HR, structured workout raw JSON) from
RawJSON at API-response time instead of storing redundant columns
- Add a fully configurable chart color system (pace/HR main-line colors, 4
effort-kind colors, tint/darken/brighten intensity knobs) under Profile >
Chart colors
- Rename training types and fix their display order (Easy, Long, 60'/30'
Threshold, Tempo, Intervals, MAS Test, Race) everywhere they're listed
- Add an Efficiency Factor progression metric; fix Progression chart axes to
use tight non-zero-based domains, m:ss/km pace formatting, and rounded
ticks instead of raw floating-point labels
- Expose the raw get_workout_by_id() payload in the raw-data viewer
alongside activity/lap/detail JSON; enlarge the modal and shrink array
indentation for readability
- Fix "last sync" reporting a meaningless activity count: record one
combined sync run per manual "Sync now" and count genuinely new
activities instead of re-listing whatever Garmin returned for the queried
window
- Let a Review Queue activity be manually cleared back to Unclassified, and
make "Reset all" available even while disconnected from Garmin
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 06:33:47 +02:00
|
|
|
p.Name = "Kriss"
|
|
|
|
|
p.PaceColor = "#111111"
|
|
|
|
|
p.EffortColor = "#222222"
|
|
|
|
|
p.MainLineTintPct = 45
|
|
|
|
|
p.BackgroundDarkenPct = 60
|
|
|
|
|
p.TargetBrightenPct = 50
|
2026-07-17 18:41:37 +02:00
|
|
|
p.GarminEmail = "runner@example.com"
|
|
|
|
|
p.GarminPassword = "hunter2"
|
|
|
|
|
p.RollingWindowDays = 120
|
|
|
|
|
p.MaxHeartRate = &maxHR
|
|
|
|
|
p.RestingHeartRate = &restingHR
|
2026-07-18 08:53:21 +02:00
|
|
|
p.WarmupMinutes = 8
|
2026-07-19 12:57:18 +02:00
|
|
|
p.BackfillHorizonDays = 14
|
2026-07-19 17:38:50 +02:00
|
|
|
p.MinRepresentativePaceSecPerKm = 600
|
|
|
|
|
p.MinRepresentativeTimeSeconds = 5
|
2026-07-17 18:41:37 +02:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
Dedup Garmin data storage, configurable chart colors, taxonomy fixes, sync/progression fixes
- Remove duplicated Garmin fields from storage; decode display-only fields
(activity name/type, lap duration/HR, structured workout raw JSON) from
RawJSON at API-response time instead of storing redundant columns
- Add a fully configurable chart color system (pace/HR main-line colors, 4
effort-kind colors, tint/darken/brighten intensity knobs) under Profile >
Chart colors
- Rename training types and fix their display order (Easy, Long, 60'/30'
Threshold, Tempo, Intervals, MAS Test, Race) everywhere they're listed
- Add an Efficiency Factor progression metric; fix Progression chart axes to
use tight non-zero-based domains, m:ss/km pace formatting, and rounded
ticks instead of raw floating-point labels
- Expose the raw get_workout_by_id() payload in the raw-data viewer
alongside activity/lap/detail JSON; enlarge the modal and shrink array
indentation for readability
- Fix "last sync" reporting a meaningless activity count: record one
combined sync run per manual "Sync now" and count genuinely new
activities instead of re-listing whatever Garmin returned for the queried
window
- Let a Review Queue activity be manually cleared back to Unclassified, and
make "Reset all" available even while disconnected from Garmin
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 06:33:47 +02:00
|
|
|
if got.Name != "Kriss" {
|
|
|
|
|
t.Errorf("Name = %q, want %q after update", got.Name, "Kriss")
|
|
|
|
|
}
|
2026-07-17 18:41:37 +02:00
|
|
|
if got.MaxHeartRate == nil || *got.MaxHeartRate != 190 {
|
|
|
|
|
t.Errorf("MaxHeartRate = %v, want 190", got.MaxHeartRate)
|
|
|
|
|
}
|
2026-07-18 08:53:21 +02:00
|
|
|
if got.WarmupMinutes != 8 {
|
|
|
|
|
t.Errorf("WarmupMinutes = %v, want 8", got.WarmupMinutes)
|
2026-07-17 18:41:37 +02:00
|
|
|
}
|
2026-07-19 12:57:18 +02:00
|
|
|
if got.BackfillHorizonDays != 14 {
|
|
|
|
|
t.Errorf("BackfillHorizonDays = %v, want 14", got.BackfillHorizonDays)
|
|
|
|
|
}
|
2026-07-19 17:38:50 +02:00
|
|
|
if got.MinRepresentativePaceSecPerKm != 600 || got.MinRepresentativeTimeSeconds != 5 {
|
|
|
|
|
t.Errorf("pace artifact filter after update = %+v, want pace=600, time=5", got)
|
|
|
|
|
}
|
Dedup Garmin data storage, configurable chart colors, taxonomy fixes, sync/progression fixes
- Remove duplicated Garmin fields from storage; decode display-only fields
(activity name/type, lap duration/HR, structured workout raw JSON) from
RawJSON at API-response time instead of storing redundant columns
- Add a fully configurable chart color system (pace/HR main-line colors, 4
effort-kind colors, tint/darken/brighten intensity knobs) under Profile >
Chart colors
- Rename training types and fix their display order (Easy, Long, 60'/30'
Threshold, Tempo, Intervals, MAS Test, Race) everywhere they're listed
- Add an Efficiency Factor progression metric; fix Progression chart axes to
use tight non-zero-based domains, m:ss/km pace formatting, and rounded
ticks instead of raw floating-point labels
- Expose the raw get_workout_by_id() payload in the raw-data viewer
alongside activity/lap/detail JSON; enlarge the modal and shrink array
indentation for readability
- Fix "last sync" reporting a meaningless activity count: record one
combined sync run per manual "Sync now" and count genuinely new
activities instead of re-listing whatever Garmin returned for the queried
window
- Let a Review Queue activity be manually cleared back to Unclassified, and
make "Reset all" available even while disconnected from Garmin
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 06:33:47 +02:00
|
|
|
if got.PaceColor != "#111111" || got.EffortColor != "#222222" {
|
|
|
|
|
t.Errorf("chart colors after update = %+v, want pace=#111111, effort=#222222", got)
|
|
|
|
|
}
|
|
|
|
|
if got.MainLineTintPct != 45 {
|
|
|
|
|
t.Errorf("MainLineTintPct after update = %v, want 45", got.MainLineTintPct)
|
|
|
|
|
}
|
|
|
|
|
if got.BackgroundDarkenPct != 60 {
|
|
|
|
|
t.Errorf("BackgroundDarkenPct after update = %v, want 60", got.BackgroundDarkenPct)
|
|
|
|
|
}
|
|
|
|
|
if got.TargetBrightenPct != 50 {
|
|
|
|
|
t.Errorf("TargetBrightenPct after update = %v, want 50", got.TargetBrightenPct)
|
|
|
|
|
}
|
2026-07-17 18:41:37 +02:00
|
|
|
}
|