diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-08-24 09:27:29 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-08-24 09:27:29 +0200 |
| commit | 8b921624666141c09cf20e2e0ee3cbe260a6412d (patch) | |
| tree | 7f708dfe7807dda1f54e4d010d58858d5e7c5999 /web/ride-filters_test.go | |
| parent | 34b8ae8f4c16b86281aa578e8f6891fca3666d37 (diff) | |
Add type filtering, drop hard-coded filters and route filter, tune backtest
Diffstat (limited to 'web/ride-filters_test.go')
| -rw-r--r-- | web/ride-filters_test.go | 59 |
1 files changed, 38 insertions, 21 deletions
diff --git a/web/ride-filters_test.go b/web/ride-filters_test.go index bf1cc6f..fd3ee13 100644 --- a/web/ride-filters_test.go +++ b/web/ride-filters_test.go @@ -23,7 +23,8 @@ func TestParseRideFilters(t *testing.T) { rideFilterDateToParam: {"2026-08-31"}, rideFilterCotacolMinParam: {"50"}, rideFilterCotacolMaxParam: {"500"}, - rideFilterRouteParam: {rideRouteFilterAvailable}, + rideFilterBikeTypeParam: {string(ride.BikeTypeRoad)}, + rideFilterRideTypeParam: {string(ride.RideTypeTraining)}, } filters := parseRideFilters(query) @@ -36,7 +37,8 @@ func TestParseRideFilters(t *testing.T) { assert.Equal(t, "2026-09-01T00:00:00Z", filters.DateToExclusive.Format(time.RFC3339)) assert.Equal(t, 50, *filters.CotacolMinPoints) assert.Equal(t, 500, *filters.CotacolMaxPoints) - assert.Equal(t, rideRouteFilterAvailable, filters.Route) + assert.Equal(t, ride.BikeTypeRoad, *filters.BikeType) + assert.Equal(t, ride.RideTypeTraining, *filters.RideType) assert.True(t, filters.Active()) } @@ -50,7 +52,8 @@ func TestParseRideFiltersIgnoresInvalidValues(t *testing.T) { rideFilterDateToParam: {"2026-08-01"}, rideFilterCotacolMinParam: {"25"}, rideFilterCotacolMaxParam: {"25"}, - rideFilterRouteParam: {"unknown"}, + rideFilterBikeTypeParam: {"unknown"}, + rideFilterRideTypeParam: {"unknown"}, } filters := parseRideFilters(query) @@ -64,7 +67,8 @@ func TestParseRideFiltersIgnoresInvalidValues(t *testing.T) { assert.Nil(t, filters.DateToExclusive) assert.Nil(t, filters.CotacolMinPoints) assert.Nil(t, filters.CotacolMaxPoints) - assert.Empty(t, filters.Route) + assert.Nil(t, filters.BikeType) + assert.Nil(t, filters.RideType) } func TestParseRideFiltersTreatsFullRangesAsInactive(t *testing.T) { @@ -88,11 +92,11 @@ func TestParseRideFiltersTreatsFullRangesAsInactive(t *testing.T) { func TestFilterRidesUsesInclusiveBoundsAndMovingTime(t *testing.T) { items := []ride.StoredRide{ - {Name: "lower boundary", DistanceM: 20_000, ExternalMovingTimeS: 1_800, StartDate: time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC), GPXPath: "lower.gpx"}, - {Name: "upper boundary", DistanceM: 100_000, ExternalMovingTimeS: 10_800, StartDate: time.Date(2026, 8, 31, 12, 0, 0, 0, time.UTC), GPXPath: "upper.gpx"}, - {Name: "outside distance", DistanceM: 110_000, ExternalMovingTimeS: 10_800, StartDate: time.Date(2026, 8, 31, 12, 0, 0, 0, time.UTC), GPXPath: "outside-distance.gpx"}, - {Name: "outside moving time", DistanceM: 100_000, ExternalMovingTimeS: 10_801, StartDate: time.Date(2026, 8, 31, 12, 0, 0, 0, time.UTC), GPXPath: "outside-duration.gpx"}, - {Name: "outside date", DistanceM: 100_000, ExternalMovingTimeS: 10_800, StartDate: time.Date(2026, 9, 1, 12, 0, 0, 0, time.UTC), GPXPath: "outside-date.gpx"}, + {Name: "lower boundary", DistanceM: 20_000, ExternalMovingTimeS: 1_800, StartDate: time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC), GPXPath: "lower.gpx", BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining}, + {Name: "upper boundary", DistanceM: 100_000, ExternalMovingTimeS: 10_800, StartDate: time.Date(2026, 8, 31, 12, 0, 0, 0, time.UTC), GPXPath: "upper.gpx", BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining}, + {Name: "outside distance", DistanceM: 110_000, ExternalMovingTimeS: 10_800, StartDate: time.Date(2026, 8, 31, 12, 0, 0, 0, time.UTC), GPXPath: "outside-distance.gpx", BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining}, + {Name: "outside moving time", DistanceM: 100_000, ExternalMovingTimeS: 10_801, StartDate: time.Date(2026, 8, 31, 12, 0, 0, 0, time.UTC), GPXPath: "outside-duration.gpx", BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining}, + {Name: "outside date", DistanceM: 100_000, ExternalMovingTimeS: 10_800, StartDate: time.Date(2026, 9, 1, 12, 0, 0, 0, time.UTC), GPXPath: "outside-date.gpx", BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining}, } filters := parseRideFilters(url.Values{ rideFilterDistanceMinParam: {"20"}, @@ -110,19 +114,21 @@ func TestFilterRidesUsesInclusiveBoundsAndMovingTime(t *testing.T) { assert.Equal(t, "upper boundary", filtered[1].Name) } -func TestFilterRidesByRouteAvailability(t *testing.T) { +func TestFilterRidesByBikeAndRideType(t *testing.T) { items := []ride.StoredRide{ - {Name: "recorded", GPXPath: "recorded.gpx"}, - {Name: "metadata only"}, + {Name: "road training", BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeTraining, GPXPath: "a.gpx"}, + {Name: "mtb race", BikeType: ride.BikeTypeMTB, RideType: ride.RideTypeRace, GPXPath: "b.gpx"}, + {Name: "road race", BikeType: ride.BikeTypeRoad, RideType: ride.RideTypeRace, GPXPath: "c.gpx"}, } - available := filterRides(items, parseRideFilters(url.Values{rideFilterRouteParam: {rideRouteFilterAvailable}})) - missing := filterRides(items, parseRideFilters(url.Values{rideFilterRouteParam: {rideRouteFilterMissing}})) + road := filterRides(items, parseRideFilters(url.Values{rideFilterBikeTypeParam: {string(ride.BikeTypeRoad)}})) + race := filterRides(items, parseRideFilters(url.Values{rideFilterRideTypeParam: {string(ride.RideTypeRace)}})) + roadRace := filterRides(items, parseRideFilters(url.Values{rideFilterBikeTypeParam: {string(ride.BikeTypeRoad)}, rideFilterRideTypeParam: {string(ride.RideTypeRace)}})) - require.Len(t, available, 1) - assert.Equal(t, "recorded", available[0].Name) - require.Len(t, missing, 1) - assert.Equal(t, "metadata only", missing[0].Name) + require.Len(t, road, 2) + require.Len(t, race, 2) + require.Len(t, roadRace, 1) + assert.Equal(t, "road race", roadRace[0].Name) } func TestFilterRidesByCotacolExcludesUnavailableScores(t *testing.T) { @@ -132,6 +138,8 @@ func TestFilterRidesByCotacolExcludesUnavailableScores(t *testing.T) { GPXPath: testGPXPath(t, "current-cotacol.gpx"), Name: "Current score", Type: "Ride", + BikeType: ride.BikeTypeRoad, + RideType: ride.RideTypeTraining, StartDate: time.Date(2026, 8, 1, 7, 0, 0, 0, time.UTC), DistanceM: 20_000, } @@ -140,6 +148,8 @@ func TestFilterRidesByCotacolExcludesUnavailableScores(t *testing.T) { GPXPath: testGPXPath(t, "stale-cotacol.gpx"), Name: "Stale score", Type: "Ride", + BikeType: ride.BikeTypeRoad, + RideType: ride.RideTypeTraining, StartDate: time.Date(2026, 8, 2, 7, 0, 0, 0, time.UTC), DistanceM: 20_000, } @@ -147,6 +157,8 @@ func TestFilterRidesByCotacolExcludesUnavailableScores(t *testing.T) { ExternalID: "strava:7ba7b810-9dad-41d1-80b4-00c04fd430c8", Name: "No score", Type: "VirtualRide", + BikeType: ride.BikeTypeRoad, + RideType: ride.RideTypeTraining, StartDate: time.Date(2026, 8, 3, 7, 0, 0, 0, time.UTC), DistanceM: 20_000, } @@ -190,7 +202,8 @@ func TestHandlerRendersRideFilterControls(t *testing.T) { assert.Contains(t, body, `name="duration_min"`) assert.Contains(t, body, `name="date_from"`) assert.Contains(t, body, `name="cotacol_min"`) - assert.Contains(t, body, `name="route"`) + assert.Contains(t, body, `name="bike_type"`) + assert.Contains(t, body, `name="ride_type"`) assert.Contains(t, body, `<script type="module" src="/static/ride-filters.js"></script>`) } @@ -198,12 +211,12 @@ func TestRideSortHeadersPreserveFilters(t *testing.T) { filters := parseRideFilters(url.Values{ rideFilterDistanceMinParam: {"50"}, rideFilterDistanceMaxParam: {"150"}, - rideFilterRouteParam: {rideRouteFilterAvailable}, + rideFilterBikeTypeParam: {string(ride.BikeTypeRoad)}, }) headers := rideSortHeadersWithFilters(RideSort{Column: rideSortDistance, Descending: true}, filters) - assert.Equal(t, "/?dir=asc&distance_max=150&distance_min=50&route=available&sort=name", headers[0].URL) + assert.Equal(t, "/?bike_type=road&dir=asc&distance_max=150&distance_min=50&sort=name", headers[0].URL) } func TestHandlerFiltersRidesBeforeRenderingSortedResults(t *testing.T) { @@ -214,6 +227,8 @@ func TestHandlerFiltersRidesBeforeRenderingSortedResults(t *testing.T) { GPXPath: testGPXPath(t, path), Name: name, Type: "Ride", + BikeType: ride.BikeTypeRoad, + RideType: ride.RideTypeTraining, StartDate: time.Date(2026, 8, 1, 7, 0, 0, 0, time.UTC), DistanceM: distance, })) @@ -239,6 +254,8 @@ func TestHandlerRendersFilteredEmptyState(t *testing.T) { GPXPath: testGPXPath(t, "empty-filter.gpx"), Name: "Short Ride", Type: "Ride", + BikeType: ride.BikeTypeRoad, + RideType: ride.RideTypeTraining, StartDate: time.Date(2026, 8, 1, 7, 0, 0, 0, time.UTC), DistanceM: 20_000, })) |
