Quellcode durchsuchen

making rest api better again

Fabien CHERET vor 5 Jahren
Ursprung
Commit
7286cf1333

+ 1 - 0
docs/delete.md

@@ -35,6 +35,7 @@ integer($int64) |
          }
      ],
      "pricing_policy": {
+        "type": "simple",
          "flat_fee": 1,
          "per_hour_fare": 0.5
      }

+ 2 - 0
docs/get.md

@@ -34,6 +34,7 @@ string |
             }
         ],
         "pricing_policy": {
+            "type": "simple",
             "flat_fee": 1,
             "per_hour_fare": 0.5
         }
@@ -52,6 +53,7 @@ string |
             }
         ],
         "pricing_policy": {
+            "type": "simple",
             "flat_fee": 1,
             "per_hour_fare": 0.5
         }

+ 1 - 0
docs/getid.md

@@ -33,6 +33,7 @@ integer($int64) |
         }
     ],
     "pricing_policy": {
+        "type": "simple",
         "flat_fee": 1,
         "per_hour_fare": 0.5
     }

+ 2 - 0
docs/post.md

@@ -35,6 +35,7 @@ Create a Parking Lot
     }
   ],
   "pricing_policy": {
+    "type": "simple",
     "flat_fee": 1,
     "per_hour_fare": 0.5
   }
@@ -65,6 +66,7 @@ Create a Parking Lot
          }
      ],
      "pricing_policy": {
+         "type": "simple",
          "flat_fee": 1,
          "per_hour_fare": 0.5
      }

+ 1 - 1
docs/postpark.md

@@ -87,7 +87,7 @@ integer($int64) |
     "timestamp": "2020-01-01T01:01:01.449192500Z",
     "status": 503,
     "error": "Service Unavailable",
-    "message": "Parking is full for this type of car",
+    "message": "Parking lot is full for this type of car",
     "path": "/parking_lot/0/park"
 }
 ```

+ 2 - 0
docs/putid.md

@@ -45,6 +45,7 @@ integer($int64) |
     }
   ],
   "pricing_policy": {
+    "type": "simple",
     "flat_fee": 1,
     "per_hour_fare": 0.5
   }
@@ -75,6 +76,7 @@ integer($int64) |
         }
     ],
     "pricing_policy": {
+        "type": "simple",
         "flat_fee": 1,
         "per_hour_fare": 0.5
     }

+ 13 - 10
src/main/java/eu/fibane/parkingtoll/api/ParkingLotApiController.java

@@ -18,11 +18,11 @@ public class ParkingLotApiController {
 
     private static final Logger log = LoggerFactory.getLogger(ParkingLotApiController.class);
 
-    private ParkingLotService controller;
+    private ParkingLotService service;
 
     @Autowired
-    public ParkingLotApiController(ParkingLotService controller) {
-        this.controller = controller;
+    public ParkingLotApiController(ParkingLotService service) {
+        this.service = service;
     }
 
     @PostMapping(value = "/parking_lot",
@@ -30,7 +30,7 @@ public class ParkingLotApiController {
             consumes = { "application/json" })
     public ResponseEntity<ParkingLot> createParkingLot(@Valid @RequestBody ParkingLot parkingLotItem) {
 
-        parkingLotItem = controller.createParkingLot(parkingLotItem);
+        parkingLotItem = service.createParkingLot(parkingLotItem);
         return ResponseEntity.created(URI.create("/parking_lot/" + parkingLotItem.getId())).body(parkingLotItem);
     }
 
@@ -38,14 +38,14 @@ public class ParkingLotApiController {
             produces = { "application/json" },
             consumes = { "application/json" })
     public ResponseEntity<ParkingLot> deleteParkingLotById(@PathVariable("parkingLotId") Long parkingLotId) {
-        ParkingLot parkingLot = controller.deleteParkingLotById(parkingLotId);
+        ParkingLot parkingLot = service.deleteParkingLotById(parkingLotId);
         return ResponseEntity.ok(parkingLot);
     }
 
     @GetMapping(value = "/parking_lot/{parkingLotId}",
             produces = { "application/json" })
     public ResponseEntity<ParkingLot> getParkingById(@PathVariable("parkingLotId") Long parkingLotId) {
-        ParkingLot parkingLot = controller.getParkingById(parkingLotId);
+        ParkingLot parkingLot = service.getParkingById(parkingLotId);
         return ResponseEntity.ok(parkingLot);
     }
 
@@ -53,7 +53,7 @@ public class ParkingLotApiController {
             produces = { "application/json" })
     public ResponseEntity<CarSlot> leaveParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody CarSlot carSlotItem) {
 
-        CarSlot carSlot = controller.leaveParkingLot(parkingLotId, carSlotItem);
+        CarSlot carSlot = service.leaveParkingLot(parkingLotId, carSlotItem);
         return ResponseEntity.ok(carSlot);
     }
 
@@ -62,7 +62,7 @@ public class ParkingLotApiController {
             consumes = { "application/json" })
     public ResponseEntity<CarSlot> parkAtParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody CarSlot carSlotItem) {
         //check if the asked parking exists
-        CarSlot slot = controller.parkAtParkingLot(parkingLotId, carSlotItem);
+        CarSlot slot = service.parkAtParkingLot(parkingLotId, carSlotItem);
         return ResponseEntity.ok(slot);
     }
 
@@ -70,14 +70,17 @@ public class ParkingLotApiController {
             produces = { "application/json" },
             consumes = { "application/json" })
     public ResponseEntity<ParkingLot> updateParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody ParkingLot parkingLotItem) {
-        ParkingLot parkingLot = controller.updateParkingLot(parkingLotId, parkingLotItem);
+        ParkingLot parkingLot = service.updateParkingLot(parkingLotId, parkingLotItem);
+        if(parkingLot == null){
+            return ResponseEntity.created(URI.create("/parking_lot/" + parkingLotItem.getId())).body(parkingLotItem);
+        }
         return ResponseEntity.noContent().build();
     }
 
     @GetMapping(value = "/parking_lot",
             produces = { "application/json" })
     public ResponseEntity<Collection<ParkingLot>> searchParkingLot(@Valid @RequestParam(value = "searchString", required = false) String searchString) {
-        Collection<ParkingLot> parkingLots = controller.searchParkingLot(searchString);
+        Collection<ParkingLot> parkingLots = service.searchParkingLot(searchString);
         return ResponseEntity.ok(parkingLots);
     }
 

+ 0 - 4
src/main/java/eu/fibane/parkingtoll/model/CarSlot.java

@@ -31,9 +31,5 @@ public class CarSlot   {
   @JsonProperty("price")
   private BigDecimal price = null;
 
-  public void updateDepartureTime(){
-    this.departureTime = Instant.now();
-  }
-
 }
 

+ 10 - 1
src/main/java/eu/fibane/parkingtoll/model/FareProcessor.java

@@ -1,10 +1,19 @@
 package eu.fibane.parkingtoll.model;
 
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
 import java.math.BigDecimal;
 import java.time.Instant;
 
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+        property = "type")
+@JsonSubTypes({
+        @JsonSubTypes.Type(value = PricingPolicy.class, name="simple")
+})
 public interface FareProcessor {
 
-    BigDecimal computeFare(Instant arrival, Instant departure);
+    BigDecimal computeFare(CarSlot carSlot);
 
 }

+ 0 - 11
src/main/java/eu/fibane/parkingtoll/model/Layout.java

@@ -64,16 +64,5 @@ public class Layout {
     this.available.incrementAndGet();
   }
 
-  public CarSlot removeCar(CarSlot carSlot) {
-    CarSlot slot = carSlots.get(carSlot.getSlot());
-    if(slot == null){
-      throw new NoSuchCarInParkingException("This car is not at the specified location");
-    }
-    //remove first, then make id available again
-    CarSlot storedCarSlot = carSlots.remove(carSlot.getSlot());
-    storedCarSlot.updateDepartureTime();
-    incrementAndFree(carSlot.getSlot());
-    return storedCarSlot;
-  }
 }
 

+ 3 - 14
src/main/java/eu/fibane/parkingtoll/model/ParkingLot.java

@@ -2,7 +2,8 @@ package eu.fibane.parkingtoll.model;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import eu.fibane.parkingtoll.exceptions.ParkingTypeDoesNotExistException;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
 import lombok.Data;
 import org.springframework.validation.annotation.Validated;
 
@@ -11,7 +12,6 @@ import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Optional;
 import java.util.stream.Collectors;
 
 /**
@@ -33,18 +33,7 @@ public class ParkingLot   {
 
   @JsonProperty("pricing_policy")
   @Valid @NotNull
-  private PricingPolicy pricingPolicy;
-
-  public CarSlot removeCar(CarSlot carSlot){
-    Layout layout = getLayoutByName(carSlot.getType());
-    return layout.removeCar(carSlot);
-  }
-
-
-  private Layout getLayoutByName(String name){
-    Optional<Layout> layout = layoutList.stream().filter(a -> a.getName().equals(name)).findFirst();
-    return layout.orElseThrow(() -> new ParkingTypeDoesNotExistException("This type of slot does not exist in this parking lot"));
-  }
+  private FareProcessor fareProcessor;
 
   @JsonIgnore
   public List<String> getSlotTypes(){

+ 5 - 1
src/main/java/eu/fibane/parkingtoll/model/PricingPolicy.java

@@ -1,6 +1,7 @@
 package eu.fibane.parkingtoll.model;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
 import eu.fibane.parkingtoll.exceptions.DepartureIsBeforeArrivalException;
 import lombok.Data;
 import org.springframework.validation.annotation.Validated;
@@ -15,13 +16,16 @@ import java.time.Instant;
 @Validated
 @Data
 public class PricingPolicy implements FareProcessor {
+
   @JsonProperty("flat_fee")
   private BigDecimal flatFee = null;
 
   @JsonProperty("per_hour_fare")
   private BigDecimal perHourFare = null;
 
-  public BigDecimal computeFare(Instant arrival, Instant departure){
+  public BigDecimal computeFare(CarSlot carSlot){
+    Instant arrival = carSlot.getArrivalTime();
+    Instant departure = carSlot.getDepartureTime();
     if(arrival.isAfter(departure)){
       throw new DepartureIsBeforeArrivalException("Departure is before arrival, should not happen");
     }

+ 0 - 6
src/main/java/eu/fibane/parkingtoll/persistence/InMemoryPersistenceManager.java

@@ -38,10 +38,6 @@ public class InMemoryPersistenceManager implements Dao<ParkingLot> {
         return result;
     }
 
-    public boolean parkingLotExists(Long id){
-        return parkingLotMap.get(id) != null;
-    }
-
     @Override
     public void update(Long id, ParkingLot parkingLot) {
         //secure case where user puts wrong ID
@@ -54,8 +50,6 @@ public class InMemoryPersistenceManager implements Dao<ParkingLot> {
         parkingLotMap.remove(id);
     }
 
-
-
     @Override
     public void clearDatabase() {
         parkingLotMap.clear();

+ 16 - 5
src/main/java/eu/fibane/parkingtoll/service/ParkingLotService.java

@@ -9,11 +9,15 @@ import eu.fibane.parkingtoll.model.CarSlot;
 import eu.fibane.parkingtoll.model.Layout;
 import eu.fibane.parkingtoll.model.ParkingLot;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
 import java.time.Instant;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.stream.Collectors;
 
+@Service
 public class ParkingLotService {
 
     private Dao<ParkingLot> persistenceManager;
@@ -51,7 +55,7 @@ public class ParkingLotService {
             carSlot.setDepartureTime(Instant.now());
             layout.incrementAndFree(carSlot.getSlot());
         }
-        BigDecimal fare = parkingLot.getPricingPolicy().computeFare(carSlot.getArrivalTime(), carSlot.getDepartureTime());
+        BigDecimal fare = parkingLot.getFareProcessor().computeFare(carSlot);
         carSlot.setPrice(fare);
         return carSlot;
     }
@@ -63,13 +67,15 @@ public class ParkingLotService {
         //check if there is available slots
         Layout layout = getLayoutByNameOrThrow(carSlotItem.getType(), parkingLot);
         if(layout.getAvailable().get() == 0){
-            throw new ParkingIsFullException("");
+            throw new ParkingIsFullException("Parking lot is full for this type of car");
         }
         //found & available spot
         synchronized (this){
             //double check locking
+            parkingLot = findParkingLotByIdOrThrow(parkingLotId);
+            layout = getLayoutByNameOrThrow(carSlotItem.getType(), parkingLot);
             if(layout.getAvailable().get() == 0){
-                throw new ParkingIsFullException("");
+                throw new ParkingIsFullException("Parking lot is full for this type of car");
             }
             Long slotId = layout.decrementAndGetID();
             carSlotItem.setSlot(slotId);
@@ -96,13 +102,18 @@ public class ParkingLotService {
     }
 
     public Collection<ParkingLot> searchParkingLot(String searchString) {
-        return persistenceManager.getAll();
+        if(searchString == null || searchString.trim().isEmpty()){
+            return persistenceManager.getAll();
+        }
+        return persistenceManager.getAll().stream().
+                filter(parkingLot -> parkingLot.getName().trim().toUpperCase().contains(searchString.trim().toUpperCase()))
+                .collect(Collectors.toCollection(ArrayList::new));
     }
 
     private Layout getLayoutByNameOrThrow(String slotName, ParkingLot parkingLot){
         return parkingLot.getLayoutList().stream()
                 .filter(lay -> lay.getName().equals(slotName)).findFirst()
-                .orElseThrow(() -> new ParkingTypeDoesNotExistException(""));
+                .orElseThrow(() -> new ParkingTypeDoesNotExistException("This type of slot does not exist in this parking lot"));
     }
 
     private ParkingLot findParkingLotByIdOrThrow(Long id){

+ 0 - 191
src/test/java/eu/fibane/parkingtoll/ParkingTollApplicationTests.java

@@ -1,191 +0,0 @@
-package eu.fibane.parkingtoll;
-
-import eu.fibane.parkingtoll.api.ParkingLotApiController;
-import eu.fibane.parkingtoll.persistence.InMemoryPersistenceManager;
-import eu.fibane.parkingtoll.exceptions.ParkingNotFoundException;
-import eu.fibane.parkingtoll.model.CarSlot;
-import eu.fibane.parkingtoll.model.ParkingLot;
-import eu.fibane.parkingtoll.model.ParkingLotTest;
-import eu.fibane.parkingtoll.model.PricingPolicy;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-
-import java.math.BigDecimal;
-import java.net.URI;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.*;
-
-@SpringBootTest
-class ParkingTollApplicationTests {
-
-	@Mock @Autowired
-	private InMemoryPersistenceManager persistenceManager;
-	@InjectMocks
-	private ParkingLotApiController parkingLotApiController;
-
-	private List<ParkingLot> parkingLots;
-
-	@BeforeEach
-	void init(){
-		MockitoAnnotations.initMocks(this);
-
-		//create data for our tests
-		parkingLots = new ArrayList<>();
-		parkingLots.add(ParkingLotTest.generateParking("Test parking 1",1L));
-		parkingLots.add(ParkingLotTest.generateParking("Test parking 2",2L));
-		parkingLots.add(ParkingLotTest.generateParking("Test parking 3",3L));
-		parkingLots.add(ParkingLotTest.generateParking("Test parking 3 second part",4L));
-	}
-
-	@Test
-	void searchEmptyParkingLotTest() {
-		when(persistenceManager.getAll()).thenReturn(Collections.emptyList());
-
-		ResponseEntity<Collection<ParkingLot>> result = parkingLotApiController.searchParkingLot("");
-		assertNotNull(result.getBody());
-		assertEquals(0, result.getBody().size());
-	}
-
-	@Test
-	void searchParkingLotTestWithWrongName() {
-		when(persistenceManager.getAll()).thenReturn(parkingLots);
-
-		ResponseEntity<Collection<ParkingLot>> result = parkingLotApiController.searchParkingLot("");
-		assertNotNull(result.getBody());
-		assertEquals(parkingLots.size(), result.getBody().size());
-
-		result = parkingLotApiController.searchParkingLot("String that is not in the names of the parking lots");
-		assertNotNull(result.getBody());
-		assertEquals(0, result.getBody().size());
-	}
-
-	@Test
-	void searchParkingLotTestWithCorrectName() {
-		when(persistenceManager.getAll()).thenReturn(parkingLots);
-
-		ResponseEntity<Collection<ParkingLot>> result = parkingLotApiController.searchParkingLot("");
-		assertNotNull(result.getBody());
-		assertEquals(parkingLots.size(), result.getBody().size());
-
-		//entire name
-		result = parkingLotApiController.searchParkingLot("Test parking 1");
-		assertNotNull(result.getBody());
-		assertEquals(1, result.getBody().size());
-
-		result = parkingLotApiController.searchParkingLot("Test parking 3");
-		assertNotNull(result.getBody());
-		assertEquals(2, result.getBody().size());
-	}
-
-	@Test
-	void parkingLotDeleteByIdTest(){
-		when(persistenceManager.delete(eq(-5L))).thenThrow(ParkingNotFoundException.class);
-		when(persistenceManager.delete(eq(1L))).thenReturn(parkingLots.get(0));
-
-		//non existing id
-		assertThrows(ParkingNotFoundException.class, () -> parkingLotApiController.deleteParkingLotById(-5L));
-
-		ResponseEntity<ParkingLot> result = parkingLotApiController.deleteParkingLotById(1L);
-		assertEquals(parkingLots.get(0), result.getBody());
-		assertEquals(result.getStatusCode(), HttpStatus.OK);
-
-		verify(persistenceManager, times(2)).delete(any());
-	}
-
-	@Test
-	void addParkingLotTest(){
-		ParkingLot storedParkingLot = ParkingLotTest.generateParking("created parking", 55L);
-
-		when(persistenceManager.save(any())).thenReturn(storedParkingLot);
-
-		ResponseEntity<ParkingLot> response = parkingLotApiController.createParkingLot(parkingLots.get(0));
-
-		assertEquals(HttpStatus.CREATED, response.getStatusCode());
-		assertEquals(URI.create("/parking_lot/55") , response.getHeaders().getLocation());
-		assertNotNull(response.getBody());
-		assertEquals(55L, response.getBody().getId());
-	}
-
-	@Test
-	void parkingLotGetByIdTest(){
-		ParkingLot storedParkingLot = ParkingLotTest.generateParking("created parking", 5L);
-		when(persistenceManager.findById(5L)).thenReturn(storedParkingLot);
-		ResponseEntity<ParkingLot> result = parkingLotApiController.getParkingById(5L);
-
-		assertEquals(HttpStatus.OK, result.getStatusCode());
-
-	}
-
-	@Test
-	void leaveParkingLotTest(){
-		ParkingLot storedParkingLot = ParkingLotTest.generateParking("created parking", 5L);
-		PricingPolicy policy = new PricingPolicy();
-		policy.setFlatFee(BigDecimal.valueOf(1));
-		storedParkingLot.setPricingPolicy(policy);
-
-		when(persistenceManager.findById(storedParkingLot.getId())).thenReturn(storedParkingLot);
-
-		CarSlot carSlot = new CarSlot();
-		carSlot.setArrivalTime(Instant.now().minus(Duration.ofHours(1)));
-		carSlot.setType(storedParkingLot.getSlotTypes().get(0));
-		storedParkingLot.parkCar(carSlot);
-
-		ResponseEntity<CarSlot> result = parkingLotApiController.leaveParkingLot(storedParkingLot.getId(), carSlot);
-		assertEquals(HttpStatus.OK, result.getStatusCode());
-	}
-
-	@Test
-	void parkParkingLotTest(){
-		ParkingLot storedParkingLot = ParkingLotTest.generateParking("created parking", 5L);
-		CarSlot carSlot = new CarSlot();
-		carSlot.setType(storedParkingLot.getSlotTypes().get(0));
-		when(persistenceManager.findById(storedParkingLot.getId())).thenReturn(storedParkingLot);
-
-		ResponseEntity<CarSlot> result = parkingLotApiController.parkAtParkingLot(storedParkingLot.getId(), carSlot);
-		assertEquals(HttpStatus.OK, result.getStatusCode());
-	}
-
-	@Test
-	void updateParkingLotTest(){
-		//no existing parking lots
-		when(persistenceManager.findById(any())).thenReturn(null);
-		when(persistenceManager.update(anyLong(), any())).thenReturn(true);
-
-		ParkingLot newParkingLot = ParkingLotTest.generateParking("created parking", 5L);
-		ResponseEntity<ParkingLot> result = parkingLotApiController.updateParkingLot(newParkingLot.getId(), newParkingLot);
-		assertEquals(HttpStatus.CREATED, result.getStatusCode());
-
-
-		when(persistenceManager.parkingLotExists(5L)).thenReturn(true);
-		newParkingLot = ParkingLotTest.generateParking("created parking", 5L);
-		result = parkingLotApiController.updateParkingLot(newParkingLot.getId(), newParkingLot);
-		assertEquals(HttpStatus.CREATED, result.getStatusCode());
-
-		ParkingLot updated = ParkingLotTest.generateParking("created parking", 6L);
-
-		when(persistenceManager.update(anyLong(), any())).thenReturn(false);
-		newParkingLot = ParkingLotTest.generateParking("created parking", 5L);
-		result = parkingLotApiController.updateParkingLot(newParkingLot.getId(), newParkingLot);
-		assertEquals(HttpStatus.NO_CONTENT, result.getStatusCode());
-
-
-
-	}
-
-}

+ 167 - 6
src/test/java/eu/fibane/parkingtoll/controller/ParkingLotApiControllerTest.java

@@ -1,26 +1,187 @@
 package eu.fibane.parkingtoll.controller;
 
 import eu.fibane.parkingtoll.api.ParkingLotApiController;
+import eu.fibane.parkingtoll.model.CarSlot;
+import eu.fibane.parkingtoll.model.ParkingLot;
+import eu.fibane.parkingtoll.model.ParkingLotTest;
+import eu.fibane.parkingtoll.model.PricingPolicy;
+import eu.fibane.parkingtoll.service.ParkingLotService;
 import org.apache.catalina.session.PersistentManager;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
 
-public class ParkingLotApiControllerTest {
+import java.math.BigDecimal;
+import java.net.URI;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.when;
 
-    //mocking our database
-    @Mock
-    private PersistentManager persistentManager;
+@SpringBootTest
+public class ParkingLotApiControllerTest {
 
+    @Mock @Autowired
+    private ParkingLotService parkingLotService;
     @InjectMocks
-    private ParkingLotApiController controller;
+    private ParkingLotApiController parkingLotApiController;
+
+    private List<ParkingLot> parkingLots;
 
     @BeforeEach
-    public void init() {
+    void init(){
         MockitoAnnotations.initMocks(this);
+
+        //create data for our tests
+        parkingLots = new ArrayList<>();
+        parkingLots.add(ParkingLotTest.generateParking("Test parking 1",1L));
+        parkingLots.add(ParkingLotTest.generateParking("Test parking 2",2L));
+        parkingLots.add(ParkingLotTest.generateParking("Test parking 3",3L));
+        parkingLots.add(ParkingLotTest.generateParking("Test parking 3 second part",4L));
+    }
+
+    @Test
+    void searchEmptyParkingLotTest() {
+        when(parkingLotService.searchParkingLot(any())).thenReturn(Collections.emptyList());
+
+        ResponseEntity<Collection<ParkingLot>> result = parkingLotApiController.searchParkingLot("");
+        assertNotNull(result.getBody());
+        assertEquals(0, result.getBody().size());
+    }
+
+    @Test
+    void searchParkingLotTestWithWrongName() {
+        when(parkingLotService.searchParkingLot("")).thenReturn(parkingLots);
+        when(parkingLotService.searchParkingLot("String that is not in the names of the parking lots"))
+                .thenReturn(Collections.emptyList());
+
+        ResponseEntity<Collection<ParkingLot>> result = parkingLotApiController.searchParkingLot("");
+        assertNotNull(result.getBody());
+        assertEquals(parkingLots.size(), result.getBody().size());
+
+        result = parkingLotApiController.searchParkingLot("String that is not in the names of the parking lots");
+        assertNotNull(result.getBody());
+        assertEquals(0, result.getBody().size());
+    }
+
+    @Test
+    void searchParkingLotTestWithCorrectName() {
+        when(parkingLotService.searchParkingLot("")).thenReturn(parkingLots);
+        when(parkingLotService.searchParkingLot("Test parking 1"))
+                .thenReturn(Collections.singletonList(parkingLots.get(0)));
+
+        when(parkingLotService.searchParkingLot("Test parking 3"))
+                .thenReturn(parkingLots.subList(2,4));
+
+        ResponseEntity<Collection<ParkingLot>> result = parkingLotApiController.searchParkingLot("");
+        assertNotNull(result.getBody());
+        assertEquals(parkingLots.size(), result.getBody().size());
+
+        //entire name
+        result = parkingLotApiController.searchParkingLot("Test parking 1");
+        assertNotNull(result.getBody());
+        assertEquals(1, result.getBody().size());
+
+        result = parkingLotApiController.searchParkingLot("Test parking 3");
+        assertNotNull(result.getBody());
+        assertEquals(2, result.getBody().size());
+    }
+
+    @Test
+    void parkingLotDeleteByIdTest(){
+        //non existing id
+        ResponseEntity<ParkingLot> result = parkingLotApiController.deleteParkingLotById(-5L);
+        assertEquals(HttpStatus.OK, result.getStatusCode());
+
+        when(parkingLotService.deleteParkingLotById(1L)).thenReturn(parkingLots.get(0));
+        result = parkingLotApiController.deleteParkingLotById(1L);
+        assertEquals(parkingLots.get(0), result.getBody());
+        assertEquals(result.getStatusCode(), HttpStatus.OK);
+
+        verify(parkingLotService, times(2)).deleteParkingLotById(any());
+    }
+
+    @Test
+    void addParkingLotTest(){
+        ParkingLot storedParkingLot = ParkingLotTest.generateParking("created parking", 55L);
+
+        when(parkingLotService.createParkingLot(any())).thenReturn(storedParkingLot);
+
+        ResponseEntity<ParkingLot> response = parkingLotApiController.createParkingLot(parkingLots.get(0));
+
+        assertEquals(HttpStatus.CREATED, response.getStatusCode());
+        assertEquals(URI.create("/parking_lot/55") , response.getHeaders().getLocation());
+        assertNotNull(response.getBody());
+        assertEquals(55L, response.getBody().getId());
+    }
+
+    @Test
+    void parkingLotGetByIdTest(){
+        ParkingLot storedParkingLot = ParkingLotTest.generateParking("created parking", 5L);
+        when(parkingLotService.getParkingById(5L)).thenReturn(storedParkingLot);
+
+        ResponseEntity<ParkingLot> result = parkingLotApiController.getParkingById(5L);
+        assertEquals(HttpStatus.OK, result.getStatusCode());
     }
 
+    @Test
+    void leaveParkingLotTest(){
+        ParkingLot storedParkingLot = ParkingLotTest.generateParking("created parking", 5L);
+        PricingPolicy policy = new PricingPolicy();
+        policy.setFlatFee(BigDecimal.valueOf(1));
+        storedParkingLot.setFareProcessor(policy);
 
+        when(parkingLotService.getParkingById(storedParkingLot.getId())).thenReturn(storedParkingLot);
+
+        CarSlot carSlot = new CarSlot();
+        carSlot.setArrivalTime(Instant.now().minus(Duration.ofHours(1)));
+        carSlot.setType(storedParkingLot.getSlotTypes().get(0));
+        //storedParkingLot.parkCar(carSlot);
+
+        ResponseEntity<CarSlot> result = parkingLotApiController.leaveParkingLot(storedParkingLot.getId(), carSlot);
+        assertEquals(HttpStatus.OK, result.getStatusCode());
+    }
+
+    @Test
+    void parkParkingLotTest(){
+        ParkingLot storedParkingLot = ParkingLotTest.generateParking("created parking", 5L);
+        CarSlot carSlot = new CarSlot();
+        carSlot.setType(storedParkingLot.getSlotTypes().get(0));
+        when(parkingLotService.getParkingById(storedParkingLot.getId())).thenReturn(storedParkingLot);
+
+        ResponseEntity<CarSlot> result = parkingLotApiController.parkAtParkingLot(storedParkingLot.getId(), carSlot);
+        assertEquals(HttpStatus.OK, result.getStatusCode());
+    }
+
+    @Test
+    void updateParkingLotTest(){
+        //no existing parking lots
+        ParkingLot storedParkingLot = ParkingLotTest.generateParking("created parking", 5L);
+        when(parkingLotService.updateParkingLot(anyLong(),any())).thenReturn(null);
+
+        ResponseEntity<ParkingLot> result = parkingLotApiController.updateParkingLot(5L, storedParkingLot);
+        assertEquals(HttpStatus.CREATED, result.getStatusCode());
+        assertEquals(URI.create("/parking_lot/" + storedParkingLot.getId()), result.getHeaders().getLocation());
+
+        when(parkingLotService.updateParkingLot(anyLong(), any())).thenReturn(storedParkingLot);
+        result = parkingLotApiController.updateParkingLot(5L, storedParkingLot);
+        assertEquals(HttpStatus.NO_CONTENT, result.getStatusCode());
+    }
+    
 
 }

+ 0 - 52
src/test/java/eu/fibane/parkingtoll/model/LayoutTest.java

@@ -39,56 +39,4 @@ class LayoutTest {
         assertTrue(id1.equals(id3) || id2.equals(id3));
     }
 
-    @Test
-    void parkCar() {
-        Layout layout = new Layout();
-        layout.setAvailable(2);
-        CarSlot carSlot = new CarSlot();
-        Instant now = Instant.now();
-        layout.parkCar(carSlot);
-        assertEquals(1, layout.getAvailable().get());
-        assertNotNull(carSlot.getSlot());
-        Duration duration = Duration.between(now,carSlot.getArrivalTime());
-        assertTrue(duration.toSeconds() < 1); // not the exact same time because of GC or VM sleep
-        assertEquals(layout.getCarSlots().get(carSlot.getSlot()), carSlot);
-
-        CarSlot carSlot2 = new CarSlot();
-        layout.parkCar(carSlot2);
-        assertEquals(0, layout.getAvailable().get());
-        assertNotNull(carSlot2.getSlot());
-        assertEquals(layout.getCarSlots().get(carSlot2.getSlot()), carSlot2);
-
-        CarSlot carSlot3 = new CarSlot();
-        assertThrows(ParkingIsFullException.class, () -> layout.parkCar(carSlot3));
-    }
-
-    @Test
-    void parkCarOnEmptyParkingLot(){
-        Layout layout = new Layout();
-        layout.setAvailable(0);
-        CarSlot carSlot = new CarSlot();
-        assertThrows(ParkingIsFullException.class, () -> layout.parkCar(carSlot));
-        assertEquals(0, layout.getAvailable().get());
-    }
-
-    @Test
-    void removeCar() {
-        Layout layout = new Layout();
-        layout.setAvailable(2);
-        CarSlot carSlot = new CarSlot();
-        layout.parkCar(carSlot);
-        assertEquals(1, layout.getAvailable().get());
-        assertNotNull(carSlot.getSlot());
-
-        //call the service
-        CarSlot removedCar = layout.removeCar(carSlot);
-        Instant now = Instant.now();
-        assertEquals(2, layout.getAvailable().get());
-        assertTrue(layout.getCarSlots().isEmpty());
-        assertNotNull(removedCar.getDepartureTime());
-        Duration duration = Duration.between(now,carSlot.getDepartureTime());
-        assertTrue(duration.toSeconds() < 1); // not the exact same time because of GC or VM sleep
-
-        assertThrows(NoSuchCarInParkingException.class, () -> layout.removeCar(carSlot));
-    }
 }

+ 0 - 71
src/test/java/eu/fibane/parkingtoll/model/ParkingLotTest.java

@@ -36,77 +36,6 @@ public class ParkingLotTest {
         return parkingLot;
     }
 
-    @Test
-    void parkCar() {
-        CarSlot carSlot = new CarSlot();
-        carSlot.setType(TYPES[0]);
-        CarSlot parkedCarSlot = parkingLot.parkCar(carSlot);
-        assertEquals(parkingLot.getId(), carSlot.getParkingLotId());
-        assertNotNull(carSlot.getArrivalTime());
-
-        //is the car parked at the right parking lot ?
-        Layout foundLayout = parkingLot.getLayoutList().stream().
-                filter(layout -> layout.getName().equals(parkedCarSlot.getType())).findFirst().orElse(null);
-        assertNotNull(foundLayout);
-        assertNotNull(foundLayout.getCarSlots());
-        assertTrue(foundLayout.getCarSlots().containsValue(parkedCarSlot));
-
-        CarSlot carSlot2 = new CarSlot();
-        carSlot2.setType(TYPES[1]);
-        CarSlot parkedCarSlot2 = parkingLot.parkCar(carSlot2);
-
-        //is the car parked at the right parking lot ?
-        Layout foundLayout2 = parkingLot.getLayoutList().stream().
-                filter(layout -> layout.getName().equals(parkedCarSlot2.getType())).findFirst().orElse(null);
-        assertNotNull(foundLayout2);
-        assertNotNull(foundLayout2.getCarSlots());
-        assertTrue(foundLayout2.getCarSlots().containsValue(parkedCarSlot2));
-
-        CarSlot carSlot3 = new CarSlot();
-        carSlot3.setType("TYPE THAT DOES NOT EXIST");
-        assertThrows(ParkingTypeDoesNotExistException.class, () -> parkingLot.parkCar(carSlot3));
-
-    }
-
-    @Test
-    void testParkingLotCapacity(){
-        CarSlot carSlot2 = new CarSlot();
-        carSlot2.setType(TYPES[1]);
-        CarSlot parkedCarSlot2 = parkingLot.parkCar(carSlot2);
-
-        CarSlot carSlot3 = new CarSlot();
-        carSlot3.setType(TYPES[1]);
-        CarSlot parkedCarSlot3 = parkingLot.parkCar(carSlot3);
-
-        CarSlot carSlot5 = new CarSlot();
-        carSlot5.setType(TYPES[1]);
-        assertThrows(ParkingIsFullException.class, () -> parkingLot.parkCar(carSlot5));
-
-    }
-
-
-    @Test
-    void removeCar() {
-        CarSlot carSlot2 = new CarSlot();
-        carSlot2.setType(TYPES[1]);
-        CarSlot parkedCarSlot2 = parkingLot.parkCar(carSlot2);
-
-        CarSlot carSlot3 = new CarSlot();
-        carSlot3.setType(TYPES[1]);
-        CarSlot parkedCarSlot3 = parkingLot.parkCar(carSlot3);
-
-        //parking lot for type 1 is full
-        Layout foundLayout2 = parkingLot.getLayoutList().stream().
-                filter(layout -> layout.getName().equals(parkedCarSlot2.getType())).findFirst().orElse(null);
-        assertNotNull(foundLayout2);
-        assertNotNull(foundLayout2.getCarSlots());
-        assertEquals(0, foundLayout2.getAvailable().get());
-
-        parkingLot.removeCar(parkedCarSlot3);
-        assertNotNull(foundLayout2.getCarSlots());
-        assertEquals(1, foundLayout2.getAvailable().get());
-
-    }
 
     @Test
     void getSlotTypes() {

+ 41 - 13
src/test/java/eu/fibane/parkingtoll/model/PricingPolicyTest.java

@@ -21,19 +21,30 @@ class PricingPolicyTest {
 
         Instant now = Instant.now();
         Instant oneHour = now.plus(Duration.ofHours(1));
+        CarSlot carSlot = new CarSlot();
+        carSlot.setArrivalTime(now);
+        carSlot.setDepartureTime(oneHour);
 
-        BigDecimal fare = policy.computeFare(now, oneHour);
+        BigDecimal fare = policy.computeFare(carSlot);
         assertEquals(flatFee, fare);
-        fare = policy.computeFare(now, now);
+        carSlot.setArrivalTime(now);
+        carSlot.setDepartureTime(now);
+
+        fare = policy.computeFare(carSlot);
         assertEquals(flatFee, fare);
 
         flatFee = BigDecimal.valueOf(- 12_355.55);
         policy.setFlatFee(flatFee);
-        fare = policy.computeFare(now, oneHour);
+        carSlot.setArrivalTime(now);
+        carSlot.setDepartureTime(oneHour);
+        fare = policy.computeFare(carSlot);
         assertEquals(flatFee, fare);
 
         Instant longAfter = now.plus(Duration.ofDays(500));
-        fare = policy.computeFare(now, longAfter);
+        carSlot.setArrivalTime(now);
+        carSlot.setDepartureTime(longAfter);
+
+        fare = policy.computeFare(carSlot);
         assertEquals(flatFee, fare);
     }
 
@@ -44,25 +55,36 @@ class PricingPolicyTest {
         BigDecimal flatFee = BigDecimal.valueOf(0);
         policy.setPerHourFare(rate);
         policy.setFlatFee(flatFee);
-
+        CarSlot carSlot = new CarSlot();
         Instant now = Instant.now();
-        BigDecimal fare = policy.computeFare(now, now);
+        carSlot.setDepartureTime(now);
+        carSlot.setArrivalTime(now);
+
+        BigDecimal fare = policy.computeFare(carSlot);
         assertEquals(0, BigDecimal.ZERO.compareTo(fare));
 
         Instant oneHour = now.plus(Duration.ofHours(1));
-        fare = policy.computeFare(now, oneHour);
+        carSlot.setArrivalTime(now);
+        carSlot.setDepartureTime(oneHour);
+        fare = policy.computeFare(carSlot);
         assertEquals(rate, fare);
 
         Instant twoHour = now.plus(Duration.ofHours(2));
-        fare = policy.computeFare(now, twoHour);
+        carSlot.setArrivalTime(now);
+        carSlot.setDepartureTime(twoHour);
+        fare = policy.computeFare(carSlot);
         assertEquals(0, rate.multiply(BigDecimal.valueOf(2)).compareTo(fare));
 
         Instant twoHourAndSomeMinutes = now.plus(Duration.ofHours(2)).plus(Duration.ofMinutes(15));
-        fare = policy.computeFare(now, twoHourAndSomeMinutes);
+        carSlot.setArrivalTime(now);
+        carSlot.setDepartureTime(twoHourAndSomeMinutes);
+        fare = policy.computeFare(carSlot);
         assertEquals(0, rate.multiply(BigDecimal.valueOf(3)).compareTo(fare));
 
         //departure is before arrival - should not happen
-        assertThrows(DepartureIsBeforeArrivalException.class,() -> policy.computeFare(twoHourAndSomeMinutes, now));
+        carSlot.setArrivalTime(twoHourAndSomeMinutes);
+        carSlot.setDepartureTime(now);
+        assertThrows(DepartureIsBeforeArrivalException.class,() -> policy.computeFare(carSlot));
     }
 
 
@@ -75,16 +97,22 @@ class PricingPolicyTest {
         policy.setFlatFee(flatFee);
 
         //0s fare
+        CarSlot carSlot = new CarSlot();
         Instant now = Instant.now();
-        BigDecimal fare = policy.computeFare(now, now);
+        carSlot.setArrivalTime(now);
+        carSlot.setDepartureTime(now);
+        BigDecimal fare = policy.computeFare(carSlot);
         assertEquals(flatFee, fare);
 
         //1h fare
-        fare = policy.computeFare(now, now.plus(Duration.ofHours(1)));
+        carSlot.setArrivalTime(now);
+        carSlot.setDepartureTime(now.plus(Duration.ofHours(1)));
+        fare = policy.computeFare(carSlot);
         assertEquals(0, flatFee.add(rate).compareTo(fare));
 
         //1h20m fare
-        fare = policy.computeFare(now, now.plus(Duration.ofHours(1)).plus(Duration.ofMinutes(20)));
+        carSlot.setDepartureTime(now.plus(Duration.ofHours(1)).plus(Duration.ofMinutes(20)));
+        fare = policy.computeFare(carSlot);
         assertEquals(0, flatFee.add(rate.multiply(BigDecimal.valueOf(2))).compareTo(fare));
     }
 

+ 13 - 23
src/test/java/eu/fibane/parkingtoll/persistence/InMemoryPersistenceManagerTest.java

@@ -17,7 +17,7 @@ import java.util.List;
 import static org.junit.jupiter.api.Assertions.*;
 
 class InMemoryPersistenceManagerTest {
-    private final PersistenceManager manager = new InMemoryPersistenceManager();
+    private final Dao<ParkingLot> manager = new InMemoryPersistenceManager();
     private final SecureRandom random = new SecureRandom();
 
     @BeforeEach
@@ -30,7 +30,6 @@ class InMemoryPersistenceManagerTest {
         manager.clearDatabase();
         assertEquals(0, manager.getAll().size());
         assertThrows(ParkingNotFoundException.class, () -> manager.findById(0L));
-        assertThrows(ParkingNotFoundException.class, () -> manager.delete(getNewParkingLot().getId()));
     }
 
     @Test
@@ -42,7 +41,7 @@ class InMemoryPersistenceManagerTest {
     }
 
     @Test
-    void addParkingLotTest() {
+    void saveTest() {
         ParkingLot parkingLot = getNewParkingLot();
         //call the service
         ParkingLot returnedParkingLot = manager.save(parkingLot);
@@ -77,7 +76,7 @@ class InMemoryPersistenceManagerTest {
 
 
     @Test
-    void getAllParkingLots() {
+    void getAllTest() {
 
         List<ParkingLot> parkingLots = new ArrayList<>();
         ParkingLot parkingLot;
@@ -96,7 +95,7 @@ class InMemoryPersistenceManagerTest {
     }
 
     @Test
-    void getParkingLotById() {
+    void findByIdTest() {
         List<ParkingLot> parkingLots = new ArrayList<>();
         for (int i = 0; i < 10; i++) {
             parkingLots.add(getNewParkingLot());
@@ -112,12 +111,12 @@ class InMemoryPersistenceManagerTest {
     }
 
     @Test
-    void updateParkingLot() {
+    void updateTest() {
         //first scenario, try to update an object that doesn't exist yet in database
         ParkingLot parkingLot = getNewParkingLot();
         assertEquals(0, manager.getAll().size());
         assertNull(parkingLot.getId());
-        manager.update(parkingLot.getId(), parkingLot);
+        manager.update(5L, parkingLot);
         assertEquals(1, manager.getAll().size());
         assertNotNull(parkingLot.getId());
 
@@ -129,18 +128,19 @@ class InMemoryPersistenceManagerTest {
     }
 
     @Test
-    void deleteParkingLot() {
+    void deleteTest() {
         ParkingLot parkingLot = getNewParkingLot();
         assertEquals(0, manager.getAll().size());
         manager.save(parkingLot);
         assertEquals(1, manager.getAll().size());
 
         //call the service
-        ParkingLot result = manager.delete(parkingLot.getId());
-        assertEquals(result,parkingLot);
+        manager.delete(parkingLot.getId());
         assertEquals(0, manager.getAll().size());
 
-        assertThrows(ParkingNotFoundException.class, () -> manager.delete(parkingLot.getId()));
+        //nothing thrown
+        manager.delete(parkingLot.getId());
+
         assertEquals(0, manager.getAll().size());
 
         manager.save(parkingLot);
@@ -148,19 +148,9 @@ class InMemoryPersistenceManagerTest {
         //now try to delete something else
         ParkingLot parkingLot1 = new ParkingLot();
         parkingLot1.setId(parkingLot.getId() + 1);
-        assertThrows(ParkingNotFoundException.class, () -> manager.delete(parkingLot1.getId()));
+        //nothing thrown
+        manager.delete(parkingLot1.getId());
         assertEquals(1, manager.getAll().size());
     }
 
-    @Test
-    void testDurations(){
-        Instant arrival = Instant.now();
-        Instant departure = arrival.plus(20, ChronoUnit.MINUTES).plus(27,ChronoUnit.HOURS);
-
-        Duration duration = Duration.between(arrival,departure);
-
-        assertEquals(20, duration.toMinutesPart());
-        assertEquals(27, duration.toHours());
-    }
-
 }

+ 214 - 0
src/test/java/eu/fibane/parkingtoll/service/ParkingLotServiceTest.java

@@ -0,0 +1,214 @@
+package eu.fibane.parkingtoll.service;
+
+import eu.fibane.parkingtoll.exceptions.NoSuchCarInParkingException;
+import eu.fibane.parkingtoll.exceptions.ParkingIsFullException;
+import eu.fibane.parkingtoll.exceptions.ParkingNotFoundException;
+import eu.fibane.parkingtoll.exceptions.ParkingTypeDoesNotExistException;
+import eu.fibane.parkingtoll.model.*;
+import eu.fibane.parkingtoll.persistence.InMemoryPersistenceManager;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+class ParkingLotServiceTest {
+
+
+    @Mock @Autowired
+    private InMemoryPersistenceManager persistenceManager;
+
+    @InjectMocks
+    private ParkingLotService service;
+
+    private List<ParkingLot> parkingLots;
+
+    @BeforeEach
+    void init(){
+        MockitoAnnotations.initMocks(this);
+
+        //create data for our tests
+        parkingLots = new ArrayList<>();
+        parkingLots.add(ParkingLotTest.generateParking("Test parking 1",1L));
+        parkingLots.add(ParkingLotTest.generateParking("Test parking 2",2L));
+        parkingLots.add(ParkingLotTest.generateParking("Test parking 3",3L));
+        parkingLots.add(ParkingLotTest.generateParking("Test parking 3 second part",4L));
+    }
+
+
+    @Test
+    void createParkingLot() {
+        ParkingLot storedParkingLot = ParkingLotTest.generateParking("created parking", 55L);
+        when(persistenceManager.save(any())).thenReturn(storedParkingLot);
+
+        ParkingLot parkingLot = service.createParkingLot(storedParkingLot);
+        assertEquals(storedParkingLot, parkingLot);
+    }
+
+    @Test
+    void deleteParkingLotById() {
+        when(persistenceManager.findById(5L)).thenReturn(null);
+        assertThrows(ParkingNotFoundException.class, () -> service.deleteParkingLotById(5L));
+
+        when(persistenceManager.findById(2L)).thenReturn(parkingLots.get(0));
+        ParkingLot parkingLot = service.deleteParkingLotById(2L);
+        assertEquals(parkingLots.get(0), parkingLot);
+    }
+
+    @Test
+    void getParkingById() {
+        when(persistenceManager.findById(2L)).thenReturn(parkingLots.get(0));
+        ParkingLot parkingLot = service.getParkingById(2L);
+        assertEquals(parkingLots.get(0), parkingLot);
+        when(persistenceManager.findById(5L)).thenReturn(null);
+        assertThrows(ParkingNotFoundException.class, () -> service.getParkingById(5L));
+    }
+
+    @Test
+    void leaveParkingLotWithFlatFee() {
+        when(persistenceManager.findById(parkingLots.get(0).getId())).thenReturn(parkingLots.get(0));
+
+        CarSlot carSlot = new CarSlot();
+        carSlot.setType(parkingLots.get(0).getSlotTypes().get(0));
+        carSlot = service.parkAtParkingLot(parkingLots.get(0).getId(),carSlot);
+
+        ParkingLot parkingLot = parkingLots.get(0);
+        PricingPolicy policy = new PricingPolicy();
+        policy.setFlatFee(BigDecimal.valueOf(25.2));
+        parkingLot.setFareProcessor(policy);
+        carSlot = service.leaveParkingLot(parkingLots.get(0).getId(), carSlot);
+
+        assertTrue(BigDecimal.valueOf(25.2).compareTo(carSlot.getPrice()) == 0);
+
+        assertNotNull(carSlot.getDepartureTime());
+        assertNotNull(carSlot.getArrivalTime());
+    }
+
+    @Test
+    void leaveParkingLotWithNoFee() {
+        when(persistenceManager.findById(parkingLots.get(0).getId())).thenReturn(parkingLots.get(0));
+
+        CarSlot carSlot = new CarSlot();
+        carSlot.setType(parkingLots.get(0).getSlotTypes().get(0));
+        carSlot = service.parkAtParkingLot(parkingLots.get(0).getId(),carSlot);
+
+        ParkingLot parkingLot = parkingLots.get(0);
+        PricingPolicy policy = new PricingPolicy();
+        policy.setFlatFee(BigDecimal.valueOf(0));
+        parkingLot.setFareProcessor(policy);
+        carSlot = service.leaveParkingLot(parkingLots.get(0).getId(), carSlot);
+
+        assertTrue(BigDecimal.valueOf(0).compareTo(carSlot.getPrice()) == 0);
+
+        assertNotNull(carSlot.getDepartureTime());
+        assertNotNull(carSlot.getArrivalTime());
+    }
+
+
+    @Test
+    void leaveParkingLotWhereWeAreNotParkedTest() {
+        when(persistenceManager.findById(parkingLots.get(0).getId())).thenReturn(parkingLots.get(0));
+        when(persistenceManager.findById(parkingLots.get(1).getId())).thenReturn(parkingLots.get(1));
+
+        CarSlot carSlot = new CarSlot();
+        carSlot.setType(parkingLots.get(0).getSlotTypes().get(0));
+        CarSlot finalCarSlot = service.parkAtParkingLot(parkingLots.get(0).getId(),carSlot);
+
+        ParkingLot parkingLot = parkingLots.get(0);
+        PricingPolicy policy = new PricingPolicy();
+        policy.setFlatFee(BigDecimal.valueOf(25.2));
+        parkingLot.setFareProcessor(policy);
+        assertThrows(NoSuchCarInParkingException.class, () -> service.leaveParkingLot(parkingLots.get(1).getId(), finalCarSlot));
+    }
+
+    @Test
+    void leaveParkingLotWithFlatFeeAndPerHourTest() {
+        when(persistenceManager.findById(parkingLots.get(0).getId())).thenReturn(parkingLots.get(0));
+
+        CarSlot carSlot = new CarSlot();
+        carSlot.setType(parkingLots.get(0).getSlotTypes().get(0));
+        carSlot = service.parkAtParkingLot(parkingLots.get(0).getId(),carSlot);
+
+        ParkingLot parkingLot = parkingLots.get(0);
+        PricingPolicy policy = new PricingPolicy();
+        policy.setFlatFee(BigDecimal.valueOf(25.2));
+        policy.setPerHourFare(BigDecimal.valueOf(35.3));
+        parkingLot.setFareProcessor(policy);
+        carSlot = service.leaveParkingLot(parkingLots.get(0).getId(), carSlot);
+
+        assertTrue(BigDecimal.valueOf(25.2).compareTo(carSlot.getPrice()) == 0);
+
+        assertNotNull(carSlot.getDepartureTime());
+        assertNotNull(carSlot.getArrivalTime());
+    }
+
+    @Test
+    void parkAtParkingLot() {
+        when(persistenceManager.findById(parkingLots.get(0).getId())).thenReturn(parkingLots.get(0));
+        CarSlot carSlot = new CarSlot();
+        carSlot.setType(parkingLots.get(0).getSlotTypes().get(0));
+        carSlot = service.parkAtParkingLot(parkingLots.get(0).getId(),carSlot);
+        assertNotNull(carSlot.getSlot());
+        assertEquals(parkingLots.get(0).getId(), carSlot.getParkingLotId());
+
+        CarSlot carSlot2 = new CarSlot();
+        carSlot2.setType(parkingLots.get(0).getSlotTypes().get(0));
+
+        parkingLots.get(0).getLayoutList().forEach(layout -> layout.setAvailable(0));
+        //parking is now full
+        assertThrows(ParkingIsFullException.class, () -> service.parkAtParkingLot(parkingLots.get(0).getId(),carSlot2));
+    }
+
+    @Test
+    void parkAtParkingLotThatDoesNotExistTest() {
+        when(persistenceManager.findById(parkingLots.get(0).getId())).thenReturn(parkingLots.get(0));
+        CarSlot carSlot = new CarSlot();
+        carSlot.setType("type of slot that does not exist");
+        assertThrows(ParkingTypeDoesNotExistException.class, () -> service.parkAtParkingLot(parkingLots.get(0).getId(),carSlot));
+    }
+
+
+    @Test
+    void updateParkingLot() {
+        //new parking
+        when(persistenceManager.findById(any())).thenReturn(null);
+        when(persistenceManager.save(any())).thenReturn(parkingLots.get(1));
+        ParkingLot result = service.updateParkingLot(55L, parkingLots.get(0));
+        assertEquals(parkingLots.get(1), result);
+
+        //existing parking lot, should return null
+        when(persistenceManager.findById(any())).thenReturn(parkingLots.get(1));
+        result = service.updateParkingLot(55L, parkingLots.get(0));
+
+        assertNull(result);
+    }
+
+    @Test
+    void searchParkingLot() {
+        when(persistenceManager.getAll()).thenReturn(parkingLots);
+        Collection<ParkingLot> result = service.searchParkingLot("test parking 1");
+        assertEquals(1, result.size());
+        assertEquals(parkingLots.get(0), result.toArray()[0]);
+
+        result = service.searchParkingLot(" test parking 3     ");
+        assertEquals(2, result.size());
+
+        result = service.searchParkingLot("   ");
+        assertEquals(4, result.size());
+
+        result = service.searchParkingLot(" string that is not in the list  ");
+        assertEquals(0, result.size());
+
+
+    }
+}