Cotacol-`)
assert.Contains(t, body, `class="ride-detail-grid"`)
assert.Contains(t, body, `class="ride-detail-columns`)
assert.NotContains(t, body, "Climbs to match")
assert.Contains(t, body, `class="ride-detail-main"`)
assert.Contains(t, body, ``)
assert.Contains(t, body, ``)
assert.NotContains(t, body, ``)
assert.Contains(t, body, `href="/analytics"`)
}
func TestHandlerFiltersAnalyticsByDistance(t *testing.T) {
server, db := newWebTestServer(t)
short := ride.StoredRide{
ExternalID: "strava:550e8400-e29b-41d4-a716-446655440003",
Name: "Short Ride",
ExternalSportType: "Ride",
BikeType: ride.BikeTypeRoad,
RideType: ride.RideTypeTraining,
StartDate: time.Date(2026, 8, 1, 7, 0, 0, 0, time.UTC),
DistanceM: 40_000,
}
long := ride.StoredRide{
ExternalID: "strava:550e8400-e29b-41d4-a716-446655440004",
Name: "Long Ride",
ExternalSportType: "Ride",
BikeType: ride.BikeTypeRoad,
RideType: ride.RideTypeTraining,
StartDate: time.Date(2026, 8, 2, 7, 0, 0, 0, time.UTC),
DistanceM: 100_000,
}
require.NoError(t, ride.Save(db, short))
require.NoError(t, ride.Save(db, long))
_, err := db.Exec(`
UPDATE rides
SET cotacol_score = CASE external_id WHEN ? THEN 20 ELSE 120 END,
cotacol_algo_version = ?,
average_speed_mps = CASE external_id WHEN ? THEN 7.0 ELSE 5.0 END
`, short.ExternalID, climb.CotacolAlgorithmVersion, long.ExternalID)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "/analytics?distance_min=60", nil)
response := httptest.NewRecorder()
server.Handler().ServeHTTP(response, req)
body := response.Body.String()
assert.Equal(t, http.StatusOK, response.Code)
assert.Contains(t, body, `id="analytics-chart"`)
assert.Contains(t, body, `id="analytics-points"`)
assert.Contains(t, body, `"cotacolPer100Km":120`)
assert.NotContains(t, body, `"cotacolPer100Km":50`)
assert.Contains(t, body, `name="distance_min"`)
assert.Contains(t, body, `href="/analytics"`)
assert.Contains(t, body, `data-analytics-count`)
}
func TestHandlerReturnsNotFoundForUnknownRide(t *testing.T) {
server, _ := newWebTestServer(t)
req := httptest.NewRequest(http.MethodGet, "/rides/999999", nil)
response := httptest.NewRecorder()
server.Handler().ServeHTTP(response, req)
assert.Equal(t, http.StatusNotFound, response.Code)
}
func TestHandlerShowsErrorForUnavailableRideRoute(t *testing.T) {
server, db := newWebTestServer(t)
_, err := db.Exec(`
INSERT INTO rides (external_id, gpx_path, name, external_sport_type, start_date, distance_m, external_moving_time_s, elapsed_time_s, total_elevation_gain_m, average_speed_mps)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "strava:6ba7b810-9dad-41d1-80b4-00c04fd430c8", "missing.gpx", "Unavailable Route", "Ride", "2026-08-01T07:00:00Z", 20_000, 0, 0, 0, 0)
require.NoError(t, err)
item, found, err := ride.GetByExternalID(db, "strava:6ba7b810-9dad-41d1-80b4-00c04fd430c8")
require.NoError(t, err)
require.True(t, found)
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/rides/%d", item.ID), nil)
response := httptest.NewRecorder()
server.Handler().ServeHTTP(response, req)
body := response.Body.String()
assert.Equal(t, http.StatusOK, response.Code)
assert.Contains(t, body, "Unavailable Route")
assert.Contains(t, body, "The recorded route is unavailable.")
assert.NotContains(t, body, `id="ride-route"`)
}
func TestSyncPageRequestsAuthorization(t *testing.T) {
server, _ := newWebTestServer(t)
req := httptest.NewRequest(http.MethodGet, "/sync", nil)
response := httptest.NewRecorder()
server.Handler().ServeHTTP(response, req)
assert.Equal(t, http.StatusOK, response.Code)
assert.Contains(t, response.Body.String(), "Authorize Strava")
}
func TestSyncPageIncludesProgressUIWhenAuthorized(t *testing.T) {
server, _ := newWebTestServer(t)
appConfig, err := config.Load(server.configPath)
require.NoError(t, err)
appConfig.Strava.AccessToken = "access"
appConfig.Strava.RefreshToken = "refresh"
appConfig.Strava.ExpiresAt = time.Now().Add(time.Hour).Unix()
require.NoError(t, config.Save(server.configPath, appConfig))
req := httptest.NewRequest(http.MethodGet, "/sync", nil)
response := httptest.NewRecorder()
server.Handler().ServeHTTP(response, req)
body := response.Body.String()
assert.Equal(t, http.StatusOK, response.Code)
assert.Contains(t, body, `id="sync-form"`)
assert.Contains(t, body, `id="sync-progress"`)
assert.Contains(t, body, `