aboutsummaryrefslogtreecommitdiff
path: root/web/climbs_test.go
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-10-18 21:19:36 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-10-18 21:19:36 +0200
commit8a0032d08862dcf28c653bee3fd21052e967e35d (patch)
tree6cb0d8efe6f5c2bf9ce9eb3ef8306faecbe7cfeb /web/climbs_test.go
parentd575274d00c8da251df29d5d6a636044673944a3 (diff)
Cut climb profile sections in the backend
The chart steps are computed in Go and shipped as finished sections, next to the profile statistics the climb package already owns: climb.ProfileSections cuts a Route at its widest bends under a ProfilePolicy, and rideanalysis.MergeClimbProfile takes over the merge that lived in web/climbs.go, so web only loads the attempt rides and maps the DTOs. The payload carries sections instead of raw elevations, and the browser keeps what needs the canvas: slope bands, labels, distance ticks and colours. official-climb-profile-logic.ts and its tests are gone, replaced by climb/profile_test.go and a page test reading the embedded profile JSON.
Diffstat (limited to 'web/climbs_test.go')
-rw-r--r--web/climbs_test.go56
1 files changed, 19 insertions, 37 deletions
diff --git a/web/climbs_test.go b/web/climbs_test.go
index d79f1d8..97c7111 100644
--- a/web/climbs_test.go
+++ b/web/climbs_test.go
@@ -2,10 +2,12 @@ package web
import (
"database/sql"
+ "encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
+ "regexp"
"testing"
"github.com/martinlehoux/biking_home/internal/ridetest"
@@ -34,6 +36,16 @@ func storeRideFixtureFile(t *testing.T, db *sql.DB, rideID int64) {
require.NoError(t, err)
}
+// climbProfileData reads the profile JSON the climb page embeds.
+func climbProfileData(t *testing.T, body string) ClimbProfileData {
+ t.Helper()
+ match := regexp.MustCompile(`(?s)id="climb-profile-data"[^>]*>(.*?)</script>`).FindStringSubmatch(body)
+ require.Len(t, match, 2, "climb page must embed its profile JSON")
+ var profile ClimbProfileData
+ require.NoError(t, json.Unmarshal([]byte(match[1]), &profile))
+ return profile
+}
+
func TestHandleClimbRendersAttemptsAndProfile(t *testing.T) {
server, db := newWebTestServer(t)
_, climbFixture := testRideForClimb(t)
@@ -49,6 +61,13 @@ func TestHandleClimbRendersAttemptsAndProfile(t *testing.T) {
body := recorder.Body.String()
assert.Contains(t, body, "Col de Test")
assert.Contains(t, body, "climb-profile-data")
+ profile := climbProfileData(t, body)
+ require.NotEmpty(t, profile.Sections)
+ assert.InDelta(t, 0, profile.Sections[0].StartKm, 0.01)
+ assert.InDelta(t, 300, profile.Sections[0].StartElevationM, 1)
+ lastSection := profile.Sections[len(profile.Sections)-1]
+ assert.InDelta(t, 1, lastSection.EndKm, 0.01)
+ assert.InDelta(t, 447, lastSection.EndElevationM, 2)
assert.Contains(t, body, "Test Ride")
// A ride without a power meter renders a dash, not a fabricated value.
assert.Contains(t, body, `<td class="numeric">-</td>`)
@@ -94,40 +113,3 @@ func TestHandleClimbNotFound(t *testing.T) {
server.Handler().ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/climbs/999", nil))
assert.Equal(t, http.StatusNotFound, recorder.Code)
}
-
-func TestBuildClimbProfileMediansAcrossRides(t *testing.T) {
- server, db := newWebTestServer(t)
- _, climbFixture := testRideForClimb(t)
- created, err := official_climb.Create(db, climbFixture)
- require.NoError(t, err)
- rideID := insertTestRide(t, db)
- storeRideFixtureFile(t, db, rideID)
- parsed, _ := testRideForClimb(t)
- analysis := rideanalysis.Analyze(parsed, nil, []official_climb.OfficialClimb{created}, official_climb.DefaultMatchPolicy())
- attempts := rideanalysis.Attempts(rideID, analysis, parsed)
-
- profile := server.buildClimbProfile(created, official_climb.AggregateAttempts(attempts), attempts)
- assert.InDelta(t, 1.0, profile.DistanceKm, 0.01)
- require.Len(t, profile.Elevations, 50)
- assert.InDelta(t, 300, profile.Elevations[0], 1)
- assert.InDelta(t, 447, profile.Elevations[49], 2)
-}
-
-func TestBuildClimbProfileRouteKeepsRecordedPoints(t *testing.T) {
- server, db := newWebTestServer(t)
- parsed, climbFixture := testRideForClimb(t)
- created, err := official_climb.Create(db, climbFixture)
- require.NoError(t, err)
- rideID := insertTestRide(t, db)
- storeRideFixtureFile(t, db, rideID)
- analysis := rideanalysis.Analyze(parsed, nil, []official_climb.OfficialClimb{created}, official_climb.DefaultMatchPolicy())
- attempts := rideanalysis.Attempts(rideID, analysis, parsed)
-
- profile := server.buildClimbProfile(created, official_climb.AggregateAttempts(attempts), attempts)
- require.NotEmpty(t, profile.Route)
- // Full recorded precision: every matched segment point is present, not a fixed resample.
- assert.InDelta(t, parsed.Coords[0].Lon, profile.Route[0][0], 1e-6)
- assert.InDelta(t, parsed.Coords[0].Lat, profile.Route[0][1], 1e-6)
- assert.InDelta(t, parsed.Coords[1].Lon, profile.Route[1][0], 1e-6)
- assert.InDelta(t, parsed.Coords[1].Lat, profile.Route[1][1], 1e-6)
-}