1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
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{Route: climb.Route{
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)
}
|