package official_climb import ( "testing" "time" "github.com/martinlehoux/biking_home/climb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestMedianOddAndEven(t *testing.T) { assert.Equal(t, 3.0, Median([]float64{5, 1, 3})) assert.Equal(t, 2.5, Median([]float64{1, 2, 3, 4})) assert.Equal(t, 0.0, Median(nil)) } func TestAggregateAttemptsUsesMedians(t *testing.T) { attempts := []Attempt{ {DistanceM: 3000, ElevationGainM: 200, SlopePercent: 6.7, Cotacol: 80, StartedAt: time.Date(2026, 1, 1, 8, 0, 0, 0, time.UTC), EndedAt: time.Date(2026, 1, 1, 8, 20, 0, 0, time.UTC)}, {DistanceM: 3200, ElevationGainM: 210, SlopePercent: 6.6, Cotacol: 84, StartedAt: time.Date(2026, 2, 1, 8, 0, 0, 0, time.UTC), EndedAt: time.Date(2026, 2, 1, 8, 15, 0, 0, time.UTC)}, {DistanceM: 6000, ElevationGainM: 900, SlopePercent: 15, Cotacol: 400, StartedAt: time.Date(2026, 3, 1, 8, 0, 0, 0, time.UTC), EndedAt: time.Date(2026, 3, 1, 8, 40, 0, 0, time.UTC)}, } aggregate := AggregateAttempts(attempts) assert.Equal(t, 3, aggregate.Attempts) assert.Equal(t, 3200.0, aggregate.DistanceM) assert.Equal(t, 210.0, aggregate.ElevationGainM) assert.Equal(t, 6.7, aggregate.SlopePercent) assert.Equal(t, 84.0, aggregate.Cotacol) require.True(t, aggregate.HasBest) assert.Equal(t, 15*time.Minute, aggregate.BestDuration) } func TestAggregateAttemptsIgnoresBrokenDurations(t *testing.T) { attempts := []Attempt{ {DistanceM: 3000, StartedAt: time.Date(2026, 1, 1, 8, 0, 0, 0, time.UTC), EndedAt: time.Date(2026, 1, 1, 7, 0, 0, 0, time.UTC)}, {DistanceM: 3200, StartedAt: time.Date(2026, 2, 1, 8, 0, 0, 0, time.UTC), EndedAt: time.Date(2026, 2, 1, 8, 15, 0, 0, time.UTC)}, } aggregate := AggregateAttempts(attempts) require.True(t, aggregate.HasBest) assert.Equal(t, 15*time.Minute, aggregate.BestDuration) } func TestAggregateAttemptsEmpty(t *testing.T) { aggregate := AggregateAttempts(nil) assert.Equal(t, 0, aggregate.Attempts) assert.False(t, aggregate.HasBest) assert.Equal(t, climb.CategoryNo, aggregate.Category) } func TestNormalizeProfileLinearSegment(t *testing.T) { segment := climb.Climb{ Distances: []float64{0, 500, 1000}, Elevations: []float64{100, 150, 200}} samples := NormalizeProfile(segment) require.Len(t, samples, profileSteps) assert.InDelta(t, 100, samples[0], 0.01) assert.InDelta(t, 200, samples[profileSteps-1], 0.01) assert.InDelta(t, 100+100*float64(profileSteps/2)/float64(profileSteps-1), samples[profileSteps/2], 0.01) } func TestMedianProfileRobustToOutlier(t *testing.T) { straight := make([]float64, profileSteps) for i := range straight { straight[i] = float64(i) } spiked := make([]float64, profileSteps) for i := range spiked { spiked[i] = float64(i) + 500 } median := MedianProfile([][]float64{straight, spiked, straight}) require.Len(t, median, profileSteps) assert.InDelta(t, 10, median[10], 0.01) assert.InDelta(t, 49, median[49], 0.01) }