package com.sweetzpot.stravazpot.activity.api; import com.sweetzpot.stravazpot.activity.model.Achievement; import com.sweetzpot.stravazpot.activity.model.AchievementType; import com.sweetzpot.stravazpot.activity.model.Activity; import com.sweetzpot.stravazpot.activity.model.ActivityLap; import com.sweetzpot.stravazpot.activity.model.ActivityType; import com.sweetzpot.stravazpot.activity.model.ActivityZone; import com.sweetzpot.stravazpot.activity.model.PhotoSource; import com.sweetzpot.stravazpot.activity.model.PhotoSummary; import com.sweetzpot.stravazpot.activity.model.Split; import com.sweetzpot.stravazpot.activity.model.WorkoutType; import com.sweetzpot.stravazpot.common.api.StravaAPITest; import com.sweetzpot.stravazpot.common.model.Coordinates; import com.sweetzpot.stravazpot.common.model.Distance; import com.sweetzpot.stravazpot.common.model.ResourceState; import com.sweetzpot.stravazpot.common.model.Speed; import com.sweetzpot.stravazpot.common.model.Temperature; import com.sweetzpot.stravazpot.common.model.Time; import org.junit.Test; import java.util.Calendar; import java.util.List; import static com.sweetzpot.stravazpot.matchers.DateMatcher.isSameDate; import static com.sweetzpot.stravazpot.util.DateUtil.makeDate; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class ActivityAPITest extends StravaAPITest { @Test public void shouldCreateAnActivity() throws Exception { enqueueActivity(); ActivityAPI activityAPI = givenAnActivityAPI(); Activity activity = activityAPI.createActivity("Morning run") .ofType(ActivityType.RUN) .startingOn(makeDate(3, Calendar.OCTOBER, 2016, 9, 5, 43)) .withElapsedTime(Time.seconds(943)) .withDescription("A tiring session") .withDistance(Distance.meters(2789)) .isPrivate(false) .withTrainer(true) .withCommute(false) .execute(); assertRequestBodyContains( "name=Morning%20run", "type=Run", "start_date_local=2016-10-03T09%3A05%3A43Z", "elapsed_time=943", "description=A%20tiring%20session", "distance=2789", "private=0", "trainer=1", "commute=0" ); } @Test public void shouldRetrieveAnActivity() throws Exception { enqueueActivity(); ActivityAPI activityAPI = givenAnActivityAPI(); Activity activity = activityAPI.getActivity(321934) .includeAllEfforts(true) .execute(); assertRequestPathContains( "/activities/321934", "include_all_efforts=true" ); assertActivityParsedCorrectly(activity); } @Test public void shoudlUpdateAnActivity() throws Exception { enqueueActivity(); ActivityAPI activityAPI = givenAnActivityAPI(); Activity activity = activityAPI.updateActivity(321934) .changeName("Afternoon ride") .changeType(ActivityType.RIDE) .changePrivate(true) .changeCommute(true) .changeTrainer(true) .changeGearID("b321934") .changeDescription("A relaxing ride") .execute(); assertRequestBodyContains( "name=Afternoon%20ride", "type=Ride", "private=true", "commute=true", "trainer=true", "gear_id=b321934", "description=A%20relaxing%20ride" ); } @Test public void shouldDeleteAnActivity() throws Exception { enqueueResponse(204, ""); ActivityAPI activityAPI = givenAnActivityAPI(); activityAPI.deleteActivity(321934) .execute(); assertRequestPathContains("/activities/321934"); } @Test public void shouldListUsersActivities() throws Exception { enqueueResponse("[]"); ActivityAPI activityAPI = givenAnActivityAPI(); List<Activity> activities = activityAPI.listMyActivities() .before(Time.seconds(123456789)) .after(Time.seconds(130000000)) .inPage(2) .perPage(10) .execute(); assertRequestPathContains( "/athlete/activities", "before=123456789", "after=130000000", "page=2", "per_page=10" ); } @Test public void shouldListFriendsActivities() throws Exception { enqueueResponse("[]"); ActivityAPI activityAPI = givenAnActivityAPI(); List<Activity> activities = activityAPI.listFriendActivities() .before(Time.seconds(123456789)) .inPage(2) .perPage(10) .execute(); assertRequestPathContains( "/activities/following", "before=123456789", "page=2", "per_page=10" ); } @Test public void shouldListRelatedActivities() throws Exception { enqueueResponse("[]"); ActivityAPI activityAPI = givenAnActivityAPI(); List<Activity> activities = activityAPI.listRelatedActivities(321934) .inPage(2) .perPage(10) .execute(); assertRequestPathContains( "/activities/321934/related", "page=2", "per_page=10" ); } @Test public void shouldListActivityZones() throws Exception { enqueueActivityZones(); ActivityAPI activityAPI = givenAnActivityAPI(); List<ActivityZone> activityZones = activityAPI.listActivityZones(321934) .execute(); assertRequestPathContains("/activities/321934/zones"); assertActivityZonesParsedCorrectly(activityZones); } @Test public void shouldListActivityLaps() throws Exception { enqueueActivityLaps(); ActivityAPI activityAPI = givenAnActivityAPI(); List<ActivityLap> laps = activityAPI.listActivityLaps(321934) .execute(); assertRequestPathContains("/activities/321934/laps"); assertActivityLapsParsedCorrectly(laps); } private ActivityAPI givenAnActivityAPI() { return new ActivityAPI(givenAValidConfig()); } private void assertActivityParsedCorrectly(Activity activity) { assertThat(activity.getID(), is(321934)); assertThat(activity.getResourceState(), is(ResourceState.DETAILED)); assertThat(activity.getExternalID(), is("2012-12-12_21-40-32-80-29011.fit")); assertThat(activity.getUploadID(), is(361720)); assertThat(activity.getAthlete(), is(notNullValue())); assertThat(activity.getName(), is("Evening Ride")); assertThat(activity.getDescription(), is("the best ride ever")); assertThat(activity.getDistance(), is(equalTo(Distance.meters(4475.4f)))); assertThat(activity.getMovingTime(), is(equalTo(Time.seconds(1303)))); assertThat(activity.getElapsedTime(), is(equalTo(Time.seconds(1333)))); assertThat(activity.getTotalElevationGain(), is(equalTo(Distance.meters(154.5f)))); assertThat(activity.getElevationHigh(), is(equalTo(Distance.meters(331.4f)))); assertThat(activity.getElevationLow(), is(equalTo(Distance.meters(276.1f)))); assertThat(activity.getType(), is(ActivityType.RUN)); assertThat(activity.getStartDate(), isSameDate(makeDate(13, Calendar.DECEMBER, 2012, 3, 43, 19))); assertThat(activity.getStartDateLocal(), isSameDate(makeDate(12, Calendar.DECEMBER, 2012, 19, 43, 19))); assertThat(activity.getTimezone(), is("(GMT-08:00) America/Los_Angeles")); assertThat(activity.getStartCoordinates(), is(equalTo(Coordinates.at(37.8f, -122.27f)))); assertThat(activity.getEndCoordinates(), is(equalTo(Coordinates.at(37.8f, -122.27f)))); assertThat(activity.getAchievementCount(), is(6)); assertThat(activity.getKudosCount(), is(1)); assertThat(activity.getCommentCount(), is(1)); assertThat(activity.getAthleteCount(), is(1)); assertThat(activity.getPhotoCount(), is(0)); assertThat(activity.getTotalPhotoCount(), is(0)); assertThat(activity.getPhotos(), is(notNullValue())); assertPhotoSummaryParsedCorrectly(activity.getPhotos()); assertThat(activity.getMap(), is(notNullValue())); assertThat(activity.isTrainer(), is(false)); assertThat(activity.isCommute(), is(false)); assertThat(activity.isManual(), is(false)); assertThat(activity.isPrivate(), is(false)); assertThat(activity.isFlagged(), is(false)); assertThat(activity.getWorkoutType(), is(WorkoutType.RUN_LONGRUN)); assertThat(activity.getGear(), is(notNullValue())); assertThat(activity.getAverageSpeed(), is(equalTo(Speed.metersPerSecond(3.4f)))); assertThat(activity.getAverageTemperature(), is(equalTo(Temperature.celsiusDegrees(36.2f)))); assertThat(activity.getMaxSpeed(), is(equalTo(Speed.metersPerSecond(4.514f)))); assertThat(activity.getCalories(), is(390.5f)); assertThat(activity.hasKudoed(), is(false)); assertThat(activity.getSegmentEfforts(), is(notNullValue())); assertAchievementsParsedCorrectly(activity.getSegmentEfforts().get(0).getAchievements().get(0)); assertThat(activity.getSplitsMetric().size(), is(2)); assertSplitParsedCorrectly(activity.getSplitsMetric().get(0)); assertThat(activity.getSplitsStandard(), is(notNullValue())); assertThat(activity.getBestEfforts(), is(notNullValue())); } private void assertPhotoSummaryParsedCorrectly(PhotoSummary photos) { assertThat(photos.getCount(), is(2)); assertThat(photos.getPrimaryPhoto().getUniqueID(), is("d64643ec9205")); assertThat(photos.getPrimaryPhoto().getUrls().keySet().size(), is(2)); assertThat(photos.getPrimaryPhoto().getSource(), is(PhotoSource.STRAVA)); } private void assertSplitParsedCorrectly(Split split) { assertThat(split.getDistance(), is(equalTo(Distance.meters(1002.5f)))); assertThat(split.getElapsedTime(), is(equalTo(Time.seconds(276)))); assertThat(split.getElevationDifference(), is(equalTo(Distance.meters(0)))); assertThat(split.getMovingTime(), is(equalTo(Time.seconds(276)))); assertThat(split.getSplit(), is(1)); } private void assertAchievementsParsedCorrectly(Achievement achievement) { assertThat(achievement.getTypeID(), is(AchievementType.OVERALL)); assertThat(achievement.getType(), is("overall")); assertThat(achievement.getRank(), is(2)); } private void enqueueActivity() { String activityJSON = "{\n" + " \"id\": 321934,\n" + " \"resource_state\": 3,\n" + " \"external_id\": \"2012-12-12_21-40-32-80-29011.fit\",\n" + " \"upload_id\": 361720,\n" + " \"athlete\": {\n" + " \"id\": 227615,\n" + " \"resource_state\": 1\n" + " },\n" + " \"name\": \"Evening Ride\",\n" + " \"description\": \"the best ride ever\",\n" + " \"distance\": 4475.4,\n" + " \"moving_time\": 1303,\n" + " \"elapsed_time\": 1333,\n" + " \"total_elevation_gain\": 154.5,\n" + " \"elev_high\": 331.4,\n" + " \"elev_low\": 276.1,\n" + " \"type\": \"Run\",\n" + " \"start_date\": \"2012-12-13T03:43:19Z\",\n" + " \"start_date_local\": \"2012-12-12T19:43:19Z\",\n" + " \"timezone\": \"(GMT-08:00) America/Los_Angeles\",\n" + " \"start_latlng\": [\n" + " 37.8,\n" + " -122.27\n" + " ],\n" + " \"end_latlng\": [\n" + " 37.8,\n" + " -122.27\n" + " ],\n" + " \"achievement_count\": 6,\n" + " \"kudos_count\": 1,\n" + " \"comment_count\": 1,\n" + " \"athlete_count\": 1,\n" + " \"photo_count\": 0,\n" + " \"total_photo_count\": 0,\n" + " \"photos\": {\n" + " \"count\": 2,\n" + " \"primary\": {\n" + " \"id\": null,\n" + " \"source\": 1,\n" + " \"unique_id\": \"d64643ec9205\",\n" + " \"urls\": {\n" + " \"100\": \"http://pics.com/28b9d28f-128x71.jpg\",\n" + " \"600\": \"http://pics.com/28b9d28f-768x431.jpg\"\n" + " }\n" + " }\n" + " },\n" + " \"map\": {\n" + " \"id\": \"a32193479\",\n" + " \"polyline\": \"kiteFpCBCD]\",\n" + " \"summary_polyline\": \"{cteFjcaBkCx@gEz@\",\n" + " \"resource_state\": 3\n" + " },\n" + " \"trainer\": false,\n" + " \"commute\": false,\n" + " \"manual\": false,\n" + " \"private\": false,\n" + " \"flagged\": false,\n" + " \"workout_type\": 2,\n" + " \"gear\": {\n" + " \"id\": \"g138727\",\n" + " \"primary\": true,\n" + " \"name\": \"Nike Air\",\n" + " \"distance\": 88983.1,\n" + " \"resource_state\": 2\n" + " },\n" + " \"average_speed\": 3.4,\n" + " \"average_temp\": 36.2,\n" + " \"max_speed\": 4.514,\n" + " \"calories\": 390.5,\n" + " \"has_kudoed\": false,\n" + " \"segment_efforts\": [\n" + " {\n" + " \"id\": 543755075,\n" + " \"resource_state\": 2,\n" + " \"name\": \"Dash for the Ferry\",\n" + " \"segment\": {\n" + " \"id\": 2417854,\n" + " \"resource_state\": 2,\n" + " \"name\": \"Dash for the Ferry\",\n" + " \"activity_type\": \"Run\",\n" + " \"distance\": 1055.11,\n" + " \"average_grade\": -0.1,\n" + " \"maximum_grade\": 2.7,\n" + " \"elevation_high\": 4.7,\n" + " \"elevation_low\": 2.7,\n" + " \"start_latlng\": [\n" + " 37.7905785,\n" + " -122.27015622\n" + " ],\n" + " \"end_latlng\": [\n" + " 37.79536649,\n" + " -122.2796434\n" + " ],\n" + " \"climb_category\": 0,\n" + " \"city\": \"Oakland\",\n" + " \"state\": \"CA\",\n" + " \"country\": \"United States\",\n" + " \"private\": false\n" + " },\n" + " \"activity\": {\n" + " \"id\": 32193479,\n" + " \"resource_state\": 1\n" + " },\n" + " \"athlete\": {\n" + " \"id\": 3776,\n" + " \"resource_state\": 1\n" + " },\n" + " \"kom_rank\": 2,\n" + " \"pr_rank\": 1,\n" + " \"elapsed_time\": 304,\n" + " \"moving_time\": 304,\n" + " \"start_date\": \"2012-12-13T03:48:14Z\",\n" + " \"start_date_local\": \"2012-12-12T19:48:14Z\",\n" + " \"distance\": 1052.33,\n" + " \"start_index\": 5348,\n" + " \"end_index\": 6485,\n" + " \"hidden\": false,\n" + " \"achievements\": [\n" + " {\n" + " \"type_id\": 2,\n" + " \"type\": \"overall\",\n" + " \"rank\": 2\n" + " },\n" + " {\n" + " \"type_id\": 3,\n" + " \"type\": \"pr\",\n" + " \"rank\": 1\n" + " }\n" + " ]\n" + " }\n" + " ],\n" + " \"splits_metric\": [\n" + " {\n" + " \"distance\": 1002.5,\n" + " \"elapsed_time\": 276,\n" + " \"elevation_difference\": 0,\n" + " \"moving_time\": 276,\n" + " \"split\": 1\n" + " },\n" + " {\n" + " \"distance\": 475.7,\n" + " \"elapsed_time\": 139,\n" + " \"elevation_difference\": 0,\n" + " \"moving_time\": 139,\n" + " \"split\": 5\n" + " }\n" + " ],\n" + " \"splits_standard\": [\n" + " {\n" + " \"distance\": 1255.9,\n" + " \"elapsed_time\": 382,\n" + " \"elevation_difference\": 3.2,\n" + " \"moving_time\": 382,\n" + " \"split\": 3\n" + " }\n" + " ],\n" + " \"best_efforts\": [\n" + " {\n" + " \"id\": 273063933,\n" + " \"resource_state\": 2,\n" + " \"name\": \"400m\",\n" + " \"segment\": null,\n" + " \"activity\": {\n" + " \"id\": 32193479\n" + " },\n" + " \"athlete\": {\n" + " \"id\": 3776\n" + " },\n" + " \"kom_rank\": null,\n" + " \"pr_rank\": null,\n" + " \"elapsed_time\": 105,\n" + " \"moving_time\": 106,\n" + " \"start_date\": \"2012-12-13T03:43:19Z\",\n" + " \"start_date_local\": \"2012-12-12T19:43:19Z\",\n" + " \"distance\": 400,\n" + " \"achievements\": [\n" + "\n" + " ]\n" + " },\n" + " {\n" + " \"id\": 273063935,\n" + " \"resource_state\": 2,\n" + " \"name\": \"1/2 mile\",\n" + " \"segment\": null,\n" + " \"activity\": {\n" + " \"id\": 32193479\n" + " },\n" + " \"athlete\": {\n" + " \"id\": 3776\n" + " },\n" + " \"kom_rank\": null,\n" + " \"pr_rank\": null,\n" + " \"elapsed_time\": 219,\n" + " \"moving_time\": 220,\n" + " \"start_date\": \"2012-12-13T03:43:19Z\",\n" + " \"start_date_local\": \"2012-12-12T19:43:19Z\",\n" + " \"distance\": 805,\n" + " \"achievements\": [\n" + "\n" + " ]\n" + " }\n" + " ]\n" + "}"; enqueueResponse(activityJSON); } private void assertActivityZonesParsedCorrectly(List<ActivityZone> activityZones) { assertThat(activityZones.size(), is(2)); ActivityZone zone = activityZones.get(0); assertThat(zone.getScore(), is(215)); assertThat(zone.getDistributionBuckets().size(), is(5)); assertThat((double) (zone.getDistributionBuckets().get(0).getMin()), is(0.0)); assertThat((double) (zone.getDistributionBuckets().get(0).getMax()), is(115.0)); assertThat(zone.getDistributionBuckets().get(0).getTime(), is(1735L)); assertThat(zone.getType(), is("heartrate")); assertThat(zone.getResourceState(), is(ResourceState.DETAILED)); assertThat(zone.isSensorBased(), is(true)); assertThat(zone.getPoints(), is(119)); assertThat(zone.hasCustomZones(), is(false)); assertThat(zone.getMax(), is(196)); } private void enqueueActivityZones() { String activityZonesJSON = "[\n" + " {\n" + " \"score\": 215,\n" + " \"distribution_buckets\": [\n" + " { \"min\": 0, \"max\":115, \"time\": 1735 },\n" + " { \"min\": 115, \"max\": 152, \"time\": 5966 },\n" + " { \"min\": 152, \"max\": 171, \"time\": 4077 },\n" + " { \"min\": 171, \"max\": 190, \"time\": 4238 },\n" + " { \"min\": 190, \"max\": -1, \"time\": 36 }\n" + " ],\n" + " \"type\": \"heartrate\",\n" + " \"resource_state\": 3,\n" + " \"sensor_based\": true,\n" + " \"points\": 119,\n" + " \"custom_zones\": false,\n" + " \"max\": 196\n" + " },\n" + " {\n" + " \"distribution_buckets\": [\n" + " { \"min\": 0, \"max\": 0, \"time\": 3043 },\n" + " { \"min\": 0, \"max\": 50, \"time\": 999 },\n" + " { \"min\": 50, \"max\": 100, \"time\": 489 },\n" + " { \"min\": 100, \"max\": 150, \"time\": 737 },\n" + " { \"min\": 150, \"max\": 200, \"time\": 1299 },\n" + " { \"min\": 200, \"max\": 250, \"time\": 1478 },\n" + " { \"min\": 250, \"max\": 300, \"time\": 1523 },\n" + " { \"min\": 300, \"max\": 350, \"time\": 2154 },\n" + " { \"min\": 350, \"max\": 400, \"time\": 2226 },\n" + " { \"min\": 400, \"max\": 450, \"time\": 1181 },\n" + " { \"min\": 450, \"max\": -1, \"time\": 923 }\n" + " ],\n" + " \"type\": \"power\",\n" + " \"resource_state\": 3,\n" + " \"sensor_based\": true\n" + " }\n" + "]"; enqueueResponse(activityZonesJSON); } private void assertActivityLapsParsedCorrectly(List<ActivityLap> laps) { assertThat(laps.size(), is(1)); ActivityLap lap = laps.get(0); assertThat(lap.getID(), is(401614652)); assertThat(lap.getResourceState(), is(ResourceState.SUMMARY)); assertThat(lap.getName(), is("Lap 1")); assertThat(lap.getActivity(), is(notNullValue())); assertThat(lap.getAthlete(), is(notNullValue())); assertThat(lap.getElapsedTime(), is(equalTo(Time.seconds(7092)))); assertThat(lap.getMovingTime(), is(equalTo(Time.seconds(6870)))); assertThat(lap.getStartDate(), isSameDate(makeDate(2, Calendar.NOVEMBER, 2013, 17, 39, 37))); assertThat(lap.getStartDateLocal(), isSameDate(makeDate(2, Calendar.NOVEMBER, 2013, 10, 39, 37))); assertThat(lap.getDistance(), is(equalTo(Distance.meters(42121.5f)))); assertThat(lap.getStartIndex(), is(0)); assertThat(lap.getEndIndex(), is(6826)); assertThat(lap.getTotalElevationGain(), is(equalTo(Distance.meters(766.0f)))); assertThat(lap.getMaxSpeed(), is(equalTo(Speed.metersPerSecond(16.8f)))); assertThat(lap.getAverageSpeed(), is(equalTo(Speed.metersPerSecond(5.9f)))); assertThat(lap.getAverageCadence(), is(64.7f)); assertThat(lap.getAverageWatts(), is(156.2f)); assertThat(lap.isDeviceWatts(), is(false)); assertThat(lap.hasHeartRate(), is(true)); assertThat(lap.getAverageHeartRate(), is(141.1f)); assertThat(lap.getMaxHeartRate(), is(176.0f)); assertThat(lap.getLapIndex(), is(1)); } private void enqueueActivityLaps() { String activityLapsJSON = "[\n" + " {\n" + " \"id\": 401614652,\n" + " \"resource_state\": 2,\n" + " \"name\": \"Lap 1\",\n" + " \"activity\": {\n" + " \"id\": 123\n" + " },\n" + " \"athlete\": {\n" + " \"id\": 227615\n" + " },\n" + " \"elapsed_time\": 7092,\n" + " \"moving_time\": 6870,\n" + " \"start_date\": \"2013-11-02T17:39:37Z\",\n" + " \"start_date_local\": \"2013-11-02T10:39:37Z\",\n" + " \"distance\": 42121.5,\n" + " \"start_index\": 0,\n" + " \"end_index\": 6826,\n" + " \"total_elevation_gain\": 766.0,\n" + " \"average_speed\": 5.9,\n" + " \"max_speed\": 16.8,\n" + " \"average_cadence\": 64.7,\n" + " \"average_watts\": 156.2,\n" + " \"device_watts\": false,\n" + " \"has_heartrate\": true,\n" + " \"average_heartrate\": 141.1,\n" + " \"max_heartrate\": 176.0,\n" + " \"lap_index\": 1\n" + " }\n" + "]"; enqueueResponse(activityLapsJSON); } }