package org.openmhealth.shim.jawbone.mapper; import com.beust.jcommander.internal.Maps; import com.fasterxml.jackson.databind.JsonNode; import org.hamcrest.Matchers; import org.openmhealth.schema.domain.omh.*; import org.springframework.core.io.ClassPathResource; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.io.IOException; import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.List; import java.util.Map; import static java.time.ZoneOffset.UTC; import static java.util.Collections.singletonList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.openmhealth.schema.domain.omh.DataPointModality.SENSED; import static org.openmhealth.schema.domain.omh.DurationUnit.SECOND; import static org.openmhealth.schema.domain.omh.KcalUnit.KILOCALORIE; import static org.openmhealth.schema.domain.omh.LengthUnit.METER; import static org.openmhealth.schema.domain.omh.PhysicalActivity.SelfReportedIntensity.MODERATE; import static org.openmhealth.shim.jawbone.mapper.JawboneDataPointMapper.RESOURCE_API_SOURCE_NAME; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; /** * @author Emerson Farrugia */ public class JawbonePhysicalActivityDataPointMapperUnitTests extends JawboneDataPointMapperUnitTests<PhysicalActivity> { private final JawbonePhysicalActivityDataPointMapper mapper = new JawbonePhysicalActivityDataPointMapper(); private JsonNode responseNode; @BeforeTest public void initializeResponseNodes() throws IOException { ClassPathResource resource = new ClassPathResource("org/openmhealth/shim/jawbone/mapper/jawbone-workouts.json"); responseNode = objectMapper.readTree(resource.getInputStream()); initializeEmptyNode(); } @Test public void asDataPointsShouldReturnNoDataPointsWithEmptyResponse() { testEmptyNode(mapper); } @Test public void asDataPointsShouldReturnCorrectNumberOfDataPoints() { List<DataPoint<PhysicalActivity>> dataPoints = mapper.asDataPoints(singletonList(responseNode)); assertThat(dataPoints, notNullValue()); assertThat(dataPoints.size(), equalTo(3)); } @Test public void asDataPointsShouldReturnCorrectSensedDataPoints() { List<DataPoint<PhysicalActivity>> dataPoints = mapper.asDataPoints(singletonList(responseNode)); assertThat(dataPoints, notNullValue()); assertThat(dataPoints.size(), greaterThan(0)); OffsetDateTime endDateTime = ZonedDateTime.of(2013, 11, 22, 5, 47, 0, 0, UTC) .withZoneSameInstant(ZoneId.of("America/Los_Angeles")).toOffsetDateTime(); TimeInterval effectiveTimeInterval = TimeInterval.ofEndDateTimeAndDuration(endDateTime, new DurationUnitValue(SECOND, 2_460)); PhysicalActivity physicalActivity = new PhysicalActivity.Builder("Run") .setDistance(new LengthUnitValue(METER, 5_116)) .setEffectiveTimeFrame(effectiveTimeInterval) .setReportedActivityIntensity(MODERATE) .setCaloriesBurned(new KcalUnitValue(KILOCALORIE, 634.928678924)) .build(); DataPoint<PhysicalActivity> firstDataPoint = dataPoints.get(0); assertThat(firstDataPoint.getBody(), equalTo(physicalActivity)); DataPointAcquisitionProvenance acquisitionProvenance = firstDataPoint.getHeader().getAcquisitionProvenance(); assertThat(acquisitionProvenance, notNullValue()); assertThat(acquisitionProvenance.getSourceName(), equalTo(RESOURCE_API_SOURCE_NAME)); assertThat(acquisitionProvenance.getAdditionalProperty("external_id").isPresent(), equalTo(true)); assertThat(acquisitionProvenance.getAdditionalProperty("external_id").get(), equalTo("40F7_htRRnT8Vo7nRBZO1X")); assertThat(acquisitionProvenance.getModality(), notNullValue()); assertThat(acquisitionProvenance.getModality(), equalTo(SENSED)); } @Test public void asDataPointsShouldReturnCorrectMissingSensedDataPoints() { List<DataPoint<PhysicalActivity>> dataPoints = mapper.asDataPoints(singletonList(responseNode)); PhysicalActivity expectedPhysicalActivity = new PhysicalActivity.Builder("Bike") .setDistance(new LengthUnitValue(METER, 6318.2688961)) .setEffectiveTimeFrame( TimeInterval.ofEndDateTimeAndDuration(OffsetDateTime.parse("2015-04-29T16:07:07-04:00"), new DurationUnitValue(SECOND, 343))) .setCaloriesBurned(new KcalUnitValue(KILOCALORIE, 27.16863765916)) .build(); assertThat(dataPoints.get(1).getBody(), equalTo(expectedPhysicalActivity)); DataPointHeader testDataPointHeader = dataPoints.get(1).getHeader(); Map<String, Object> testProperties = Maps.newHashMap(); testProperties.put(HEADER_EXTERNAL_ID_KEY, "SbiOBJjJJk8n2xLpNTMFng12pGRjX-qe"); testProperties.put(HEADER_SOURCE_UPDATE_KEY, "2015-04-29T20:07:56Z"); testProperties.put(HEADER_SCHEMA_ID_KEY, PhysicalActivity.SCHEMA_ID); testDataPointHeader(testDataPointHeader, testProperties); } @Test public void asDataPointsShouldReturnDataPointWithoutCaloriesBurnedWhenCaloriesAreMissing() { assertThat(mapper.asDataPoints(responseNode).get(2).getBody().getCaloriesBurned(), nullValue()); } // TODO multiple tests? @Test public void asSelfReportedIntensityShouldReturnCorrectIntensityWhenWithinSpecification() { PhysicalActivity.SelfReportedIntensity selfReportedIntensity = mapper.asSelfReportedIntensity(1); assertThat(selfReportedIntensity, Matchers.equalTo(PhysicalActivity.SelfReportedIntensity.LIGHT)); selfReportedIntensity = mapper.asSelfReportedIntensity(2); assertThat(selfReportedIntensity, Matchers.equalTo(PhysicalActivity.SelfReportedIntensity.MODERATE)); selfReportedIntensity = mapper.asSelfReportedIntensity(3); assertThat(selfReportedIntensity, Matchers.equalTo(PhysicalActivity.SelfReportedIntensity.MODERATE)); selfReportedIntensity = mapper.asSelfReportedIntensity(4); assertThat(selfReportedIntensity, Matchers.equalTo(PhysicalActivity.SelfReportedIntensity.VIGOROUS)); selfReportedIntensity = mapper.asSelfReportedIntensity(5); assertThat(selfReportedIntensity, Matchers.equalTo(PhysicalActivity.SelfReportedIntensity.VIGOROUS)); } @Test(expectedExceptions = IllegalArgumentException.class) public void asSelfReportedIntensityShouldThrowExceptionWhenOutsideSpecification() { mapper.asSelfReportedIntensity(6); } // TODO experiment with formatting and quote replacement @Test public void isSensedShouldReturnTrueWhenStepsArePresent() throws IOException { JsonNode nodeWithSteps = objectMapper.readTree("{\n" + "\"details\": {\n" + "\"steps\": 5128,\n" + "\"time\": 2460\n" + "}\n" + "}"); assertTrue(mapper.isSensed(nodeWithSteps)); } @Test public void isSensedShouldReturnFalseWhenStepsAreNotPresent() throws IOException { JsonNode nodeWithSteps = objectMapper.readTree("{\n" + "\"details\": {\n" + "\"steps\": 0,\n" + "\"time\": 2460\n" + "}\n" + "}"); assertFalse(mapper.isSensed(nodeWithSteps)); nodeWithSteps = objectMapper.readTree("{\n" + "\"details\": {\n" + "\"time\": 2460\n" + "}\n" + "}"); assertFalse(mapper.isSensed(nodeWithSteps)); } @Test public void getActivityNameShouldReturnTitleWhenPresent() { String activityName = mapper.getActivityName("run", 15); assertThat(activityName, equalTo("run")); } @Test public void getActivityNameShouldReturnCorrectSubtypeWhenTitleIsMissing() { String activityName = mapper.getActivityName(null, 9); assertThat(activityName, equalTo("crossfit")); activityName = mapper.getActivityName(null, 29); assertThat(activityName, equalTo("workout")); } @Test public void getActivityNameShouldReturnGenericWorkoutNameWhenSubtypeAndTitleAreMissing() { String activityName = mapper.getActivityName(null, null); assertThat(activityName, equalTo("workout")); } }