package official_climb_test import ( "database/sql" "testing" "time" "github.com/jftuga/geodist" "github.com/martinlehoux/biking_home/internal/dbtest" "github.com/martinlehoux/biking_home/official_climb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func coord(lat, lon float64) geodist.Coord { return geodist.Coord{Lat: lat, Lon: lon} } func insertRide(t *testing.T, db *sql.DB, opts ...func(*rideInsert)) int64 { t.Helper() insert := rideInsert{startDate: "2026-09-01T08:00:00Z"} for _, opt := range opts { opt(&insert) } result, 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:test-ride-"+insert.startDate, "missing.gpx", "Test Ride", "Ride", insert.startDate, 20_000, 0, 0, 0, 0) require.NoError(t, err) id, err := result.LastInsertId() require.NoError(t, err) return id } type rideInsert struct { startDate string } func withStartDate(startDate string) func(*rideInsert) { return func(r *rideInsert) { r.startDate = startDate } } func attemptFixture(rideID, climbID int64, occurrence int) official_climb.Attempt { return official_climb.Attempt{ RideID: rideID, ClimbID: climbID, Occurrence: occurrence, DistanceM: 3200, ElevationGainM: 210, SlopePercent: 6.5, Cotacol: 84.2, CotacolAlgoVersion: "v1", StartedAt: time.Date(2026, 9, 1, 10, 0, 0, 0, time.UTC), EndedAt: time.Date(2026, 9, 1, 10, 18, 30, 0, time.UTC), } } func TestReplaceRideAttemptsRoundTrip(t *testing.T) { db := dbtest.New(t) created, err := official_climb.Create(db, official_climb.OfficialClimb{Name: "Col de Test", StartCoord: coord(43.6, 5.4), EndCoord: coord(43.62, 5.42)}) require.NoError(t, err) rideID := insertRide(t, db) require.NoError(t, official_climb.ReplaceRideAttempts(db, rideID, []official_climb.Attempt{ attemptFixture(rideID, created.ID, 0), attemptFixture(rideID, created.ID, 1), })) attempts, err := official_climb.AttemptsForClimb(db, created.ID) require.NoError(t, err) require.Len(t, attempts, 2) assert.Equal(t, 0, attempts[0].Occurrence) assert.Equal(t, 1, attempts[1].Occurrence) assert.Equal(t, 3200.0, attempts[0].DistanceM) assert.Equal(t, "v1", attempts[0].CotacolAlgoVersion) assert.Equal(t, time.Date(2026, 9, 1, 10, 18, 30, 0, time.UTC), attempts[0].EndedAt) rideAttempts, err := official_climb.AttemptsForRide(db, rideID) require.NoError(t, err) require.Len(t, rideAttempts, 2) } func TestReplaceRideAttemptsDropsStaleRows(t *testing.T) { db := dbtest.New(t) first, err := official_climb.Create(db, official_climb.OfficialClimb{Name: "Col A", StartCoord: coord(43.6, 5.4), EndCoord: coord(43.62, 5.42)}) require.NoError(t, err) second, err := official_climb.Create(db, official_climb.OfficialClimb{Name: "Col B", StartCoord: coord(43.7, 5.5), EndCoord: coord(43.72, 5.52)}) require.NoError(t, err) rideID := insertRide(t, db) require.NoError(t, official_climb.ReplaceRideAttempts(db, rideID, []official_climb.Attempt{attemptFixture(rideID, first.ID, 0)})) require.NoError(t, official_climb.ReplaceRideAttempts(db, rideID, []official_climb.Attempt{attemptFixture(rideID, second.ID, 0)})) attempts, err := official_climb.AttemptsForClimb(db, first.ID) require.NoError(t, err) assert.Empty(t, attempts) attempts, err = official_climb.AttemptsForClimb(db, second.ID) require.NoError(t, err) require.Len(t, attempts, 1) } func TestReplaceRideAttemptsRejectsForeignRide(t *testing.T) { db := dbtest.New(t) created, err := official_climb.Create(db, official_climb.OfficialClimb{Name: "Col de Test", StartCoord: coord(43.6, 5.4), EndCoord: coord(43.62, 5.42)}) require.NoError(t, err) rideID := insertRide(t, db) attempt := attemptFixture(rideID+1, created.ID, 0) err = official_climb.ReplaceRideAttempts(db, rideID, []official_climb.Attempt{attempt}) assert.Error(t, err) } func TestAttemptsForClimbOrdersByRideDate(t *testing.T) { db := dbtest.New(t) created, err := official_climb.Create(db, official_climb.OfficialClimb{Name: "Col de Test", StartCoord: coord(43.6, 5.4), EndCoord: coord(43.62, 5.42)}) require.NoError(t, err) newer := insertRide(t, db, withStartDate("2026-09-10T08:00:00Z")) older := insertRide(t, db, withStartDate("2026-09-02T08:00:00Z")) require.NoError(t, official_climb.ReplaceRideAttempts(db, newer, []official_climb.Attempt{attemptFixture(newer, created.ID, 0)})) require.NoError(t, official_climb.ReplaceRideAttempts(db, older, []official_climb.Attempt{attemptFixture(older, created.ID, 0)})) attempts, err := official_climb.AttemptsForClimb(db, created.ID) require.NoError(t, err) require.Len(t, attempts, 2) assert.Equal(t, older, attempts[0].RideID) assert.Equal(t, newer, attempts[1].RideID) }