Add template for assignment 2

This commit is contained in:
2021-04-26 14:12:50 +02:00
parent 6d5b4b7b48
commit dd1e95d6aa
100 changed files with 6085 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>at.ac.tuwien.infosys.dst</groupId>
<artifactId>dst</artifactId>
<version>2021.1</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>ass2-service-trip</artifactId>
<name>DST :: Assignment 2 :: Service :: Trip</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>at.ac.tuwien.infosys.dst</groupId>
<artifactId>ass1-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>at.ac.tuwien.infosys.dst</groupId>
<artifactId>ass2-service-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>at.ac.tuwien.infosys.dst</groupId>
<artifactId>ass1-jpa</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,73 @@
package dst.ass2.service.trip;
import dst.ass1.jpa.dao.IDAOFactory;
import dst.ass1.jpa.dao.IDriverDAO;
import dst.ass1.jpa.model.IDriver;
import dst.ass2.service.api.match.IMatchingService;
import dst.ass2.service.api.trip.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.ManagedBean;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.util.List;
@ManagedBean
@Transactional
public class MatchingService implements IMatchingService {
@Inject
private ITripService tripService;
@Inject
private IDAOFactory daoFactory;
private IDriverDAO driverDAO;
private int driverIndex;
private static final Logger LOG = LoggerFactory.getLogger(MatchingService.class);
private List<IDriver> all;
@PostConstruct
public void setup() {
driverDAO = daoFactory.createDriverDAO();
}
@Override
public MoneyDTO calculateFare(TripDTO trip) throws InvalidTripException {
LOG.info("Calculate fare for trip: {}", trip.getId());
BigDecimal value = Math.random() > 0.5 ? BigDecimal.ONE : BigDecimal.TEN;
MoneyDTO moneyDTO = new MoneyDTO();
moneyDTO.setCurrency("EUR");
moneyDTO.setValue(value);
return moneyDTO;
}
@Override
public void queueTripForMatching(Long tripId) {
if (all == null) {
all = driverDAO.findAll();
driverIndex = all.size() - 2;
}
TripDTO trip = tripService.find(tripId);
try {
LOG.info("Queue trip {} for matching", tripId);
IDriver driver = all.get(driverIndex++);
if (driverIndex >= all.size()) {
driverIndex = 0;
}
MatchDTO matchDTO = new MatchDTO();
matchDTO.setDriverId(driver.getId());
matchDTO.setVehicleId(driver.getVehicle().getId());
matchDTO.setFare(trip.getFare());
tripService.match(tripId, matchDTO);
} catch (EntityNotFoundException | DriverNotAvailableException e) {
//LOG.error("Error during matching", e);
}
}
}
@@ -0,0 +1,38 @@
package dst.ass2.service.trip;
import dst.ass1.jpa.model.IModelFactory;
import dst.ass1.jpa.tests.TestData;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@SpringBootConfiguration
@Profile("testdata")
public class TestDataConfig implements ApplicationListener<ApplicationReadyEvent> {
@PersistenceContext
private EntityManager em;
@Bean
public TestData testData() {
return new TestData();
}
@Bean
public TestDataInserter testDataInserter(TestData testData, IModelFactory modelFactory, PlatformTransactionManager transactionManager) {
return new TestDataInserter(testData, modelFactory, transactionManager);
}
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
event.getApplicationContext()
.getBean(TestDataInserter.class)
.insertTestData(em);
}
}
@@ -0,0 +1,34 @@
package dst.ass2.service.trip;
import dst.ass1.jpa.model.IModelFactory;
import dst.ass1.jpa.tests.TestData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import javax.persistence.EntityManager;
public class TestDataInserter {
private static final Logger LOG = LoggerFactory.getLogger(TestDataInserter.class);
private PlatformTransactionManager transactionManager;
private IModelFactory modelFactory;
private TestData testData;
public TestDataInserter(TestData testData, IModelFactory modelFactory, PlatformTransactionManager transactionManager) {
this.testData = testData;
this.modelFactory = modelFactory;
this.transactionManager = transactionManager;
}
public void insertTestData(EntityManager em) {
LOG.info("Inserting test data...");
TransactionTemplate tx = new TransactionTemplate(transactionManager);
tx.execute(status -> {
testData.insert(modelFactory, em);
return null;
});
}
}
@@ -0,0 +1,14 @@
package dst.ass2.service.trip;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableTransactionManagement
public class TripApplication {
public static void main(String[] args) {
SpringApplication.run(TripApplication.class, args);
}
}
@@ -0,0 +1,57 @@
package dst.ass2.service.trip;
import dst.ass1.jpa.dao.IDAOFactory;
import dst.ass1.jpa.dao.impl.DAOFactory;
import dst.ass1.jpa.model.IModelFactory;
import dst.ass1.jpa.model.impl.ModelFactory;
import dst.ass1.jpa.util.Constants;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@SpringBootConfiguration
public class TripApplicationConfig {
@PersistenceContext
private EntityManager em;
@Bean
public ResourceConfig jerseyConfig() {
return new ResourceConfig()
.packages("dst.ass2.service.trip");
}
@Bean
public IModelFactory modelFactory() {
return new ModelFactory();
}
@Bean
public IDAOFactory daoFactory() {
return new DAOFactory(em);
}
@Bean
public LocalEntityManagerFactoryBean entityManagerFactoryBean() {
LocalEntityManagerFactoryBean bean = new LocalEntityManagerFactoryBean();
bean.setPersistenceUnitName(Constants.JPA_PERSISTENCE_UNIT);
// fixes collection proxy problem when using jersey
bean.getJpaPropertyMap().put("hibernate.enable_lazy_load_no_trans", true);
return bean;
}
@Bean
public PlatformTransactionManager transactionManager(LocalEntityManagerFactoryBean entityManagerFactoryBean) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setPersistenceUnitName(Constants.JPA_PERSISTENCE_UNIT);
transactionManager.setEntityManagerFactory(entityManagerFactoryBean.getObject());
return transactionManager;
}
}
@@ -0,0 +1,616 @@
package dst.ass2.service.trip.tests;
import dst.ass1.jpa.tests.TestData;
import dst.ass2.service.api.match.IMatchingService;
import dst.ass2.service.api.trip.*;
import dst.ass2.service.trip.TripApplication;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Bean;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TripApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("testdata")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class TripServiceResourceTest {
@Autowired
private TestData testData;
@LocalServerPort
private int port;
@MockBean
private IMatchingService matchingService;
private TestRestTemplate restTemplate;
private HttpHeaders headers;
@Bean
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
return new RestTemplate(requestFactory);
}
@Before
public void setUp() {
this.headers = new HttpHeaders();
this.restTemplate = new TestRestTemplate();
}
private String url(String uri) {
return "http://localhost:" + port + uri;
}
@Test
public void createTrip_withWrongHttpMethod_returnsError() {
String url = url("/trips");
MultiValueMap<String, String> map = getCreateMap(2134L, 2222L, 33L);
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class, map);
assertThat("Response was: " + response, response.getStatusCode().series(), is(HttpStatus.Series.CLIENT_ERROR));
assertThat(response.getStatusCode(), not(HttpStatus.NOT_FOUND));
}
@Test
public void createTrip_withUnknownRider_returnsNotFoundError() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
String url = url("/trips");
MultiValueMap<String, String> body = getCreateMap(3333L, testData.location1Id, testData.location2Id);
HttpEntity<?> request = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
assertThat("Response was: " + response, response.getStatusCode(), is(HttpStatus.NOT_FOUND));
}
@Test
public void createTrip_withValidKeys_returnsTripId() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
String url = url("/trips");
MultiValueMap<String, String> body = getCreateMap(testData.rider1Id, testData.location1Id, testData.location2Id);
HttpEntity<?> request = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
assertThat(response.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
assertThat(response.getBody(), notNullValue());
try {
Long.parseLong(response.getBody());
} catch (NumberFormatException e) {
throw new AssertionError("Response body of " + url + " should be a Long value (the trip ID)", e);
}
}
@Test
public void createTrip_withInvalidTrip_andThenGetTrip_hasNoFare() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenThrow(new InvalidTripException());
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
String url = url("/trips");
MultiValueMap<String, String> body = getCreateMap(testData.rider1Id, testData.location1Id, testData.location2Id);
HttpEntity<?> request = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
assertThat(response.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
assertThat(response.getBody(), notNullValue());
try {
long id = Long.parseLong(response.getBody());
ResponseEntity<TripDTO> trip = getTrip(id);
assertThat(trip.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
assertNull(trip.getBody().getFare());
} catch (NumberFormatException e) {
throw new AssertionError("Response body of " + url + " should be a Long value (the trip ID)", e);
}
}
@Test
public void findTrip_withUnknownKey_returnsNotFoundError() {
String url = url("/trips/" + (Long) 1338L);
ResponseEntity<String> trip = restTemplate.getForEntity(url, String.class);
assertThat(trip.getStatusCode(), is(HttpStatus.NOT_FOUND));
}
@Test
public void findTrip_onCreatedTrip_returnsJsonEntity() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
Long pickup = testData.location5Id;
Long rider = testData.rider4Id;
Long destination = testData.location1Id;
Long trip = testData.trip6Id;
String url = url("/trips/" + trip);
ResponseEntity<TripDTO> response = restTemplate.getForEntity(url, TripDTO.class);
TripDTO tripDTO = response.getBody();
assertThat(response.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
assertNotNull(tripDTO);
assertEquals(trip, tripDTO.getId());
assertEquals(destination, tripDTO.getDestinationId());
assertEquals(pickup, tripDTO.getPickupId());
assertEquals(rider, tripDTO.getRiderId());
assertEquals(3, tripDTO.getStops().size());
assertEquals(getTen(), tripDTO.getFare());
}
@Test
public void addStop_onCreatedTrip_returnsOkAndCalculatedFare() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
String url = url("/trips/" + testData.trip6Id + "/stops");
MultiValueMap<String, String> body = body("locationId", testData.location5Id);
HttpEntity<?> request = new HttpEntity<>(body, headers);
ResponseEntity<MoneyDTO> response = restTemplate.postForEntity(url, request, MoneyDTO.class);
MoneyDTO moneyDTO = response.getBody();
assertThat(response.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
assertNotNull(moneyDTO);
assertEquals(getTen(), moneyDTO);
}
@Test
public void addStop_andThenGetTrip_containsStopInList() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
String url = url("/trips/" + testData.trip6Id + "/stops");
MultiValueMap<String, String> body = body("locationId", testData.location5Id);
HttpEntity<?> request = new HttpEntity<>(body, headers);
ResponseEntity<MoneyDTO> response = restTemplate.postForEntity(url, request, MoneyDTO.class);
assertThat(response.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
ResponseEntity<TripDTO> tripResponse = getTrip(testData.trip6Id);
assertThat(tripResponse.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
assertThat(tripResponse.getBody().getStops(), hasItem(testData.location5Id));
}
@Test
public void addStop_onQueuedTrip_returnsAppropriateError() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
String url = url("/trips/" + testData.trip10Id + "/stops");
MultiValueMap<String, String> body = body("locationId", testData.location5Id);
HttpEntity<?> request = new HttpEntity<>(body, headers);
ResponseEntity<?> response = restTemplate.postForEntity(url, request, String.class);
assertThat("Make use of an appropriate HTTP status code", response.getStatusCode(), allOf(
CoreMatchers.is(not(HttpStatus.OK)),
CoreMatchers.is(not(HttpStatus.NOT_FOUND)),
CoreMatchers.is(not(HttpStatus.INTERNAL_SERVER_ERROR))
));
}
@Test
public void addStop_withNonExistingLocation_returnsNotFoundError() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
Long trip = testData.trip6Id;
String url = url("/trips/" + trip + "/stops");
MultiValueMap<String, String> body = body("locationId", 1337L);
HttpEntity<?> request = new HttpEntity<>(body, headers);
ResponseEntity<?> response = restTemplate.postForEntity(url, request, String.class);
assertThat(response.getStatusCode(), is(HttpStatus.NOT_FOUND));
}
@Test
public void addStop_withLocationAlreadyInStopList_returnsAppropriateError() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
Long trip = testData.trip6Id;
String url = url("/trips/" + trip + "/stops");
MultiValueMap<String, String> body = body("locationId", testData.location2Id);
HttpEntity<?> request = new HttpEntity<>(body, headers);
ResponseEntity<?> response = restTemplate.postForEntity(url, request, String.class);
assertThat("Make use of an appropriate HTTP status code", response.getStatusCode(), allOf(
CoreMatchers.is(not(HttpStatus.OK)),
CoreMatchers.is(not(HttpStatus.NOT_FOUND)),
CoreMatchers.is(not(HttpStatus.INTERNAL_SERVER_ERROR))
));
}
@Test
public void addStop_withInvalidTrip_returnsOk() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenThrow(new InvalidTripException());
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
Long trip = testData.trip6Id;
String url = url("/trips/" + trip + "/stops");
MultiValueMap<String, String> body = body("locationId", testData.location5Id);
HttpEntity<?> request = new HttpEntity<>(body, headers);
ResponseEntity<?> response = restTemplate.postForEntity(url, request, String.class);
assertThat(response.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
}
@Test
public void removeStop_onCreatedTrip_returnOk() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
Long trip = testData.trip6Id;
Long location = testData.location2Id;
String url = url("/trips/" + trip + "/stops/" + location);
ResponseEntity<String> exchange = restTemplate.exchange(
url,
HttpMethod.DELETE,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat(exchange.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
}
@Test
public void removeStop_andThenGetTrip_doesntContainStopInList() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
Long trip = testData.trip6Id;
Long location = testData.location2Id;
String url = url("/trips/" + trip + "/stops/" + location);
restTemplate.delete(url, new HashMap<>());
ResponseEntity<TripDTO> response = getTrip(trip);
assertNotNull(response);
List<Long> ids = response.getBody().getStops();
assertFalse(ids.contains(location));
assertTrue(ids.contains(testData.location3Id));
assertTrue(ids.contains(testData.location4Id));
}
@Test
public void removeStop_onQueuedTrip_returnsAppropriateError() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
Long trip = testData.trip10Id;
Long location = testData.location4Id;
String url = url("/trips/" + trip + "/stops/" + location);
ResponseEntity<String> response = restTemplate.exchange(
url,
HttpMethod.DELETE,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat("Make use of an appropriate HTTP status code", response.getStatusCode(), allOf(
CoreMatchers.is(not(HttpStatus.OK)),
CoreMatchers.is(not(HttpStatus.NOT_FOUND)),
CoreMatchers.is(not(HttpStatus.INTERNAL_SERVER_ERROR))
));
}
@Test
public void removeStop_withNonExistingLocation_returnsNotFoundError() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
Long trip = testData.trip6Id;
Long location = 1234L;
String url = url("/trips/" + trip + "/stops/" + location);
ResponseEntity<String> exchange = restTemplate.exchange(
url,
HttpMethod.DELETE,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat(exchange.getStatusCode(), is(HttpStatus.NOT_FOUND));
}
@Test
public void removeStop_WithLocationNotInStopsList_returnsAppropriateError() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
Long trip = testData.trip6Id;
Long location = testData.location5Id;
String url = url("/trips/" + trip + "/stops/" + location);
ResponseEntity<String> response = restTemplate.exchange(
url,
HttpMethod.DELETE,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat("Make use of an appropriate HTTP status code", response.getStatusCode(), allOf(
CoreMatchers.is(not(HttpStatus.OK)),
CoreMatchers.is(not(HttpStatus.NOT_FOUND)),
CoreMatchers.is(not(HttpStatus.INTERNAL_SERVER_ERROR))
));
}
@Test
public void removeStop_withInvalidTrip_returnsOk() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenThrow(new InvalidTripException());
Long trip = testData.trip6Id;
Long location = testData.location2Id;
String url = url("/trips/" + trip + "/stops/" + location);
ResponseEntity<String> exchange = restTemplate.exchange(
url,
HttpMethod.DELETE,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat(exchange.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
}
@Test
public void confirmTrip_withKnownTrip_shouldReturnSuccessful() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
Long trip = testData.trip6Id;
String url = url("/trips/" + trip + "/confirm");
ResponseEntity<String> exchange = restTemplate.exchange(
url,
HttpMethod.PATCH,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat(exchange.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
}
@Test
public void confirmTrip_withUnknownTrip_shouldReturnNotFoundError() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenReturn(getTen());
Long trip = 1244L;
String url = url("/trips/" + trip + "/confirm");
ResponseEntity<String> exchange = restTemplate.exchange(
url,
HttpMethod.PATCH,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat(exchange.getStatusCode(), is(HttpStatus.NOT_FOUND));
}
@Test
public void confirmTrip_withInvalidTrip_shouldReturnAppropriateError() throws InvalidTripException {
when(matchingService.calculateFare(any())).thenThrow(new InvalidTripException());
Long trip = testData.trip6Id;
String url = url("/trips/" + trip + "/confirm");
ResponseEntity<String> response = restTemplate.exchange(
url,
HttpMethod.PATCH,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat("Make use of an appropriate HTTP status code", response.getStatusCode(), allOf(
CoreMatchers.is(not(HttpStatus.OK)),
CoreMatchers.is(not(HttpStatus.NOT_FOUND)),
CoreMatchers.is(not(HttpStatus.INTERNAL_SERVER_ERROR))
));
}
@Test
public void deleteTrip_withKnownTrip_shouldReturnSuccessful() {
Long trip = testData.trip6Id;
String url = url("/trips/" + trip);
ResponseEntity<String> exchange = restTemplate.exchange(
url,
HttpMethod.DELETE,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat(exchange.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
}
@Test
public void deleteTrip_withUnknownTrip_shouldReturnNotFoundError() {
Long trip = 1245L;
String url = url("/trips/" + trip);
ResponseEntity<String> exchange = restTemplate.exchange(
url,
HttpMethod.DELETE,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat(exchange.getStatusCode(), is(HttpStatus.NOT_FOUND));
}
@Test
public void cancelTrip_withKnownKey_shouldReturnSuccessful() {
Long trip = testData.trip6Id;
String url = url("/trips/" + trip + "/cancel");
ResponseEntity<String> exchange = restTemplate.exchange(
url,
HttpMethod.PATCH,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat(exchange.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
}
@Test
public void cancelTrip_withUnknownKey_shouldReturnNotFoundError() {
Long trip = 12545L;
String url = url("/trips/" + trip + "/cancel");
ResponseEntity<String> exchange = restTemplate.exchange(
url,
HttpMethod.PATCH,
null,
(Class<String>) null,
new HashMap<String, String>()
);
assertThat(exchange.getStatusCode(), is(HttpStatus.NOT_FOUND));
}
@Test
public void completeTrip_withKnownTrip_shouldReturnSuccessful() {
Long trip = testData.trip9Id;
TripInfoDTO tripInfoDTO = new TripInfoDTO();
tripInfoDTO.setCompleted(new Date());
tripInfoDTO.setDistance(100.0);
tripInfoDTO.setFare(getTen());
String url = url("/trips/" + trip + "/complete");
ResponseEntity<String> exchange = restTemplate.postForEntity(
url,
tripInfoDTO,
String.class
);
assertThat(exchange.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
}
@Test
public void completeTrip_withUnknownTrip_shouldReturnNotFoundError() {
Long trip = 12545L;
TripInfoDTO tripInfoDTO = new TripInfoDTO();
tripInfoDTO.setCompleted(new Date());
tripInfoDTO.setDistance(100.0);
tripInfoDTO.setFare(getTen());
String url = url("/trips/" + trip + "/complete");
ResponseEntity<String> exchange = restTemplate.postForEntity(
url,
tripInfoDTO,
String.class
);
assertThat(exchange.getStatusCode(), is(HttpStatus.NOT_FOUND));
}
@Test
public void matchTrip_withKnownTrip_shouldReturnSuccessful() {
Long trip = testData.trip10Id;
Long driver = testData.driver4Id + 1L;
Long vehicle = testData.vehicle1Id;
String url = url("/trips/" + trip + "/match");
MatchDTO matchDTO = new MatchDTO();
matchDTO.setVehicleId(vehicle);
matchDTO.setDriverId(driver);
matchDTO.setFare(getTen());
ResponseEntity<String> exchange = restTemplate.postForEntity(
url,
matchDTO,
String.class
);
assertThat(exchange.getStatusCode().series(), is(HttpStatus.Series.SUCCESSFUL));
}
@Test
public void matchTrip_withUnavailableDriver_shouldReturnAppropriateError() {
Long trip = testData.trip10Id;
Long driver = testData.driver4Id;
Long vehicle = testData.vehicle1Id;
String url = url("/trips/" + trip + "/match");
MatchDTO matchDTO = new MatchDTO();
matchDTO.setVehicleId(vehicle);
matchDTO.setDriverId(driver);
matchDTO.setFare(getTen());
ResponseEntity<String> response = restTemplate.postForEntity(
url,
matchDTO,
String.class
);
assertThat("Make use of an appropriate HTTP status code", response.getStatusCode(), allOf(
CoreMatchers.is(not(HttpStatus.OK)),
CoreMatchers.is(not(HttpStatus.NOT_FOUND)),
CoreMatchers.is(not(HttpStatus.INTERNAL_SERVER_ERROR))
));
}
private MultiValueMap<String, String> getCreateMap(Long riderId, Long pickupId, Long destinationId) {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("riderId", riderId + "");
map.add("pickupId", pickupId + "");
map.add("destinationId", destinationId + "");
return map;
}
private MoneyDTO getTen() {
MoneyDTO moneyDTO = new MoneyDTO();
moneyDTO.setCurrency("EUR");
moneyDTO.setValue(BigDecimal.TEN);
return moneyDTO;
}
private MultiValueMap<String, String> body(String key, Object value) {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add(key, String.valueOf(value));
return map;
}
private ResponseEntity<TripDTO> getTrip(Long id) {
String url = url("/trips/" + id);
return restTemplate.getForEntity(url, TripDTO.class);
}
}
@@ -0,0 +1,464 @@
package dst.ass2.service.trip.tests;
import dst.ass1.jpa.dao.IDAOFactory;
import dst.ass1.jpa.dao.ITripDAO;
import dst.ass1.jpa.model.*;
import dst.ass1.jpa.tests.TestData;
import dst.ass2.service.api.match.IMatchingService;
import dst.ass2.service.api.trip.*;
import dst.ass2.service.trip.TripApplication;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import java.math.BigDecimal;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TripApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("testdata")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class TripServiceTest implements ApplicationContextAware {
private static final Logger LOG = LoggerFactory.getLogger(TripServiceTest.class);
private ApplicationContext ctx;
@MockBean
private IMatchingService matchingService;
private ITripService tripService;
private IDAOFactory daoFactory;
private TestData testData;
private ITripDAO tripDAO;
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.ctx = ctx;
}
@Before
public void setUp() {
LOG.info("Test resolving beans from application context");
daoFactory = ctx.getBean(IDAOFactory.class);
tripService = ctx.getBean(ITripService.class);
testData = ctx.getBean(TestData.class);
tripDAO = daoFactory.createTripDAO();
}
@Test
public void testCreateWithValidArguments_persistsTrip_and_returnsTripDTO() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getOne());
TripDTO tripDTO = tripService.create(testData.rider1Id, testData.location1Id, testData.location2Id);
verify(matchingService, times(1)).calculateFare(any());
assertNotNull(tripDTO);
assertNotNull(tripDTO.getId());
assertNotNull(tripDTO.getFare());
assertEquals(testData.rider1Id, tripDTO.getRiderId());
assertEquals(testData.location1Id, tripDTO.getPickupId());
assertEquals(testData.location2Id, tripDTO.getDestinationId());
assertEquals(getOne(), tripDTO.getFare());
ITrip trip = tripDAO.findById(tripDTO.getId());
assertNotNull(trip);
assertEquals(testData.location1Id, trip.getPickup().getId());
assertEquals(testData.location2Id, trip.getDestination().getId());
assertEquals(testData.rider1Id, trip.getRider().getId());
assertEquals(TripState.CREATED, trip.getState());
}
@Test(expected = EntityNotFoundException.class)
public void testCreateWithInvalidRider_throwsException() throws Exception {
tripService.create(1337L, testData.location1Id, testData.location2Id);
}
@Test(expected = EntityNotFoundException.class)
public void testCreateWithInvalidPickup_throwsException() throws Exception {
tripService.create(testData.rider1Id, 1444L, testData.location2Id);
}
@Test(expected = EntityNotFoundException.class)
public void testCreateWithInvalidDestination_throwsException() throws Exception {
tripService.create(testData.rider1Id, testData.location1Id, 1337L);
}
@Test
public void testCreateWithInvalidTrip_setsFareToNull() throws Exception {
when(matchingService.calculateFare(any())).thenThrow(new InvalidTripException());
TripDTO tripDTO = tripService.create(testData.rider1Id, testData.location1Id, testData.location2Id);
assertNotNull(tripDTO);
assertNotNull(tripDTO.getId());
ITrip trip = tripDAO.findById(tripDTO.getId());
assertNotNull(trip);
assertNull(tripDTO.getFare());
}
@Test
public void testConfirmWithValidTrip_isQueued() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getTen());
tripService.confirm(testData.trip6Id);
ITrip confirmed = tripDAO.findById(testData.trip6Id);
assertEquals(TripState.QUEUED, confirmed.getState());
verify(matchingService, times(1)).queueTripForMatching(any());
}
@Test(expected = EntityNotFoundException.class)
public void testConfirmWithUnknownTrip_throwsException() throws Exception {
tripService.confirm(1337L);
}
@Test(expected = IllegalStateException.class)
public void testConfirmQueuedTrip_throwsException() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getOne());
tripService.confirm(testData.trip10Id);
}
@Test(expected = InvalidTripException.class)
public void testConfirmWithInvalidTrip_throwsException() throws Exception {
when(matchingService.calculateFare(any())).thenThrow(new InvalidTripException());
tripService.confirm(testData.trip6Id);
}
@Test
public void testMatch_matchCreated_stateMatched() throws Exception {
Long tripId = testData.trip10Id;
MatchDTO matchDTO = new MatchDTO();
matchDTO.setFare(getOne());
matchDTO.setDriverId(testData.driver4Id + 1);
matchDTO.setVehicleId(testData.vehicle1Id);
tripService.match(tripId, matchDTO);
ITrip updated = tripDAO.findById(tripId);
assertNotNull(updated);
assertEquals(TripState.MATCHED, updated.getState());
IMatch match = updated.getMatch();
assertNotNull(match);
assertEquals(matchDTO.getDriverId(), match.getDriver().getId());
assertEquals(matchDTO.getVehicleId(), match.getVehicle().getId());
IMoney fare = match.getFare();
assertEquals(getOne().getCurrency(), fare.getCurrency());
assertEquals(getOne().getValue().compareTo(fare.getValue()), 0);
}
@Test
public void testMatchWithUnknownTrip_throwsException_and_requeueTrip() throws Exception {
long tripId = 1337L;
MatchDTO matchDTO = new MatchDTO();
matchDTO.setFare(getOne());
matchDTO.setDriverId(testData.driver4Id + 1);
matchDTO.setVehicleId(testData.vehicle1Id);
try {
tripService.match(tripId, matchDTO);
} catch (EntityNotFoundException ex) {
verify(matchingService, times(1)).queueTripForMatching(any());
return;
}
fail();
}
@Test
public void testMatch_driverAlreadyAssigned_throwsException_and_requeueTrip() throws Exception {
Long tripId = testData.trip10Id;
MatchDTO matchDTO = new MatchDTO();
matchDTO.setFare(getTen());
matchDTO.setDriverId(testData.driver4Id);
matchDTO.setVehicleId(testData.vehicle1Id);
try {
tripService.match(tripId, matchDTO);
} catch (DriverNotAvailableException ex) {
verify(matchingService, times(1)).queueTripForMatching(any());
return;
}
fail();
}
@Test
public void testCompleteWithValidTripInfo_shouldPersistTripInfo_and_setTripCompleted() throws Exception {
Long tripId = testData.trip9Id;
TripInfoDTO tripInfoDTO = new TripInfoDTO();
tripInfoDTO.setCompleted(new Date());
tripInfoDTO.setFare(getTen());
tripInfoDTO.setDistance(2.0);
tripService.complete(tripId, tripInfoDTO);
ITrip updated = tripDAO.findById(tripId);
assertNotNull(updated);
assertEquals(TripState.COMPLETED, updated.getState());
ITripInfo tripInfo = updated.getTripInfo();
assertNotNull(tripInfo);
assertEquals(tripInfoDTO.getCompleted(), tripInfo.getCompleted());
assertEquals(2.0, tripInfo.getDistance(), 0);
assertEquals(getTen().getCurrency(), tripInfo.getTotal().getCurrency());
assertEquals(0, getTen().getValue().compareTo(tripInfo.getTotal().getValue()));
}
@Test(expected = EntityNotFoundException.class)
public void testCompleteWithUnknownTrip_throwsException() throws Exception {
long tripId = 1337L;
TripInfoDTO tripInfoDTO = new TripInfoDTO();
tripInfoDTO.setCompleted(new Date());
tripInfoDTO.setFare(getTen());
tripInfoDTO.setDistance(2.0);
tripService.complete(tripId, tripInfoDTO);
}
@Test
public void testCancelWithValidTrip_shouldCancelTrip() throws Exception {
tripService.cancel(testData.trip6Id);
ITrip updated = tripDAO.findById(testData.trip6Id);
assertNotNull(updated);
assertEquals(TripState.CANCELLED, updated.getState());
}
@Test(expected = EntityNotFoundException.class)
public void testCancelWithUnkownTrip_throwsException() throws Exception {
tripService.cancel(1337L);
}
@Test
public void testAddStop_shouldReturnTrue() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getTen());
TripDTO tripDTO = getTrip6DTO();
tripDTO.setFare(getTen());
boolean added = tripService.addStop(tripDTO, testData.location1Id);
assertTrue(added);
assertEquals(getTen(), tripDTO.getFare());
verify(matchingService, times(1)).calculateFare(any());
}
@Test(expected = EntityNotFoundException.class)
public void testAddStopUnknownLocation_throwsException() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getTen());
TripDTO tripDTO = getTrip6DTO();
tripDTO.setFare(getTen());
tripService.addStop(tripDTO, 1344L);
}
@Test(expected = IllegalStateException.class)
public void testAddStopInvalidTripState_throwsException() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getTen());
ITrip trip = tripDAO.findById(testData.trip1Id);
TripDTO tripDTO = new TripDTO();
tripDTO.setId(trip.getId());
tripDTO.setPickupId(trip.getPickup().getId());
tripDTO.setDestinationId(trip.getDestination().getId());
tripDTO.setRiderId(trip.getRider().getId());
tripDTO.setStops(trip.getStops().stream().map(ILocation::getId).collect(Collectors.toList()));
tripDTO.setFare(getTen());
tripService.addStop(tripDTO, testData.location2Id);
}
@Test
public void testAddStopInvalidTrip_setsFareToNull() throws Exception {
when(matchingService.calculateFare(any())).thenThrow(new InvalidTripException());
TripDTO tripDTO = getTrip6DTO();
tripDTO.setFare(getOne());
boolean added = tripService.addStop(tripDTO, testData.location1Id);
assertTrue(added);
assertNull(tripDTO.getFare());
assertEquals(4, tripDTO.getStops().size());
}
@Test
public void testAddStop_shouldReturnFalse() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getTen());
TripDTO tripDTO = getTrip6DTO();
tripDTO.setFare(getOne());
boolean added = tripService.addStop(tripDTO, testData.location2Id);
assertFalse(added);
//fare should not be updated if the location hasn't been added
assertEquals(getOne(), tripDTO.getFare());
}
@Test
public void testRemoveStop_shouldReturnTrue() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getOne());
TripDTO tripDTO = getTrip6DTO();
tripDTO.setFare(getTen());
boolean removed = tripService.removeStop(tripDTO, testData.location2Id);
assertTrue(removed);
assertEquals(getOne(), tripDTO.getFare());
assertEquals(2, tripDTO.getStops().size());
ITrip updated = tripDAO.findById(testData.trip6Id);
assertEquals(2, updated.getStops().size());
verify(matchingService, times(1)).calculateFare(any());
}
private MoneyDTO getZero() {
MoneyDTO moneyDTO = new MoneyDTO();
moneyDTO.setCurrency("EUR");
moneyDTO.setValue(BigDecimal.ZERO);
return moneyDTO;
}
@Test
public void testRemoveStop_shouldReturnFalse() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getZero());
TripDTO tripDTO = getTrip6DTO();
tripDTO.setFare(getOne());
boolean removed = tripService.removeStop(tripDTO, testData.location1Id);
assertFalse(removed);
//fare should not be updated if the location hasn't been removed
assertEquals(getOne(), tripDTO.getFare());
assertEquals(3, tripDTO.getStops().size());
ITrip updated = tripDAO.findById(testData.trip6Id);
assertEquals(3, updated.getStops().size());
}
@Test(expected = EntityNotFoundException.class)
public void testRemoveStopUnknownLocation_throwsException() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getOne());
TripDTO tripDTO = getTrip6DTO();
tripDTO.setFare(getTen());
tripService.removeStop(tripDTO, 1344L);
}
@Test(expected = IllegalStateException.class)
public void testRemoveStopInvalidTripState_throwsException() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getOne());
ITrip trip = tripDAO.findById(testData.trip10Id);
TripDTO tripDTO = new TripDTO();
tripDTO.setId(trip.getId());
tripDTO.setPickupId(trip.getPickup().getId());
tripDTO.setDestinationId(trip.getDestination().getId());
tripDTO.setRiderId(trip.getRider().getId());
List<Long> stopIds = trip.getStops().stream().map(ILocation::getId).collect(Collectors.toList());
tripDTO.setStops(stopIds);
tripDTO.setFare(getTen());
tripService.removeStop(tripDTO, testData.location4Id);
}
@Test
public void testRemoveStopInvalidTrip_setsFareToNull() throws Exception {
when(matchingService.calculateFare(any())).thenThrow(new InvalidTripException());
TripDTO tripDTO = getTrip6DTO();
tripDTO.setFare(getOne());
boolean removed = tripService.removeStop(tripDTO, testData.location4Id);
assertTrue(removed);
assertNull(tripDTO.getFare());
}
@Test
public void testDeleteValidTrip_shouldSucceed() throws Exception {
tripService.delete(testData.trip6Id);
ITrip deleted = tripDAO.findById(testData.trip6Id);
assertNull(deleted);
}
@Test(expected = EntityNotFoundException.class)
public void testDeleteUnknownTrip_throwsException() throws Exception {
tripService.delete(1111L);
}
@Test
public void testFindTrip_shouldSucceed() throws Exception {
when(matchingService.calculateFare(any())).thenReturn(getTen());
TripDTO tripDTO = tripService.find(testData.trip9Id);
assertNotNull(tripDTO);
assertEquals(testData.trip9Id, tripDTO.getId());
assertEquals(testData.rider1Id, tripDTO.getRiderId());
assertEquals(testData.location2Id, tripDTO.getPickupId());
assertEquals(testData.location5Id, tripDTO.getDestinationId());
assertEquals(getTen(), tripDTO.getFare());
}
@Test
public void testFindTrip_shouldReturnNull() {
TripDTO tripDTO = tripService.find(1337L);
assertNull(tripDTO);
}
private MoneyDTO getOne() {
MoneyDTO moneyDTO = new MoneyDTO();
moneyDTO.setCurrency("EUR");
moneyDTO.setValue(BigDecimal.ONE);
return moneyDTO;
}
private MoneyDTO getTen() {
MoneyDTO moneyDTO = new MoneyDTO();
moneyDTO.setValue(BigDecimal.TEN);
moneyDTO.setCurrency("EUR");
return moneyDTO;
}
private TripDTO getTrip6DTO() {
TripDTO tripDTO = new TripDTO();
tripDTO.setId(testData.trip6Id);
tripDTO.setPickupId(testData.location5Id);
tripDTO.setDestinationId(testData.location1Id);
tripDTO.setRiderId(testData.rider4Id);
LinkedList<Long> longs = new LinkedList<>();
longs.add(testData.location2Id);
longs.add(testData.location3Id);
longs.add(testData.location4Id);
tripDTO.setStops(longs);
return tripDTO;
}
}
@@ -0,0 +1 @@
server.port=8091