From 53615a05708ff852af42905e2b5ced66ba70c43d Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Sun, 23 Aug 2026 11:09:07 +0200 Subject: Merge ride and rides packages into ride/store following mountain_pass structure Consolidate persistence (Save, List, Backfill) into ride/store.go alongside ride.go/climb.go/moving_time.go, rename rides.Ride to ride.StoredRide to avoid collision with parsed ride.Ride, update all web/cli consumers and RideView to use named Ride field, adjust templates and regenerate templ files, and remove rides directory. --- rides/rides_test.go | 304 ---------------------------------------------------- 1 file changed, 304 deletions(-) delete mode 100644 rides/rides_test.go (limited to 'rides/rides_test.go') diff --git a/rides/rides_test.go b/rides/rides_test.go deleted file mode 100644 index ea87172..0000000 --- a/rides/rides_test.go +++ /dev/null @@ -1,304 +0,0 @@ -package rides - -import ( - "database/sql" - "os" - "path/filepath" - "testing" - "time" - - "github.com/martinlehoux/biking_home/internal/dbtest" - "github.com/martinlehoux/biking_home/ride" - "github.com/martinlehoux/kagamigo/kcore" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func newTestDB(t *testing.T) *sql.DB { - return dbtest.New(t) -} - -func sampleRide(t *testing.T) Ride { - t.Helper() - gpxPath := filepath.Join(t.TempDir(), "ride.gpx") - require.NoError(t, os.WriteFile(gpxPath, []byte(testGPX), 0o600)) - return Ride{ - ExternalID: "strava:1234", - GPXPath: gpxPath, - Name: "Morning Ride", - Type: "Ride", - StartDate: time.Date(2026, 8, 1, 7, 30, 0, 0, time.UTC), - DistanceM: 42_195, - ExternalMovingTimeS: 7_200, - ElapsedTimeS: 7_800, - TotalElevationGainM: 850, - AverageSpeedMps: 5.86, - } -} - -const testGPX = `100200` - -func TestUpsertAndGet(t *testing.T) { - db := newTestDB(t) - sample := sampleRide(t) - err := Save(db, sample) - require.NoError(t, err) - - got, ok, err := GetByExternalID(db, "strava:1234") - require.NoError(t, err) - require.True(t, ok) - assert.Equal(t, "Morning Ride", got.Name) - assert.Equal(t, sample.StartDate, got.StartDate) - assert.Equal(t, 42_195.0, got.DistanceM) - assert.Equal(t, sample.GPXPath, got.GPXPath) - score, found := got.CotacolScore() - require.True(t, found) - assert.Greater(t, score, 0.0) - assert.Equal(t, ride.CotacolAlgorithmVersion, got.CotacolAlgorithmVersion()) - - _, ok, err = GetByExternalID(db, "strava:9999") - require.NoError(t, err) - assert.False(t, ok) -} - -func TestGetByID(t *testing.T) { - db := newTestDB(t) - sample := sampleRide(t) - require.NoError(t, Save(db, sample)) - stored, found, err := GetByExternalID(db, sample.ExternalID) - require.NoError(t, err) - require.True(t, found) - - got, found, err := GetByID(db, stored.ID) - require.NoError(t, err) - require.True(t, found) - assert.Equal(t, sample.ExternalID, got.ExternalID) - assert.Equal(t, sample.Name, got.Name) - - _, found, err = GetByID(db, 999999) - require.NoError(t, err) - assert.False(t, found) -} - -func TestSaveComputesCotacol(t *testing.T) { - db := newTestDB(t) - require.NoError(t, Save(db, sampleRide(t))) - - got, ok, err := GetByExternalID(db, "strava:1234") - require.NoError(t, err) - require.True(t, ok) - score, found := got.CotacolScore() - require.True(t, found) - assert.Greater(t, score, 0.0) - assert.Equal(t, ride.CotacolAlgorithmVersion, got.CotacolAlgorithmVersion()) -} - -func TestSaveComputesMovingTimeAndPreservesExternal(t *testing.T) { - db := newTestDB(t) - require.NoError(t, Save(db, sampleRide(t))) - - got, ok, err := GetByExternalID(db, "strava:1234") - require.NoError(t, err) - require.True(t, ok) - moving, found := got.MovingTimeS() - require.True(t, found) - assert.Equal(t, int64(60), moving) - assert.Equal(t, ride.MovingTimeAlgorithmVersion, got.MovingTimeAlgorithmVersion()) - assert.Equal(t, int64(7_200), got.ExternalMovingTimeS) -} - -func TestSaveAllowsRideWithoutGPX(t *testing.T) { - db := newTestDB(t) - indoor := sampleRide(t) - indoor.ExternalID = "strava:14701658670" - indoor.GPXPath = "" - - require.NoError(t, Save(db, indoor)) - - got, found, err := GetByExternalID(db, indoor.ExternalID) - require.NoError(t, err) - require.True(t, found) - assert.Empty(t, got.GPXPath) - _, ready := got.CotacolScore() - assert.False(t, ready) - assert.Empty(t, got.CotacolAlgorithmVersion()) - _, ready = got.MovingTimeS() - assert.False(t, ready) - assert.Empty(t, got.MovingTimeAlgorithmVersion()) -} - -func TestEffectiveMovingTimePrefersCanonical(t *testing.T) { - db := newTestDB(t) - require.NoError(t, Save(db, sampleRide(t))) - got, ok, err := GetByExternalID(db, "strava:1234") - require.NoError(t, err) - require.True(t, ok) - assert.Equal(t, int64(60), got.EffectiveMovingTimeS()) - - _, err = db.Exec("UPDATE rides SET moving_time_algo_version = 'old' WHERE external_id = ?", got.ExternalID) - require.NoError(t, err) - got, ok, err = GetByExternalID(db, "strava:1234") - require.NoError(t, err) - require.True(t, ok) - assert.Equal(t, int64(7_200), got.EffectiveMovingTimeS()) -} - -func TestListSortedByMovingTimeUsesCanonicalWithExternalFallback(t *testing.T) { - db := newTestDB(t) - first := sampleRide(t) - first.ExternalID = "strava:550e8400-e29b-41d4-a716-446655440000" - second := sampleRide(t) - second.ExternalID = "strava:6ba7b810-9dad-41d1-80b4-00c04fd430c8" - require.NoError(t, Save(db, first)) - require.NoError(t, Save(db, second)) - _, err := db.Exec("UPDATE rides SET external_moving_time_s = 1000, moving_time_s = 500 WHERE external_id = ?", first.ExternalID) - require.NoError(t, err) - _, err = db.Exec("UPDATE rides SET external_moving_time_s = 300, moving_time_s = 500, moving_time_algo_version = 'old' WHERE external_id = ?", second.ExternalID) - require.NoError(t, err) - - items, err := ListSorted(db, SortMovingTime, false) - require.NoError(t, err) - assert.Equal(t, second.ExternalID, items[0].ExternalID) - assert.Equal(t, first.ExternalID, items[1].ExternalID) -} - -func TestListSortedBySpeed(t *testing.T) { - db := newTestDB(t) - slow := sampleRide(t) - slow.ExternalID = "strava:550e8400-e29b-41d4-a716-446655440000" - slow.AverageSpeedMps = 4.2 - fast := sampleRide(t) - fast.ExternalID = "strava:6ba7b810-9dad-41d1-80b4-00c04fd430c8" - fast.AverageSpeedMps = 9.8 - require.NoError(t, Save(db, slow)) - require.NoError(t, Save(db, fast)) - - items, err := ListSorted(db, SortSpeed, false) - require.NoError(t, err) - assert.Equal(t, slow.ExternalID, items[0].ExternalID) - assert.Equal(t, fast.ExternalID, items[1].ExternalID) -} - -func TestBackfill(t *testing.T) { - db := newTestDB(t) - require.NoError(t, Save(db, sampleRide(t))) - - count, err := Backfill(db) - require.NoError(t, err) - assert.Equal(t, 1, count) - got, ok, err := GetByExternalID(db, "strava:1234") - require.NoError(t, err) - require.True(t, ok) - _, found := got.CotacolScore() - assert.True(t, found) - assert.Equal(t, ride.CotacolAlgorithmVersion, got.CotacolAlgorithmVersion()) - - count, err = Backfill(db) - require.NoError(t, err) - assert.Equal(t, 1, count) -} - -func TestBackfillSkipsInvalidRide(t *testing.T) { - db := newTestDB(t) - _, err := db.Exec(` - INSERT INTO rides (external_id, gpx_path, name, type, start_date, distance_m, external_moving_time_s, elapsed_time_s, total_elevation_gain_m, average_speed_mps) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, "strava:550e8400-e29b-41d4-a716-446655440000", "missing.gpx", "Broken Ride", "Ride", "2026-08-01T07:00:00Z", 20_000, 0, 0, 0, 0) - require.NoError(t, err) - - count, err := Backfill(db) - require.NoError(t, err) - assert.Zero(t, count) - - got, ok, err := GetByExternalID(db, "strava:550e8400-e29b-41d4-a716-446655440000") - require.NoError(t, err) - require.True(t, ok) - _, found := got.CotacolScore() - assert.False(t, found) -} - -func TestUpsertUpdatesExisting(t *testing.T) { - db := newTestDB(t) - ride := sampleRide(t) - require.NoError(t, Save(db, ride)) - ride.Name = "Renamed Ride" - ride.DistanceM = 50_000 - require.NoError(t, Save(db, ride)) - - got, ok, err := GetByExternalID(db, "strava:1234") - require.NoError(t, err) - require.True(t, ok) - assert.Equal(t, "Renamed Ride", got.Name) - assert.Equal(t, 50_000.0, got.DistanceM) -} - -func TestList(t *testing.T) { - db := newTestDB(t) - first := sampleRide(t) - first.StartDate = time.Date(2026, 7, 1, 7, 0, 0, 0, time.UTC) - second := sampleRide(t) - second.ExternalID = "strava:5678" - second.Name = "Evening Ride" - require.NoError(t, Save(db, first)) - require.NoError(t, Save(db, second)) - - rides, err := List(db) - require.NoError(t, err) - require.Len(t, rides, 2) - assert.Equal(t, "strava:5678", rides[0].ExternalID) - assert.Equal(t, "strava:1234", rides[1].ExternalID) - kcore.Assert(len(rides) == 2, "two rides") -} - -func TestListSortedByCotacolKeepsStaleLast(t *testing.T) { - db := newTestDB(t) - first := sampleRide(t) - first.ExternalID = "strava:550e8400-e29b-41d4-a716-446655440000" - first.DistanceM = 20_000 - second := sampleRide(t) - second.ExternalID = "strava:6ba7b810-9dad-41d1-80b4-00c04fd430c8" - second.DistanceM = 40_000 - require.NoError(t, Save(db, first)) - require.NoError(t, Save(db, second)) - _, err := db.Exec(` - UPDATE rides - SET cotacol_score = CASE external_id WHEN ? THEN 4 ELSE 6 END, - cotacol_algo_version = ? - `, first.ExternalID, ride.CotacolAlgorithmVersion) - require.NoError(t, err) - - items, err := ListSorted(db, SortCotacol, false) - require.NoError(t, err) - assert.Equal(t, first.ExternalID, items[0].ExternalID) - assert.Equal(t, second.ExternalID, items[1].ExternalID) - - _, err = db.Exec("UPDATE rides SET cotacol_algo_version = ? WHERE external_id = ?", "old", first.ExternalID) - require.NoError(t, err) - items, err = ListSorted(db, SortCotacol, false) - require.NoError(t, err) - assert.Equal(t, second.ExternalID, items[0].ExternalID) - assert.Equal(t, first.ExternalID, items[1].ExternalID) -} - -func TestListSortedByCotacolPer100Km(t *testing.T) { - db := newTestDB(t) - first := sampleRide(t) - first.ExternalID = "strava:550e8400-e29b-41d4-a716-446655440000" - first.DistanceM = 20_000 - second := sampleRide(t) - second.ExternalID = "strava:6ba7b810-9dad-41d1-80b4-00c04fd430c8" - second.DistanceM = 40_000 - require.NoError(t, Save(db, first)) - require.NoError(t, Save(db, second)) - _, err := db.Exec(` - UPDATE rides - SET cotacol_score = CASE external_id WHEN ? THEN 4 ELSE 6 END, - cotacol_algo_version = ? - `, first.ExternalID, ride.CotacolAlgorithmVersion) - require.NoError(t, err) - - items, err := ListSorted(db, SortCotacolKm, false) - require.NoError(t, err) - assert.Equal(t, second.ExternalID, items[0].ExternalID) - assert.Equal(t, first.ExternalID, items[1].ExternalID) -} -- cgit v1.3