diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-08-05 11:21:55 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-08-05 11:21:55 +0200 |
| commit | bef31e1d35fe88f2698cf0e3191f26a79623f66f (patch) | |
| tree | 8d8e10b2a58fc4ca557f0312cfda60adfc308c30 /web/server_test.go | |
| parent | 69f317d8cb51b911732712a9fcc8ac49563326c6 (diff) | |
feat: Add web ride library with Strava sync
Diffstat (limited to 'web/server_test.go')
| -rw-r--r-- | web/server_test.go | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/web/server_test.go b/web/server_test.go new file mode 100644 index 0000000..8d3f653 --- /dev/null +++ b/web/server_test.go @@ -0,0 +1,133 @@ +package web + +import ( + "database/sql" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/martinlehoux/biking_home/rides" + _ "github.com/mattn/go-sqlite3" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newWebTestServer(t *testing.T) (*Server, *sql.DB) { + t.Helper() + db, err := sql.Open("sqlite3", ":memory:") + require.NoError(t, err) + t.Cleanup(func() { db.Close() }) + _, err = db.Exec(` + create table rides ( + id integer primary key, + external_id text unique not null, + gpx_path text not null, + name text not null, + type text not null, + start_date text not null, + distance_m real not null, + moving_time_s integer not null, + elapsed_time_s integer not null, + total_elevation_gain_m real not null, + average_speed_mps real not null, + created_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), + updated_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')) + ) + `) + require.NoError(t, err) + envPath := filepath.Join(t.TempDir(), ".env") + require.NoError(t, os.WriteFile(envPath, []byte("STRAVA_CLIENT_ID=123\nSTRAVA_CLIENT_SECRET=secret\n"), 0o600)) + return NewServer(db, envPath, t.TempDir(), "http://localhost:8080"), db +} + +func TestHandlerRendersRidesPage(t *testing.T) { + server, db := newWebTestServer(t) + require.NoError(t, rides.Save(db, rides.Ride{ + ExternalID: "strava:1234", + GPXPath: "missing.gpx", + Name: "Long Ride", + Type: "Ride", + StartDate: time.Date(2026, 8, 1, 7, 0, 0, 0, time.UTC), + DistanceM: 20_000, + })) + require.NoError(t, rides.Save(db, rides.Ride{ + ExternalID: "strava:5678", + GPXPath: "short.gpx", + Name: "Short Ride", + Type: "Ride", + StartDate: time.Date(2026, 8, 2, 7, 0, 0, 0, time.UTC), + DistanceM: 9_999, + })) + req := httptest.NewRequest(http.MethodGet, "/", nil) + response := httptest.NewRecorder() + + server.Handler().ServeHTTP(response, req) + + assert.Equal(t, http.StatusOK, response.Code) + assert.Contains(t, response.Body.String(), "All rides") + assert.Contains(t, response.Body.String(), "Long Ride") + assert.NotContains(t, response.Body.String(), "Short Ride") + assert.Contains(t, response.Body.String(), "Cotacol") + assert.Contains(t, response.Body.String(), "Cotacol / 100 km") +} + +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 TestSyncRedirectsToOAuthWhenUnauthenticated(t *testing.T) { + server, _ := newWebTestServer(t) + form := url.Values{"from": {"2026-08-01"}, "to": {"2026-08-04"}} + req := httptest.NewRequest(http.MethodPost, "/sync", strings.NewReader(form.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + response := httptest.NewRecorder() + + server.Handler().ServeHTTP(response, req) + + assert.Equal(t, http.StatusFound, response.Code) + location, err := url.Parse(response.Header().Get("Location")) + require.NoError(t, err) + assert.Equal(t, "/strava/login", location.Path) + assert.Equal(t, "/sync?from=2026-08-01&to=2026-08-04", location.Query().Get("return_to")) +} + +func TestStravaLoginRedirectsToAuthorize(t *testing.T) { + server, _ := newWebTestServer(t) + req := httptest.NewRequest(http.MethodGet, "/strava/login?return_to=/sync", nil) + response := httptest.NewRecorder() + + server.Handler().ServeHTTP(response, req) + + assert.Equal(t, http.StatusFound, response.Code) + location, err := url.Parse(response.Header().Get("Location")) + require.NoError(t, err) + assert.Equal(t, "www.strava.com", location.Host) + assert.Equal(t, "/oauth/authorize", location.Path) + assert.Equal(t, "activity:read_all", location.Query().Get("scope")) + assert.Equal(t, "http://localhost:8080/strava/callback", location.Query().Get("redirect_uri")) + assert.NotEmpty(t, location.Query().Get("state")) +} + +func TestParseDateRangeMakesEndInclusive(t *testing.T) { + from, to, err := parseDateRange("2026-08-01", "2026-08-04") + require.NoError(t, err) + assert.Equal(t, "2026-08-01T00:00:00Z", from.Format("2006-01-02T15:04:05Z07:00")) + assert.Equal(t, "2026-08-05T00:00:00Z", to.Format("2006-01-02T15:04:05Z07:00")) +} + +func TestFormatCotacolPer100Km(t *testing.T) { + assert.Equal(t, "20.0", formatCotacolPer100Km(2, 10_000)) + assert.Equal(t, "-", formatCotacolPer100Km(2, 0)) +} |