Fix Weather API access
This commit is contained in:
parent
054fa58290
commit
9f13e89632
3 changed files with 495 additions and 5 deletions
|
|
@ -862,11 +862,12 @@ For ActivityPub federated posts and thumbnails:
|
|||
|
||||
### Phase 6: Testing & Documentation
|
||||
|
||||
**Testing:** ✅ **77 tests passing**
|
||||
**Testing:** ✅ **95 tests passing**
|
||||
- [x] Unit tests for TrainingLoadService (10 tests - TSS, ATL, CTL, TSB calculations)
|
||||
- [x] Unit tests for PersonalRecordService (13 tests - all PR types, improvement detection)
|
||||
- [x] Unit tests for AchievementService (16 tests - all badge types, edge cases)
|
||||
- [x] Unit tests for FitFileService (10 tests - existing tests updated and fixed)
|
||||
- [x] Unit tests for WeatherService (18 tests - lat/lon vs latitude/longitude field names, configuration, API errors, parsing)
|
||||
- [x] Integration tests for ActivityController (10 tests - full stack HTTP to database)
|
||||
- [ ] Integration tests for ActivityPub federation endpoints
|
||||
- [ ] Integration tests for WebFinger discovery
|
||||
|
|
|
|||
|
|
@ -99,8 +99,11 @@ public class WeatherService {
|
|||
JsonNode firstPoint = trackPoints.get(0);
|
||||
log.info("First track point JSON: {}", firstPoint.toString());
|
||||
|
||||
// Check if lat/lon fields exist
|
||||
if (!firstPoint.has("lat") || !firstPoint.has("lon")) {
|
||||
// Check if lat/lon fields exist (support both "lat"/"lon" and "latitude"/"longitude")
|
||||
boolean hasLat = firstPoint.has("lat") || firstPoint.has("latitude");
|
||||
boolean hasLon = firstPoint.has("lon") || firstPoint.has("longitude");
|
||||
|
||||
if (!hasLat || !hasLon) {
|
||||
// Collect field names from iterator
|
||||
java.util.List<String> fieldNames = new java.util.ArrayList<>();
|
||||
firstPoint.fieldNames().forEachRemaining(fieldNames::add);
|
||||
|
|
@ -111,8 +114,9 @@ public class WeatherService {
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
double lat = firstPoint.get("lat").asDouble();
|
||||
double lon = firstPoint.get("lon").asDouble();
|
||||
// Extract coordinates (try both short and long field names)
|
||||
double lat = firstPoint.has("lat") ? firstPoint.get("lat").asDouble() : firstPoint.get("latitude").asDouble();
|
||||
double lon = firstPoint.has("lon") ? firstPoint.get("lon").asDouble() : firstPoint.get("longitude").asDouble();
|
||||
log.info("Extracted location from first track point: lat={}, lon={}", lat, lon);
|
||||
|
||||
// Check if activity is recent (within 5 days) - use current weather API
|
||||
|
|
|
|||
|
|
@ -0,0 +1,485 @@
|
|||
package org.operaton.fitpub.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.operaton.fitpub.model.entity.Activity;
|
||||
import org.operaton.fitpub.model.entity.WeatherData;
|
||||
import org.operaton.fitpub.repository.WeatherDataRepository;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.ResourceAccessException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URI;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Unit tests for WeatherService.
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class WeatherServiceTest {
|
||||
|
||||
@Mock
|
||||
private WeatherDataRepository weatherDataRepository;
|
||||
|
||||
@Mock
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Spy
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@InjectMocks
|
||||
private WeatherService weatherService;
|
||||
|
||||
private Activity testActivity;
|
||||
private UUID activityId;
|
||||
|
||||
// Sample OpenWeatherMap API response
|
||||
private static final String SAMPLE_WEATHER_RESPONSE = """
|
||||
{
|
||||
"coord": {"lon": 8.2552, "lat": 49.9894},
|
||||
"weather": [
|
||||
{
|
||||
"id": 800,
|
||||
"main": "Clear",
|
||||
"description": "clear sky",
|
||||
"icon": "01d"
|
||||
}
|
||||
],
|
||||
"base": "stations",
|
||||
"main": {
|
||||
"temp": 15.5,
|
||||
"feels_like": 14.2,
|
||||
"temp_min": 13.0,
|
||||
"temp_max": 17.0,
|
||||
"pressure": 1013,
|
||||
"humidity": 65
|
||||
},
|
||||
"visibility": 10000,
|
||||
"wind": {
|
||||
"speed": 3.5,
|
||||
"deg": 180
|
||||
},
|
||||
"clouds": {
|
||||
"all": 20
|
||||
},
|
||||
"dt": 1700758089,
|
||||
"sys": {
|
||||
"type": 2,
|
||||
"id": 2012516,
|
||||
"country": "DE",
|
||||
"sunrise": 1700721600,
|
||||
"sunset": 1700757600
|
||||
},
|
||||
"timezone": 3600,
|
||||
"id": 2873891,
|
||||
"name": "Mannheim",
|
||||
"cod": 200
|
||||
}
|
||||
""";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
activityId = UUID.randomUUID();
|
||||
testActivity = new Activity();
|
||||
testActivity.setId(activityId);
|
||||
testActivity.setStartedAt(LocalDateTime.now().minusDays(1)); // Recent activity
|
||||
|
||||
// Inject the real RestTemplate mock and set config values
|
||||
ReflectionTestUtils.setField(weatherService, "restTemplate", restTemplate);
|
||||
ReflectionTestUtils.setField(weatherService, "weatherEnabled", true);
|
||||
ReflectionTestUtils.setField(weatherService, "apiKey", "test-api-key-12345678901234567890");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should successfully fetch weather with SHORT field names (lat/lon)")
|
||||
void testFetchWeather_ShortFieldNames() {
|
||||
// Track points with SHORT field names
|
||||
String trackPointsJson = """
|
||||
[
|
||||
{
|
||||
"timestamp": "2025-11-23T18:08:09",
|
||||
"lat": 49.98939173296094,
|
||||
"lon": 8.255225038155913,
|
||||
"elevation": 100.5,
|
||||
"heartRate": 116
|
||||
}
|
||||
]
|
||||
""";
|
||||
testActivity.setTrackPointsJson(trackPointsJson);
|
||||
|
||||
when(weatherDataRepository.existsByActivityId(activityId)).thenReturn(false);
|
||||
when(restTemplate.getForObject(any(URI.class), eq(String.class)))
|
||||
.thenReturn(SAMPLE_WEATHER_RESPONSE);
|
||||
when(weatherDataRepository.save(any(WeatherData.class)))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
WeatherData weatherData = result.get();
|
||||
assertEquals(activityId, weatherData.getActivityId());
|
||||
assertEquals(new BigDecimal("15.5"), weatherData.getTemperatureCelsius());
|
||||
assertEquals("Clear", weatherData.getWeatherCondition());
|
||||
|
||||
verify(restTemplate, times(1)).getForObject(any(URI.class), eq(String.class));
|
||||
verify(weatherDataRepository, times(1)).save(any(WeatherData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should successfully fetch weather with LONG field names (latitude/longitude)")
|
||||
void testFetchWeather_LongFieldNames() {
|
||||
// Track points with LONG field names (as used in production)
|
||||
String trackPointsJson = """
|
||||
[
|
||||
{
|
||||
"timestamp": "2025-11-23T18:08:09",
|
||||
"latitude": 49.98939173296094,
|
||||
"longitude": 8.255225038155913,
|
||||
"elevation": null,
|
||||
"heartRate": 116,
|
||||
"cadence": null,
|
||||
"power": null,
|
||||
"speed": null,
|
||||
"temperature": null,
|
||||
"distance": null
|
||||
}
|
||||
]
|
||||
""";
|
||||
testActivity.setTrackPointsJson(trackPointsJson);
|
||||
|
||||
when(weatherDataRepository.existsByActivityId(activityId)).thenReturn(false);
|
||||
when(restTemplate.getForObject(any(URI.class), eq(String.class)))
|
||||
.thenReturn(SAMPLE_WEATHER_RESPONSE);
|
||||
when(weatherDataRepository.save(any(WeatherData.class)))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
WeatherData weatherData = result.get();
|
||||
assertEquals(activityId, weatherData.getActivityId());
|
||||
assertEquals(new BigDecimal("15.5"), weatherData.getTemperatureCelsius());
|
||||
assertEquals(new BigDecimal("14.2"), weatherData.getFeelsLikeCelsius());
|
||||
assertEquals(65, weatherData.getHumidity());
|
||||
assertEquals(1013, weatherData.getPressure());
|
||||
assertEquals(new BigDecimal("3.5"), weatherData.getWindSpeedMps());
|
||||
assertEquals("clear sky", weatherData.getWeatherDescription());
|
||||
|
||||
verify(restTemplate, times(1)).getForObject(any(URI.class), eq(String.class));
|
||||
verify(weatherDataRepository, times(1)).save(any(WeatherData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return empty when weather is disabled in config")
|
||||
void testFetchWeather_Disabled() {
|
||||
ReflectionTestUtils.setField(weatherService, "weatherEnabled", false);
|
||||
testActivity.setTrackPointsJson("[{\"lat\":50.0,\"lon\":8.0}]");
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
verify(weatherDataRepository, never()).save(any(WeatherData.class));
|
||||
verify(restTemplate, never()).getForObject(any(URI.class), eq(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return empty when API key is not configured")
|
||||
void testFetchWeather_NoApiKey() {
|
||||
ReflectionTestUtils.setField(weatherService, "apiKey", "");
|
||||
testActivity.setTrackPointsJson("[{\"lat\":50.0,\"lon\":8.0}]");
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
verify(weatherDataRepository, never()).save(any(WeatherData.class));
|
||||
verify(restTemplate, never()).getForObject(any(URI.class), eq(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return cached weather if it already exists")
|
||||
void testFetchWeather_Cached() {
|
||||
testActivity.setTrackPointsJson("[{\"latitude\":50.0,\"longitude\":8.0}]");
|
||||
|
||||
WeatherData cachedWeather = new WeatherData();
|
||||
cachedWeather.setActivityId(activityId);
|
||||
cachedWeather.setTemperatureCelsius(new BigDecimal("20.0"));
|
||||
|
||||
when(weatherDataRepository.existsByActivityId(activityId)).thenReturn(true);
|
||||
when(weatherDataRepository.findByActivityId(activityId)).thenReturn(Optional.of(cachedWeather));
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
assertEquals(new BigDecimal("20.0"), result.get().getTemperatureCelsius());
|
||||
verify(restTemplate, never()).getForObject(any(URI.class), eq(String.class));
|
||||
verify(weatherDataRepository, never()).save(any(WeatherData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return empty when track points JSON is null")
|
||||
void testFetchWeather_NoTrackPoints() {
|
||||
testActivity.setTrackPointsJson(null);
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
verify(weatherDataRepository, never()).save(any(WeatherData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return empty when track points JSON is empty")
|
||||
void testFetchWeather_EmptyTrackPoints() {
|
||||
testActivity.setTrackPointsJson("");
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
verify(weatherDataRepository, never()).save(any(WeatherData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return empty when track points array is empty")
|
||||
void testFetchWeather_EmptyArray() {
|
||||
testActivity.setTrackPointsJson("[]");
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
verify(weatherDataRepository, never()).save(any(WeatherData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return empty when track points missing lat/lon fields")
|
||||
void testFetchWeather_MissingCoordinates() {
|
||||
String trackPointsJson = """
|
||||
[
|
||||
{
|
||||
"timestamp": "2025-11-23T18:08:09",
|
||||
"elevation": 100.5,
|
||||
"heartRate": 116
|
||||
}
|
||||
]
|
||||
""";
|
||||
testActivity.setTrackPointsJson(trackPointsJson);
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
verify(weatherDataRepository, never()).save(any(WeatherData.class));
|
||||
verify(restTemplate, never()).getForObject(any(URI.class), eq(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return empty for old activities (>5 days)")
|
||||
void testFetchWeather_OldActivity() {
|
||||
testActivity.setStartedAt(LocalDateTime.now().minusDays(10)); // Old activity
|
||||
testActivity.setTrackPointsJson("[{\"latitude\":50.0,\"longitude\":8.0}]");
|
||||
|
||||
when(weatherDataRepository.existsByActivityId(activityId)).thenReturn(false);
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
verify(restTemplate, never()).getForObject(any(URI.class), eq(String.class));
|
||||
verify(weatherDataRepository, never()).save(any(WeatherData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle 401 authentication error from API")
|
||||
void testFetchWeather_AuthenticationError() {
|
||||
testActivity.setTrackPointsJson("[{\"latitude\":50.0,\"longitude\":8.0}]");
|
||||
|
||||
when(weatherDataRepository.existsByActivityId(activityId)).thenReturn(false);
|
||||
when(restTemplate.getForObject(any(URI.class), eq(String.class)))
|
||||
.thenThrow(new HttpClientErrorException(
|
||||
org.springframework.http.HttpStatus.UNAUTHORIZED,
|
||||
"Unauthorized",
|
||||
"{\"cod\":401,\"message\":\"Invalid API key\"}".getBytes(),
|
||||
null
|
||||
));
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
verify(weatherDataRepository, never()).save(any(WeatherData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle network/connection errors")
|
||||
void testFetchWeather_NetworkError() {
|
||||
testActivity.setTrackPointsJson("[{\"latitude\":50.0,\"longitude\":8.0}]");
|
||||
|
||||
when(weatherDataRepository.existsByActivityId(activityId)).thenReturn(false);
|
||||
when(restTemplate.getForObject(any(URI.class), eq(String.class)))
|
||||
.thenThrow(new ResourceAccessException("Connection timeout"));
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
verify(weatherDataRepository, never()).save(any(WeatherData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle malformed JSON response from API")
|
||||
void testFetchWeather_MalformedResponse() {
|
||||
testActivity.setTrackPointsJson("[{\"latitude\":50.0,\"longitude\":8.0}]");
|
||||
|
||||
when(weatherDataRepository.existsByActivityId(activityId)).thenReturn(false);
|
||||
when(restTemplate.getForObject(any(URI.class), eq(String.class)))
|
||||
.thenReturn("this is not valid JSON");
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
verify(weatherDataRepository, never()).save(any(WeatherData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should parse all weather fields correctly")
|
||||
void testParseWeatherResponse_AllFields() {
|
||||
String responseWithRain = """
|
||||
{
|
||||
"main": {
|
||||
"temp": 10.0,
|
||||
"feels_like": 8.5,
|
||||
"pressure": 1015,
|
||||
"humidity": 80
|
||||
},
|
||||
"weather": [{"main": "Rain", "description": "light rain", "icon": "10d"}],
|
||||
"wind": {"speed": 5.5, "deg": 270},
|
||||
"clouds": {"all": 75},
|
||||
"visibility": 8000,
|
||||
"rain": {"1h": 2.5},
|
||||
"sys": {"sunrise": 1700721600, "sunset": 1700757600}
|
||||
}
|
||||
""";
|
||||
|
||||
testActivity.setTrackPointsJson("[{\"latitude\":50.0,\"longitude\":8.0}]");
|
||||
|
||||
when(weatherDataRepository.existsByActivityId(activityId)).thenReturn(false);
|
||||
when(restTemplate.getForObject(any(URI.class), eq(String.class)))
|
||||
.thenReturn(responseWithRain);
|
||||
when(weatherDataRepository.save(any(WeatherData.class)))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
WeatherData weatherData = result.get();
|
||||
assertEquals(new BigDecimal("10.0"), weatherData.getTemperatureCelsius());
|
||||
assertEquals(new BigDecimal("8.5"), weatherData.getFeelsLikeCelsius());
|
||||
assertEquals("Rain", weatherData.getWeatherCondition());
|
||||
assertEquals("light rain", weatherData.getWeatherDescription());
|
||||
assertEquals(new BigDecimal("5.5"), weatherData.getWindSpeedMps());
|
||||
assertEquals(270, weatherData.getWindDirection());
|
||||
assertEquals(75, weatherData.getCloudiness());
|
||||
assertEquals(8000, weatherData.getVisibilityMeters());
|
||||
assertEquals(new BigDecimal("2.5"), weatherData.getPrecipitationMm());
|
||||
assertNotNull(weatherData.getSunrise());
|
||||
assertNotNull(weatherData.getSunset());
|
||||
assertEquals("openweathermap", weatherData.getDataSource());
|
||||
assertNotNull(weatherData.getFetchedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle response with missing optional fields")
|
||||
void testParseWeatherResponse_MinimalFields() {
|
||||
String minimalResponse = """
|
||||
{
|
||||
"main": {
|
||||
"temp": 15.0,
|
||||
"feels_like": 14.0,
|
||||
"pressure": 1010,
|
||||
"humidity": 60
|
||||
},
|
||||
"weather": [{"main": "Clouds", "description": "few clouds", "icon": "02d"}]
|
||||
}
|
||||
""";
|
||||
|
||||
testActivity.setTrackPointsJson("[{\"latitude\":50.0,\"longitude\":8.0}]");
|
||||
|
||||
when(weatherDataRepository.existsByActivityId(activityId)).thenReturn(false);
|
||||
when(restTemplate.getForObject(any(URI.class), eq(String.class)))
|
||||
.thenReturn(minimalResponse);
|
||||
when(weatherDataRepository.save(any(WeatherData.class)))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
WeatherData weatherData = result.get();
|
||||
assertEquals(new BigDecimal("15.0"), weatherData.getTemperatureCelsius());
|
||||
assertEquals("Clouds", weatherData.getWeatherCondition());
|
||||
assertNull(weatherData.getWindSpeedMps());
|
||||
assertNull(weatherData.getPrecipitationMm());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should get existing weather data by activity ID")
|
||||
void testGetWeatherForActivity() {
|
||||
WeatherData weatherData = new WeatherData();
|
||||
weatherData.setActivityId(activityId);
|
||||
|
||||
when(weatherDataRepository.findByActivityId(activityId)).thenReturn(Optional.of(weatherData));
|
||||
|
||||
Optional<WeatherData> result = weatherService.getWeatherForActivity(activityId);
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
assertEquals(activityId, result.get().getActivityId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should delete weather data by activity ID")
|
||||
void testDeleteWeatherForActivity() {
|
||||
doNothing().when(weatherDataRepository).deleteByActivityId(activityId);
|
||||
|
||||
weatherService.deleteWeatherForActivity(activityId);
|
||||
|
||||
verify(weatherDataRepository, times(1)).deleteByActivityId(activityId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle mixed field names (lat + longitude)")
|
||||
void testFetchWeather_MixedFieldNames() {
|
||||
// Edge case: one coordinate uses short form, other uses long form
|
||||
String trackPointsJson = """
|
||||
[
|
||||
{
|
||||
"timestamp": "2025-11-23T18:08:09",
|
||||
"lat": 49.98939173296094,
|
||||
"longitude": 8.255225038155913,
|
||||
"elevation": 100.5
|
||||
}
|
||||
]
|
||||
""";
|
||||
testActivity.setTrackPointsJson(trackPointsJson);
|
||||
|
||||
when(weatherDataRepository.existsByActivityId(activityId)).thenReturn(false);
|
||||
when(restTemplate.getForObject(any(URI.class), eq(String.class)))
|
||||
.thenReturn(SAMPLE_WEATHER_RESPONSE);
|
||||
when(weatherDataRepository.save(any(WeatherData.class)))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
Optional<WeatherData> result = weatherService.fetchWeatherForActivity(testActivity);
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
verify(restTemplate, times(1)).getForObject(any(URI.class), eq(String.class));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue