diff --git a/activity.go b/activity.go index 00219f2..dbeaaf1 100644 --- a/activity.go +++ b/activity.go @@ -7,12 +7,12 @@ import ( const UserAgent = "github.com/bzimmer/activity" -type GPXer interface { +type GPXEncoder interface { // GPX returns a GPX instance GPX() (*gpx.GPX, error) } -type GeoJSONer interface { +type GeoJSONEncoder interface { // GeoJSON returns a GeoJSON instance - GeoJSON() (*geojson.Feature, error) + GeoJSON() (*geojson.FeatureCollection, error) } diff --git a/cyclinganalytics/cyclinganalytics.go b/cyclinganalytics/cyclinganalytics.go index 99ecfcd..1330e52 100644 --- a/cyclinganalytics/cyclinganalytics.go +++ b/cyclinganalytics/cyclinganalytics.go @@ -16,6 +16,9 @@ import ( const _baseURL = "https://www.cyclinganalytics.com/api" +// APIOption for configuring API requests +type APIOption func(url.Values) error + // Client for accessing Cycling Analytics' API type Client struct { config oauth2.Config diff --git a/cyclinganalytics/cyclinganalytics_test.go b/cyclinganalytics/cyclinganalytics_test.go index 4b1628d..26415eb 100644 --- a/cyclinganalytics/cyclinganalytics_test.go +++ b/cyclinganalytics/cyclinganalytics_test.go @@ -1,10 +1,17 @@ package cyclinganalytics_test import ( + "context" + "net/http" + "testing" "time" - "github.com/bzimmer/activity/cyclinganalytics" "github.com/bzimmer/httpwares" + "github.com/stretchr/testify/assert" + "golang.org/x/oauth2" + "golang.org/x/time/rate" + + "github.com/bzimmer/activity/cyclinganalytics" ) func newClient(status int, filename string) (*cyclinganalytics.Client, error) { @@ -13,6 +20,20 @@ func newClient(status int, filename string) (*cyclinganalytics.Client, error) { Status: status, Filename: filename, ContentType: "application/json"}), - cyclinganalytics.WithHTTPTracing(false), cyclinganalytics.WithTokenCredentials("fooKey", "barToken", time.Time{})) } + +func TestWith(t *testing.T) { + t.Parallel() + a := assert.New(t) + client, err := cyclinganalytics.NewClient( + cyclinganalytics.WithConfig(oauth2.Config{}), + cyclinganalytics.WithHTTPTracing(true), + cyclinganalytics.WithHTTPClient(http.DefaultClient), + cyclinganalytics.WithToken(&oauth2.Token{}), + cyclinganalytics.WithAutoRefresh(context.Background()), + cyclinganalytics.WithRateLimiter(rate.NewLimiter(rate.Every(time.Second), 10)), + cyclinganalytics.WithClientCredentials("foo", "bar")) + a.NoError(err) + a.NotNil(client) +} diff --git a/cyclinganalytics/encoding.go b/cyclinganalytics/encoding.go index 18bfbab..7e21606 100644 --- a/cyclinganalytics/encoding.go +++ b/cyclinganalytics/encoding.go @@ -11,7 +11,7 @@ import ( "github.com/bzimmer/activity" ) -var _ activity.GPXer = (*Ride)(nil) +var _ activity.GPXEncoder = (*Ride)(nil) func (r *Ride) GPX() (*gpx.GPX, error) { var layout geom.Layout diff --git a/cyclinganalytics/encoding_test.go b/cyclinganalytics/encoding_test.go index 7bc5e17..923b24d 100644 --- a/cyclinganalytics/encoding_test.go +++ b/cyclinganalytics/encoding_test.go @@ -10,7 +10,7 @@ import ( "github.com/bzimmer/activity/cyclinganalytics" ) -func TestRideToGPX(t *testing.T) { +func TestRideGPX(t *testing.T) { t.Parallel() a := assert.New(t) client, err := newClient(http.StatusOK, "ride.json") @@ -19,7 +19,7 @@ func TestRideToGPX(t *testing.T) { opts := cyclinganalytics.RideOptions{ Streams: []string{"latitude", "longitude", "elevation"}, } - ride, err := client.Rides.Ride(ctx, 175334338355, opts) + ride, err := client.Rides.Ride(ctx, 175334338355, cyclinganalytics.WithRideOptions(opts)) a.NoError(err) a.NotNil(ride) a.NotNil(ride.Streams) diff --git a/cyclinganalytics/model_test.go b/cyclinganalytics/model_test.go index 74bcf90..f09e7c4 100644 --- a/cyclinganalytics/model_test.go +++ b/cyclinganalytics/model_test.go @@ -25,6 +25,7 @@ func TestModel(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() src := cyclinganalytics.Ride{ UTCDatetime: cyclinganalytics.Datetime{Time: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)}, } diff --git a/cyclinganalytics/rides.go b/cyclinganalytics/rides.go index 097490f..afc880a 100644 --- a/cyclinganalytics/rides.go +++ b/cyclinganalytics/rides.go @@ -35,31 +35,37 @@ const ( meupload = "me/upload" ) -func (r *RideOptions) values() (*url.Values, error) { - v := &url.Values{} - if r.Streams != nil { - if err := validateStreams(r.Streams); err != nil { - return nil, err +func WithRideOptions(r RideOptions) APIOption { + return func(v url.Values) error { + if r.Streams != nil { + if err := validateStreams(r.Streams); err != nil { + return err + } + v.Set("streams", strings.Join(r.Streams, ",")) } - v.Set("streams", strings.Join(r.Streams, ",")) - } - if r.Curves.AveragePower && r.Curves.EffectivePower { - v.Set("curves", "true") - } else { - v.Set("power_curve", fmt.Sprintf("%t", r.Curves.AveragePower)) - v.Set("epower_curve", fmt.Sprintf("%t", r.Curves.EffectivePower)) + if r.Curves.AveragePower && r.Curves.EffectivePower { + v.Set("curves", "true") + } else { + v.Set("power_curve", fmt.Sprintf("%t", r.Curves.AveragePower)) + v.Set("epower_curve", fmt.Sprintf("%t", r.Curves.EffectivePower)) + } + return nil } - return v, nil } // Ride returns a single ride with available options -func (s *RidesService) Ride(ctx context.Context, rideID int64, opts RideOptions) (*Ride, error) { +func (s *RidesService) Ride(ctx context.Context, rideID int64, opts ...APIOption) (*Ride, error) { uri := fmt.Sprintf("ride/%d", rideID) - params, err := opts.values() - if err != nil { - return nil, err + v := url.Values{} + for i := range opts { + f := opts[i] + if f != nil { + if err := f(v); err != nil { + return nil, err + } + } } - req, err := s.client.newAPIRequest(ctx, http.MethodGet, uri, params, nil) + req, err := s.client.newAPIRequest(ctx, http.MethodGet, uri, &v, nil) if err != nil { return nil, err } diff --git a/cyclinganalytics/rides_test.go b/cyclinganalytics/rides_test.go index 17532be..a7ea8b0 100644 --- a/cyclinganalytics/rides_test.go +++ b/cyclinganalytics/rides_test.go @@ -3,7 +3,9 @@ package cyclinganalytics_test import ( "context" "net/http" + "net/http/httptest" "testing" + "time" "github.com/stretchr/testify/assert" @@ -11,44 +13,117 @@ import ( "github.com/bzimmer/activity/cyclinganalytics" ) -func TestRides(t *testing.T) { - t.Parallel() - a := assert.New(t) - client, err := newClient(http.StatusOK, "me-rides.json") - a.NoError(err) - ctx := context.Background() - rides, err := client.Rides.Rides(ctx, cyclinganalytics.Me, activity.Pagination{}) - a.NoError(err) - a.NotNil(rides) - a.Equal(2, len(rides)) -} - func TestRide(t *testing.T) { t.Parallel() a := assert.New(t) - client, err := newClient(http.StatusOK, "ride.json") - a.NoError(err) - ctx := context.Background() - opts := cyclinganalytics.RideOptions{ - Streams: []string{"latitude", "longitude", "elevation"}, + + tests := []struct { + name string + err string + rideID int64 + }{ + { + rideID: 22322, + name: "query recent feed", + }, + { + rideID: 175334338355, + name: "query recent feed", + }, + { + rideID: 882722, + name: "query recent feed", + err: "Something went horribly wrong", + }, + } + + for i := range tests { + tt := tests[i] + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + mux := http.NewServeMux() + mux.HandleFunc("/ride/22322", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "testdata/ride.json") + }) + mux.HandleFunc("/ride/175334338355", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "testdata/ride.json") + }) + mux.HandleFunc("/ride/882722", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusBadRequest) + http.ServeFile(w, r, "testdata/error.json") + }) + + svr := httptest.NewServer(mux) + defer svr.Close() + + opts := cyclinganalytics.WithRideOptions(cyclinganalytics.RideOptions{ + Streams: []string{"latitude", "longitude", "elevation"}, + }) + client, err := cyclinganalytics.NewClient( + cyclinganalytics.WithBaseURL(svr.URL), + cyclinganalytics.WithHTTPTracing(false), + cyclinganalytics.WithTokenCredentials("fooKey", "barToken", time.Time{})) + a.NoError(err) + ride, err := client.Rides.Ride(context.Background(), tt.rideID, opts) + if tt.err != "" { + a.Error(err) + a.Nil(ride) + a.Contains(err.Error(), tt.err) + return + } + a.NoError(err) + a.NotNil(ride) + a.NotNil(ride.Streams) + a.Equal(27154, len(ride.Streams.Elevation)) + gears := ride.Streams.Gears + a.NotNil(gears) + a.Equal(813, len(gears.Shifts)) + }) } - ride, err := client.Rides.Ride(ctx, 175334338355, opts) - a.NoError(err) - a.NotNil(ride) - a.NotNil(ride.Streams) - a.Equal(27154, len(ride.Streams.Elevation)) - gears := ride.Streams.Gears - a.NotNil(gears) - a.Equal(813, len(gears.Shifts)) } -func TestRideForbidden(t *testing.T) { +func TestRides(t *testing.T) { t.Parallel() a := assert.New(t) - client, err := newClient(http.StatusForbidden, "error.json") - a.NoError(err) - ctx := context.Background() - ride, err := client.Rides.Ride(ctx, 175334338355, cyclinganalytics.RideOptions{}) - a.Error(err) - a.Nil(ride) + + tests := []struct { + name string + user cyclinganalytics.UserID + }{ + { + user: cyclinganalytics.Me, + name: "query rides for `me`", + }, + { + user: cyclinganalytics.UserID(882782), + name: "query rides for user", + }, + } + + for i := range tests { + tt := tests[i] + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + mux := http.NewServeMux() + mux.HandleFunc("/me/rides", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "testdata/me-rides.json") + }) + mux.HandleFunc("/882782/rides", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "testdata/me-rides.json") + }) + + svr := httptest.NewServer(mux) + defer svr.Close() + + client, err := cyclinganalytics.NewClient( + cyclinganalytics.WithBaseURL(svr.URL), + cyclinganalytics.WithHTTPTracing(false), + cyclinganalytics.WithTokenCredentials("fooKey", "barToken", time.Time{})) + a.NoError(err) + rides, err := client.Rides.Rides(context.Background(), tt.user, activity.Pagination{}) + a.NoError(err) + a.NotNil(rides) + a.Len(rides, 2) + }) + } } diff --git a/cyclinganalytics/upload_test.go b/cyclinganalytics/upload_test.go new file mode 100644 index 0000000..6de0e7b --- /dev/null +++ b/cyclinganalytics/upload_test.go @@ -0,0 +1,50 @@ +package cyclinganalytics_test + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/bzimmer/activity/cyclinganalytics" + "github.com/stretchr/testify/assert" +) + +func TestUpload(t *testing.T) { + tests := []struct { + name, err string + }{ + { + name: "uploader error", + err: "missing upload file", + }, + } + + for i := range tests { + tt := tests[i] + t.Run(tt.name, func(t *testing.T) { + a := assert.New(t) + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + }) + + svr := httptest.NewServer(mux) + defer svr.Close() + + client, err := cyclinganalytics.NewClient( + cyclinganalytics.WithBaseURL(svr.URL), + cyclinganalytics.WithTokenCredentials("fooKey", "barToken", time.Time{})) + a.NoError(err) + uploader := client.Uploader() + a.NotNil(uploader) + upload, err := uploader.Upload(context.Background(), nil) + if tt.err != "" { + a.Error(err) + a.Nil(upload) + a.Contains(err.Error(), tt.err) + return + } + }) + } +} diff --git a/cyclinganalytics/user_test.go b/cyclinganalytics/user_test.go new file mode 100644 index 0000000..78c62c4 --- /dev/null +++ b/cyclinganalytics/user_test.go @@ -0,0 +1,52 @@ +package cyclinganalytics_test + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/bzimmer/activity/cyclinganalytics" + "github.com/stretchr/testify/assert" +) + +func TestUser(t *testing.T) { + tests := []struct { + name string + }{ + { + name: "query `me` user", + }, + } + + for i := range tests { + tt := tests[i] + t.Run(tt.name, func(t *testing.T) { + a := assert.New(t) + mux := http.NewServeMux() + mux.HandleFunc("/me", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + enc := json.NewEncoder(w) + a.NoError(enc.Encode(&cyclinganalytics.User{ + Email: "me@example.com", + ID: cyclinganalytics.UserID(1590343), + Name: "Some One", + })) + }) + + svr := httptest.NewServer(mux) + defer svr.Close() + + client, err := cyclinganalytics.NewClient( + cyclinganalytics.WithBaseURL(svr.URL), + cyclinganalytics.WithTokenCredentials("fooKey", "barToken", time.Time{})) + a.NoError(err) + me, err := client.User.Me(context.Background()) + a.NoError(err) + a.NotNil(me) + a.Equal("me@example.com", me.Email) + }) + } +} diff --git a/inreach/encoding.go b/inreach/encoding.go new file mode 100644 index 0000000..8142343 --- /dev/null +++ b/inreach/encoding.go @@ -0,0 +1,84 @@ +package inreach + +import ( + "strconv" + "time" + + "github.com/bzimmer/activity" + "github.com/twpayne/go-geom" + "github.com/twpayne/go-geom/encoding/geojson" +) + +var _ activity.GeoJSONEncoder = (*Feed)(nil) + +func (k *Feed) GeoJSON() (*geojson.FeatureCollection, error) { //nolint + if k.Document == nil || k.Document.Folder == nil || k.Document.Folder.Placemark == nil { + return &geojson.FeatureCollection{}, nil + } + var features []*geojson.Feature + for _, pm := range k.Document.Folder.Placemark { + // the KML has a Placemark for an unnecessary LineString + if pm.ExtendedData == nil || len(pm.ExtendedData.Data) == 0 { + continue + } + var id string + c := make([]float64, 3) + p := make(map[string]interface{}) + for _, ed := range pm.ExtendedData.Data { + switch ed.Name { + case FieldID: + id = ed.Value + case FieldLatitude: + x, err := strconv.ParseFloat(ed.Value, 64) + if err != nil { + return nil, err + } + c[1] = x + case FieldLongitude: + x, err := strconv.ParseFloat(ed.Value, 64) + if err != nil { + return nil, err + } + c[0] = x + case FieldElevation: + if m := reElevation.FindStringSubmatch(ed.Value); m != nil { + x, err := strconv.ParseFloat(m[1], 64) + if err != nil { + return nil, err + } + c[2] = x + } + case FieldIMEI: + x, err := strconv.ParseInt(ed.Value, 0, 64) + if err != nil { + return nil, err + } + p[ed.Name] = x + case FieldInEmergency, FieldValidGPS: + x, err := strconv.ParseBool(ed.Value) + if err != nil { + return nil, err + } + p[ed.Name] = x + case FieldTime: + // use the UTC value and convert to local time when displayed + case FieldTimeUTC: + t, err := time.Parse(timeLayout, ed.Value) + if err != nil { + return nil, err + } + p[ed.Name] = t + default: + p[ed.Name] = ed.Value + } + } + + features = append(features, &geojson.Feature{ + ID: id, + Geometry: geom.NewPointFlat(geom.XYZ, c), + Properties: p, + }) + } + + return &geojson.FeatureCollection{Features: features}, nil +} diff --git a/inreach/encoding_test.go b/inreach/encoding_test.go new file mode 100644 index 0000000..e24eb3c --- /dev/null +++ b/inreach/encoding_test.go @@ -0,0 +1,46 @@ +package inreach_test + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/bzimmer/activity/inreach" +) + +func TestEncoding(t *testing.T) { + tests := []struct { + name string + user string + }{ + { + user: "foobar", + name: "query recent feed", + }, + } + for i := range tests { + tt := tests[i] + t.Run(tt.name, func(t *testing.T) { + a := assert.New(t) + mux := http.NewServeMux() + mux.HandleFunc("/Feed/Share/foobar", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "testdata/feed.kml") + }) + svr := httptest.NewServer(mux) + defer svr.Close() + + client, err := inreach.NewClient(inreach.WithBaseURL(svr.URL)) + a.NoError(err) + feed, err := client.Feed.Feed(context.Background(), tt.user) + a.NoError(err) + a.NotNil(feed) + + x, err := feed.GeoJSON() + a.NoError(err) + a.NotNil(x) + }) + } +} diff --git a/inreach/feed.go b/inreach/feed.go index 3acfb49..9284027 100644 --- a/inreach/feed.go +++ b/inreach/feed.go @@ -11,7 +11,7 @@ import ( type FeedService service // Feed returns the feed for the user in the (optionally) specified time range -func (s *FeedService) Feed(ctx context.Context, user string, opts ...APIOption) (*KML, error) { +func (s *FeedService) Feed(ctx context.Context, user string, opts ...APIOption) (*Feed, error) { v := make(url.Values) for _, opt := range opts { if opt == nil { @@ -26,7 +26,7 @@ func (s *FeedService) Feed(ctx context.Context, user string, opts ...APIOption) if err != nil { return nil, err } - var feed KML + var feed Feed err = s.client.do(req, &feed) if err != nil { return nil, err diff --git a/inreach/feed_test.go b/inreach/feed_test.go index 0cd0e5b..24fe44f 100644 --- a/inreach/feed_test.go +++ b/inreach/feed_test.go @@ -67,10 +67,6 @@ func TestFeed(t *testing.T) { feed, err := client.Feed.Feed(context.Background(), tt.user, opt) a.NoError(err) a.NotNil(feed) - - x, err := feed.GeoJSON() - a.NoError(err) - a.NotNil(x) }) } } diff --git a/inreach/inreach_test.go b/inreach/inreach_test.go index 4bc4fd6..c2f38d7 100644 --- a/inreach/inreach_test.go +++ b/inreach/inreach_test.go @@ -43,7 +43,7 @@ func TestClient(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { client, err := inreach.NewClient( - inreach.WithHTTPTracing(true), + inreach.WithHTTPTracing(false), inreach.WithTransport(tt.tripper), inreach.WithHTTPClient(tt.client)) if tt.err != "" { diff --git a/inreach/model.go b/inreach/model.go index 076b558..31469e6 100644 --- a/inreach/model.go +++ b/inreach/model.go @@ -3,11 +3,6 @@ package inreach import ( "encoding/xml" "regexp" - "strconv" - "time" - - "github.com/twpayne/go-geom" - "github.com/twpayne/go-geom/encoding/geojson" ) type Fault struct { @@ -148,7 +143,7 @@ type Document struct { Folder *Folder `xml:"Folder"` } -type KML struct { +type Feed struct { XMLName xml.Name `xml:"kml"` Text string `xml:",chardata"` XSD string `xml:"xsd,attr"` @@ -156,72 +151,3 @@ type KML struct { XMLNS string `xml:"xmlns,attr"` Document *Document `xml:"Document"` } - -func (k *KML) GeoJSON() (*geojson.FeatureCollection, error) { //nolint - var features []*geojson.Feature - for _, pm := range k.Document.Folder.Placemark { - // the KML has a Placemark for an unnecessary LineString - if pm.ExtendedData == nil { - continue - } - var id string - c := make([]float64, 3) - p := make(map[string]interface{}) - for _, ed := range pm.ExtendedData.Data { - switch ed.Name { - case FieldID: - id = ed.Value - case FieldLatitude: - x, err := strconv.ParseFloat(ed.Value, 64) - if err != nil { - return nil, err - } - c[1] = x - case FieldLongitude: - x, err := strconv.ParseFloat(ed.Value, 64) - if err != nil { - return nil, err - } - c[0] = x - case FieldElevation: - if m := reElevation.FindStringSubmatch(ed.Value); m != nil { - x, err := strconv.ParseFloat(m[1], 64) - if err != nil { - return nil, err - } - c[2] = x - } - case FieldIMEI: - x, err := strconv.ParseInt(ed.Value, 0, 64) - if err != nil { - return nil, err - } - p[ed.Name] = x - case FieldInEmergency, FieldValidGPS: - x, err := strconv.ParseBool(ed.Value) - if err != nil { - return nil, err - } - p[ed.Name] = x - case FieldTime: - // use the UTC value and convert to local time when displayed - case FieldTimeUTC: - t, err := time.Parse(timeLayout, ed.Value) - if err != nil { - return nil, err - } - p[ed.Name] = t - default: - p[ed.Name] = ed.Value - } - } - - features = append(features, &geojson.Feature{ - ID: id, - Geometry: geom.NewPointFlat(geom.XYZ, c), - Properties: p, - }) - } - - return &geojson.FeatureCollection{Features: features}, nil -} diff --git a/rwgps/encoding.go b/rwgps/encoding.go index b7f3c59..5aeb905 100644 --- a/rwgps/encoding.go +++ b/rwgps/encoding.go @@ -10,10 +10,10 @@ import ( "github.com/bzimmer/activity" ) -var _ activity.GPXer = (*Trip)(nil) -var _ activity.GeoJSONer = (*Trip)(nil) +var _ activity.GPXEncoder = (*Trip)(nil) +var _ activity.GeoJSONEncoder = (*Trip)(nil) -func (t *Trip) GeoJSON() (*geojson.Feature, error) { +func (t *Trip) GeoJSON() (*geojson.FeatureCollection, error) { layout := geom.XYZ dim, n := layout.Stride(), len(t.TrackPoints) coords := make([]float64, dim*n) @@ -24,16 +24,19 @@ func (t *Trip) GeoJSON() (*geojson.Feature, error) { coords[x+2] = tp.Elevation.Meters() } // @todo add streams - g := &geojson.Feature{ - ID: strconv.FormatInt(t.ID, 10), - Geometry: geom.NewLineStringFlat(layout, coords), - Properties: map[string]interface{}{ - "type": t.Type, - "name": t.Name, - "source": _baseURL, + return &geojson.FeatureCollection{ + Features: []*geojson.Feature{ + { + ID: strconv.FormatInt(t.ID, 10), + Geometry: geom.NewLineStringFlat(layout, coords), + Properties: map[string]interface{}{ + "type": t.Type, + "name": t.Name, + "source": _baseURL, + }, + }, }, - } - return g, nil + }, nil } func (t *Trip) GPX() (*gpx.GPX, error) { diff --git a/rwgps/encoding_test.go b/rwgps/encoding_test.go index d9fc466..e44cde3 100644 --- a/rwgps/encoding_test.go +++ b/rwgps/encoding_test.go @@ -31,8 +31,9 @@ func TestEncodingFromTrip(t *testing.T) { a.NotNil(gpx) a.Equal(1465, len(gpx.Trk[0].TrkSeg[0].TrkPt)) - gj, err := trip.GeoJSON() + fc, err := trip.GeoJSON() a.NoError(err) - a.NotNil(gj) - a.Equal(1465*3, len(gj.Geometry.FlatCoords())) + a.NotNil(fc) + a.Len(fc.Features, 1) + a.Len(fc.Features[0].Geometry.FlatCoords(), 1465*3) } diff --git a/strava/activity_test.go b/strava/activity_test.go index f1813c2..829103c 100644 --- a/strava/activity_test.go +++ b/strava/activity_test.go @@ -252,7 +252,7 @@ func TestActivityTimeout(t *testing.T) { t.Run("timeout lt sleep => failure", func(t *testing.T) { ctx := context.Background() - ctx, cancel := context.WithTimeout(ctx, time.Millisecond*15) + ctx, cancel := context.WithTimeout(ctx, time.Microsecond) defer cancel() act, err := client.Activity.Activity(ctx, 154504250376823) a.Error(err) diff --git a/strava/encoding.go b/strava/encoding.go index d2f258d..0dca6a6 100644 --- a/strava/encoding.go +++ b/strava/encoding.go @@ -12,8 +12,8 @@ import ( "github.com/bzimmer/activity" ) -var _ activity.GPXer = (*Route)(nil) -var _ activity.GPXer = (*Activity)(nil) +var _ activity.GPXEncoder = (*Route)(nil) +var _ activity.GPXEncoder = (*Activity)(nil) func polylineToLineString(polylines ...string) (*geom.LineString, error) { const N = 2 diff --git a/strava/encoding_test.go b/strava/encoding_test.go index a73af85..821942c 100644 --- a/strava/encoding_test.go +++ b/strava/encoding_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestGPXFromRoute(t *testing.T) { +func TestGPXRoute(t *testing.T) { t.Parallel() a := assert.New(t) @@ -79,7 +79,7 @@ func TestGPXFromRoute(t *testing.T) { } } -func TestGPXFromActivity(t *testing.T) { +func TestGPXActivity(t *testing.T) { t.Parallel() a := assert.New(t)