package org.learning.by.example.reactive.microservices.handlers;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.learning.by.example.reactive.microservices.exceptions.GeoLocationNotFoundException;
import org.learning.by.example.reactive.microservices.exceptions.GetGeoLocationException;
import org.learning.by.example.reactive.microservices.exceptions.GetSunriseSunsetException;
import org.learning.by.example.reactive.microservices.model.*;
import org.learning.by.example.reactive.microservices.services.GeoLocationService;
import org.learning.by.example.reactive.microservices.services.SunriseSunsetService;
import org.learning.by.example.reactive.microservices.test.HandlersHelper;
import org.learning.by.example.reactive.microservices.test.tags.UnitTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@UnitTest
@DisplayName("ApiHandler Unit Tests")
class ApiHandlerTests {
private static final String ADDRESS_VARIABLE = "address";
private static final String GOOGLE_ADDRESS = "1600 Amphitheatre Parkway, Mountain View, CA";
private static final String SUNRISE_TIME = "12:55:17 PM";
private static final String SUNSET_TIME = "3:14:28 AM";
private static final double GOOGLE_LAT = 37.4224082;
private static final double GOOGLE_LNG = -122.0856086;
private static final String NOT_FOUND = "not found";
private static final String CANT_GET_LOCATION = "cant get location";
private static final String CANT_GET_SUNRISE_SUNSET = "can't get sunrise sunset";
private static final Mono<GeographicCoordinates> GOOGLE_LOCATION = Mono.just(new GeographicCoordinates(GOOGLE_LAT, GOOGLE_LNG));
private static final Mono<SunriseSunset> SUNRISE_SUNSET = Mono.just(new SunriseSunset(SUNRISE_TIME, SUNSET_TIME));
private static final Mono<GeographicCoordinates> LOCATION_NOT_FOUND = Mono.error(new GeoLocationNotFoundException(NOT_FOUND));
private static final Mono<GeographicCoordinates> LOCATION_EXCEPTION = Mono.error(new GetGeoLocationException(CANT_GET_LOCATION));
private static final Mono<GeographicCoordinates> SUNRISE_SUNSET_ERROR = Mono.error(new GetSunriseSunsetException(CANT_GET_SUNRISE_SUNSET));
@Autowired
private ApiHandler apiHandler;
@SpyBean
private GeoLocationService geoLocationService;
@SpyBean
private SunriseSunsetService sunriseSunsetService;
@Test
void combineTest() {
GOOGLE_LOCATION.and(SUNRISE_SUNSET, LocationResponse::new)
.subscribe(this::verifyLocationResponse);
}
private void verifyLocationResponse(final LocationResponse locationResponse) {
assertThat(locationResponse.getGeographicCoordinates().getLatitude(), is(GOOGLE_LAT));
assertThat(locationResponse.getGeographicCoordinates().getLongitude(), is(GOOGLE_LNG));
assertThat(locationResponse.getSunriseSunset().getSunrise(), is(SUNRISE_TIME));
assertThat(locationResponse.getSunriseSunset().getSunset(), is(SUNSET_TIME));
}
@Test
void serverResponseTest() {
GOOGLE_LOCATION.and(SUNRISE_SUNSET, LocationResponse::new)
.transform(apiHandler::serverResponse).subscribe(this::verifyServerResponse);
}
private void verifyServerResponse(final ServerResponse serverResponse) {
assertThat(serverResponse.statusCode(), is(HttpStatus.OK));
final LocationResponse locationResponse = HandlersHelper.extractEntity(serverResponse, LocationResponse.class);
verifyLocationResponse(locationResponse);
}
@Test
void buildResponseTest() {
final ServerRequest serverRequest = mock(ServerRequest.class);
when(serverRequest.pathVariable(ADDRESS_VARIABLE)).thenReturn(GOOGLE_ADDRESS);
doReturn(GOOGLE_LOCATION).when(geoLocationService).fromAddress(any());
doReturn(SUNRISE_SUNSET).when(sunriseSunsetService).fromGeographicCoordinates(any());
Mono.just(GOOGLE_ADDRESS).transform(apiHandler::buildResponse).subscribe(this::verifyServerResponse);
reset(geoLocationService);
reset(sunriseSunsetService);
}
@Test
void getLocationTest() {
ServerRequest serverRequest = mock(ServerRequest.class);
when(serverRequest.pathVariable(ADDRESS_VARIABLE)).thenReturn(GOOGLE_ADDRESS);
doReturn(GOOGLE_LOCATION).when(geoLocationService).fromAddress(any());
doReturn(SUNRISE_SUNSET).when(sunriseSunsetService).fromGeographicCoordinates(any());
apiHandler.getLocation(serverRequest).subscribe(this::verifyServerResponse);
reset(geoLocationService);
reset(sunriseSunsetService);
}
@Test
void postLocationTest() {
ServerRequest serverRequest = mock(ServerRequest.class);
when(serverRequest.bodyToMono(LocationRequest.class)).thenReturn(Mono.just(new LocationRequest(GOOGLE_ADDRESS)));
doReturn(GOOGLE_LOCATION).when(geoLocationService).fromAddress(any());
doReturn(SUNRISE_SUNSET).when(sunriseSunsetService).fromGeographicCoordinates(any());
apiHandler.postLocation(serverRequest).subscribe(this::verifyServerResponse);
reset(geoLocationService);
reset(sunriseSunsetService);
}
@Test
void getLocationNotFoundTest() {
ServerRequest serverRequest = mock(ServerRequest.class);
when(serverRequest.pathVariable(ADDRESS_VARIABLE)).thenReturn(GOOGLE_ADDRESS);
doReturn(LOCATION_NOT_FOUND).when(geoLocationService).fromAddress(any());
doReturn(SUNRISE_SUNSET).when(sunriseSunsetService).fromGeographicCoordinates(any());
ServerResponse serverResponse = apiHandler.getLocation(serverRequest).block();
assertThat(serverResponse.statusCode(), is(HttpStatus.NOT_FOUND));
ErrorResponse error = HandlersHelper.extractEntity(serverResponse, ErrorResponse.class);
assertThat(error.getError(), is(NOT_FOUND));
reset(geoLocationService);
reset(sunriseSunsetService);
}
@Test
void getLocationErrorSunriseSunsetTest() {
ServerRequest serverRequest = mock(ServerRequest.class);
when(serverRequest.pathVariable(ADDRESS_VARIABLE)).thenReturn(GOOGLE_ADDRESS);
doReturn(GOOGLE_LOCATION).when(geoLocationService).fromAddress(any());
doReturn(SUNRISE_SUNSET_ERROR).when(sunriseSunsetService).fromGeographicCoordinates(any());
ServerResponse serverResponse = apiHandler.getLocation(serverRequest).block();
assertThat(serverResponse.statusCode(), is(HttpStatus.INTERNAL_SERVER_ERROR));
ErrorResponse error = HandlersHelper.extractEntity(serverResponse, ErrorResponse.class);
assertThat(error.getError(), is(CANT_GET_SUNRISE_SUNSET));
reset(geoLocationService);
reset(sunriseSunsetService);
}
@Test
void getLocationBothServiceErrorTest() {
ServerRequest serverRequest = mock(ServerRequest.class);
when(serverRequest.pathVariable(ADDRESS_VARIABLE)).thenReturn(GOOGLE_ADDRESS);
doReturn(LOCATION_EXCEPTION).when(geoLocationService).fromAddress(any());
doReturn(SUNRISE_SUNSET_ERROR).when(sunriseSunsetService).fromGeographicCoordinates(any());
ServerResponse serverResponse = apiHandler.getLocation(serverRequest).block();
assertThat(serverResponse.statusCode(), is(HttpStatus.INTERNAL_SERVER_ERROR));
ErrorResponse error = HandlersHelper.extractEntity(serverResponse, ErrorResponse.class);
assertThat(error.getError(), is(CANT_GET_LOCATION));
reset(geoLocationService);
reset(sunriseSunsetService);
}
}