feat(cw2): Drone related test
This commit is contained in:
parent
4e87584723
commit
88a316c0f0
4 changed files with 606 additions and 3 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -40,3 +40,4 @@ out/
|
||||||
.direnv/
|
.direnv/
|
||||||
.envrc
|
.envrc
|
||||||
localjson
|
localjson
|
||||||
|
ilp-cw-api/results.json
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,17 @@ public class DroneInfoService {
|
||||||
public static final String servicePointsEndpoint = "service-points";
|
public static final String servicePointsEndpoint = "service-points";
|
||||||
public static final String restrictedAreasEndpoint = "restricted-areas";
|
public static final String restrictedAreasEndpoint = "restricted-areas";
|
||||||
|
|
||||||
private final RestTemplate restTemplate = new RestTemplate();
|
private final RestTemplate restTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor, handles the base url here.
|
* Constructor, handles the base url here.
|
||||||
*/
|
*/
|
||||||
public DroneInfoService() {
|
public DroneInfoService() {
|
||||||
|
this(new RestTemplate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DroneInfoService(RestTemplate restTemplate) {
|
||||||
|
this.restTemplate = restTemplate;
|
||||||
String baseUrl = System.getenv("ILP_ENDPOINT");
|
String baseUrl = System.getenv("ILP_ENDPOINT");
|
||||||
if (baseUrl == null || baseUrl.isBlank()) {
|
if (baseUrl == null || baseUrl.isBlank()) {
|
||||||
this.baseUrl =
|
this.baseUrl =
|
||||||
|
|
@ -268,11 +273,12 @@ public class DroneInfoService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Drone> fetchAllDrones() {
|
public List<Drone> fetchAllDrones() {
|
||||||
|
System.out.println("fetchAllDrones called");
|
||||||
String dronesEndpoint = "drones";
|
String dronesEndpoint = "drones";
|
||||||
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
|
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
|
||||||
|
System.out.println("Fetching from URL: " + droneUrl);
|
||||||
Drone[] drones = restTemplate.getForObject(droneUrl, Drone[].class);
|
Drone[] drones = restTemplate.getForObject(droneUrl, Drone[].class);
|
||||||
assert drones != null;
|
return drones == null ? new ArrayList<>() : Arrays.asList(drones);
|
||||||
return Arrays.asList(drones);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<RestrictedArea> fetchRestrictedAreas() {
|
public List<RestrictedArea> fetchRestrictedAreas() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,392 @@
|
||||||
|
package io.github.js0ny.ilp_coursework.controller;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import io.github.js0ny.ilp_coursework.data.common.DroneCapability;
|
||||||
|
import io.github.js0ny.ilp_coursework.data.common.LngLat;
|
||||||
|
import io.github.js0ny.ilp_coursework.data.external.Drone;
|
||||||
|
import io.github.js0ny.ilp_coursework.data.request.AttrQueryRequest;
|
||||||
|
import io.github.js0ny.ilp_coursework.data.request.MedDispatchRecRequest;
|
||||||
|
import io.github.js0ny.ilp_coursework.service.DroneAttrComparatorService;
|
||||||
|
import io.github.js0ny.ilp_coursework.service.DroneInfoService;
|
||||||
|
import io.github.js0ny.ilp_coursework.service.PathFinderService;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
@WebMvcTest(DroneController.class)
|
||||||
|
public class DroneControllerTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@MockitoBean
|
||||||
|
private DroneInfoService droneInfoService;
|
||||||
|
|
||||||
|
@MockitoBean
|
||||||
|
private DroneAttrComparatorService droneAttrComparatorService;
|
||||||
|
|
||||||
|
@MockitoBean
|
||||||
|
private PathFinderService pathFinderService;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
objectMapper = new ObjectMapper();
|
||||||
|
objectMapper.registerModule(new JavaTimeModule());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("GET /dronesWithCooling/{state}")
|
||||||
|
class GetDronesWithCoolingTest {
|
||||||
|
|
||||||
|
final String API_ENDPOINT_BASE = "/api/v1/dronesWithCooling/";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("true -> 200 OK")
|
||||||
|
void getDronesWithCooling_shouldReturn200AndArrayOfString_whenStateIsTrue()
|
||||||
|
throws Exception {
|
||||||
|
String endpoint = API_ENDPOINT_BASE + "true";
|
||||||
|
List<String> expected = List.of("1", "5", "8", "9");
|
||||||
|
when(
|
||||||
|
droneInfoService.dronesWithCooling(anyBoolean())).thenReturn(expected);
|
||||||
|
var mock = mockMvc.perform(
|
||||||
|
get(endpoint).contentType(MediaType.APPLICATION_JSON));
|
||||||
|
|
||||||
|
mock.andExpect(status().isOk());
|
||||||
|
mock.andExpect(
|
||||||
|
content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("false -> 200 OK")
|
||||||
|
void getDronesWithCooling_shouldReturn200AndArrayOfString_whenStateIsFalse()
|
||||||
|
throws Exception {
|
||||||
|
String endpoint = API_ENDPOINT_BASE + "false";
|
||||||
|
List<String> expected = List.of("2", "3", "4", "6", "7", "10");
|
||||||
|
when(
|
||||||
|
droneInfoService.dronesWithCooling(anyBoolean())).thenReturn(expected);
|
||||||
|
var mock = mockMvc.perform(
|
||||||
|
get(endpoint).contentType(MediaType.APPLICATION_JSON));
|
||||||
|
|
||||||
|
mock.andExpect(status().isOk());
|
||||||
|
mock.andExpect(
|
||||||
|
content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("-> 400 Bad Request")
|
||||||
|
void getDronesWithCooling_shouldReturn400_whenStateIsInvalid()
|
||||||
|
throws Exception {
|
||||||
|
String endpoint = API_ENDPOINT_BASE + "invalid";
|
||||||
|
mockMvc.perform(
|
||||||
|
get(endpoint).contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("GET /droneDetails/{droneId}")
|
||||||
|
class DroneDetailsTest {
|
||||||
|
|
||||||
|
static final String API_ENDPOINT_BASE = "/api/v1/droneDetails/";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("-> 200 OK")
|
||||||
|
void getDroneDetails_shouldReturn200AndJson_whenCorrectInput()
|
||||||
|
throws Exception {
|
||||||
|
Drone expected = new Drone("Drone 1", "1",
|
||||||
|
new DroneCapability(true, true, 4.0f, 2000, 0.01f, 4.3f, 6.5f));
|
||||||
|
String endpoint = API_ENDPOINT_BASE + "1";
|
||||||
|
when(
|
||||||
|
droneInfoService.droneDetail(anyString())).thenReturn(expected);
|
||||||
|
var mock = mockMvc.perform(
|
||||||
|
get(endpoint).contentType(MediaType.APPLICATION_JSON));
|
||||||
|
|
||||||
|
mock.andExpect(status().isOk());
|
||||||
|
mock.andExpect(
|
||||||
|
content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("-> 404 Not Found")
|
||||||
|
void getDroneDetails_shouldReturn404_whenDroneNotFound()
|
||||||
|
throws Exception {
|
||||||
|
String endpoint = API_ENDPOINT_BASE + "invalidDroneId";
|
||||||
|
when(
|
||||||
|
droneInfoService.droneDetail(anyString())).thenThrow(new IllegalArgumentException());
|
||||||
|
mockMvc.perform(
|
||||||
|
get(endpoint).contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("GET /queryAsPath/{attrName}/{attrVal}")
|
||||||
|
class GetQueryAsPathTests {
|
||||||
|
|
||||||
|
final String API_ENDPOINT_BASE = "/api/v1/queryAsPath/";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("capacity = 8 -> 200 OK")
|
||||||
|
void getQueryAsPath_shouldReturn200AndArrayOfString_whenCapacityIs8()
|
||||||
|
throws Exception {
|
||||||
|
String attrName = "capacity";
|
||||||
|
String attrVal = "8";
|
||||||
|
List<String> expected = List.of("2", "4", "7", "9");
|
||||||
|
when(droneAttrComparatorService.dronesWithAttribute(attrName, attrVal))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(get(API_ENDPOINT_BASE + attrName + "/" + attrVal)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("heating = true -> 200 OK")
|
||||||
|
void getQueryAsPath_shouldReturn200AndArrayOfString_whenHeatingIsTrue()
|
||||||
|
throws Exception {
|
||||||
|
String attrName = "heating";
|
||||||
|
String attrVal = "true";
|
||||||
|
List<String> expected = List.of("1", "2", "4", "5", "6", "7", "9");
|
||||||
|
when(droneAttrComparatorService.dronesWithAttribute(attrName, attrVal))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(get(API_ENDPOINT_BASE + attrName + "/" + attrVal)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("cooling = false -> 200 OK")
|
||||||
|
void getQueryAsPath_shouldReturn200AndArrayOfString_whenCoolingIsFalse()
|
||||||
|
throws Exception {
|
||||||
|
String attrName = "cooling";
|
||||||
|
String attrVal = "false";
|
||||||
|
List<String> expected = List.of("2", "3", "4", "6", "7", "10");
|
||||||
|
when(droneAttrComparatorService.dronesWithAttribute(attrName, attrVal))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(get(API_ENDPOINT_BASE + attrName + "/" + attrVal)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("maxMoves = 1000 -> 200 OK")
|
||||||
|
void getQueryAsPath_shouldReturn200AndArrayOfString_whenMaxMovesIs1000()
|
||||||
|
throws Exception {
|
||||||
|
String attrName = "maxMoves";
|
||||||
|
String attrVal = "1000";
|
||||||
|
List<String> expected = List.of("2", "4", "7", "9");
|
||||||
|
when(droneAttrComparatorService.dronesWithAttribute(attrName, attrVal))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(get(API_ENDPOINT_BASE + attrName + "/" + attrVal)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("invalid = null -> 200 OK (empty list)")
|
||||||
|
void getQueryAsPath_shouldReturn200AndEmptyArrayOfString_whenInvalidAttribute()
|
||||||
|
throws Exception {
|
||||||
|
String attrName = "invalid";
|
||||||
|
String attrVal = "null";
|
||||||
|
List<String> expected = List.of();
|
||||||
|
when(droneAttrComparatorService.dronesWithAttribute(attrName, attrVal))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(get(API_ENDPOINT_BASE + attrName + "/" + attrVal)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("POST /query")
|
||||||
|
class PostQueryTests {
|
||||||
|
|
||||||
|
final String API_ENDPOINT = "/api/v1/query";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("three eqs -> 200 OK")
|
||||||
|
void postQuery_shouldReturn200AndArrayOfString_whenThreeEqualsConditions()
|
||||||
|
throws Exception {
|
||||||
|
AttrQueryRequest req1 = new AttrQueryRequest("capacity", "=", "20");
|
||||||
|
AttrQueryRequest req2 = new AttrQueryRequest("heating", "=", "false");
|
||||||
|
AttrQueryRequest req3 = new AttrQueryRequest("cooling", "=", "true");
|
||||||
|
AttrQueryRequest[] requestBody = {req1, req2, req3};
|
||||||
|
List<String> expected = List.of("8");
|
||||||
|
|
||||||
|
when(droneAttrComparatorService.dronesSatisfyingAttributes(any(AttrQueryRequest[].class)))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(post(API_ENDPOINT)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(requestBody)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("GT LT -> 200 OK")
|
||||||
|
void postQuery_shouldReturn200AndArrayOfString_whenGtLtConditions()
|
||||||
|
throws Exception {
|
||||||
|
AttrQueryRequest req1 = new AttrQueryRequest("capacity", ">", "8");
|
||||||
|
AttrQueryRequest req2 = new AttrQueryRequest("maxMoves", "<", "2000");
|
||||||
|
AttrQueryRequest[] requestBody = {req1, req2};
|
||||||
|
List<String> expected = List.of("5", "10");
|
||||||
|
|
||||||
|
when(droneAttrComparatorService.dronesSatisfyingAttributes(any(AttrQueryRequest[].class)))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(post(API_ENDPOINT)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(requestBody)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("GT LT -contradict -> 200 OK (empty list)")
|
||||||
|
void postQuery_shouldReturn200AndEmptyArrayOfString_whenContradictoryConditions()
|
||||||
|
throws Exception {
|
||||||
|
AttrQueryRequest req1 = new AttrQueryRequest("capacity", ">", "8");
|
||||||
|
AttrQueryRequest req2 = new AttrQueryRequest("capacity", "<", "8");
|
||||||
|
AttrQueryRequest[] requestBody = {req1, req2};
|
||||||
|
List<String> expected = List.of();
|
||||||
|
|
||||||
|
when(droneAttrComparatorService.dronesSatisfyingAttributes(any(AttrQueryRequest[].class)))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(post(API_ENDPOINT)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(requestBody)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("GT LT EQ -> 200 OK")
|
||||||
|
void postQuery_shouldReturn200AndArrayOfString_whenGtLtEqConditions()
|
||||||
|
throws Exception {
|
||||||
|
AttrQueryRequest req1 = new AttrQueryRequest("capacity", ">", "8");
|
||||||
|
AttrQueryRequest req2 = new AttrQueryRequest("maxMoves", "<", "2000");
|
||||||
|
AttrQueryRequest req3 = new AttrQueryRequest("cooling", "=", "true");
|
||||||
|
AttrQueryRequest[] requestBody = {req1, req2, req3};
|
||||||
|
List<String> expected = List.of("5");
|
||||||
|
|
||||||
|
when(droneAttrComparatorService.dronesSatisfyingAttributes(any(AttrQueryRequest[].class)))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(post(API_ENDPOINT)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(requestBody)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("POST /queryAvailableDrones")
|
||||||
|
class PostQueryAvailableDronesTests {
|
||||||
|
|
||||||
|
final String API_ENDPOINT = "/api/v1/queryAvailableDrones";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Example -> 200 OK")
|
||||||
|
void postQueryAvailableDrones_shouldReturn200AndArrayOfString_whenExampleRequest()
|
||||||
|
throws Exception {
|
||||||
|
var reqs = new MedDispatchRecRequest.MedRequirement(0.75f, false, true, 13.5f);
|
||||||
|
var delivery = new LngLat(-3.00, 55.121);
|
||||||
|
var record = new MedDispatchRecRequest(123, LocalDate.parse("2025-12-22"), LocalTime.parse("14:30"), reqs, delivery);
|
||||||
|
MedDispatchRecRequest[] requestBody = {record};
|
||||||
|
List<String> expected = List.of("1", "2", "6", "7", "9");
|
||||||
|
|
||||||
|
when(droneInfoService.dronesMatchesRequirements(any(MedDispatchRecRequest[].class)))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(post(API_ENDPOINT)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(requestBody)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Treat Null as False (Cooling) -> 200 OK")
|
||||||
|
void postQueryAvailableDrones_shouldReturn200AndArrayOfString_whenCoolingIsNull()
|
||||||
|
throws Exception {
|
||||||
|
var requestMap = Map.of(
|
||||||
|
"id", 123,
|
||||||
|
"date", "2025-12-22",
|
||||||
|
"time", "14:30",
|
||||||
|
"requirements", Map.of(
|
||||||
|
"capacity", 0.75,
|
||||||
|
"heating", true,
|
||||||
|
"maxCost", 13.5
|
||||||
|
)
|
||||||
|
);
|
||||||
|
List<String> expected = List.of("1", "2", "6", "7", "9");
|
||||||
|
|
||||||
|
when(droneInfoService.dronesMatchesRequirements(any(MedDispatchRecRequest[].class)))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(post(API_ENDPOINT)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(new Object[]{requestMap})))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Complex -> 200 OK")
|
||||||
|
void postQueryAvailableDrones_shouldReturn200AndArrayOfString_whenComplexRequest()
|
||||||
|
throws Exception {
|
||||||
|
var reqs1 = new MedDispatchRecRequest.MedRequirement(0.75f, false, true, 13.5f);
|
||||||
|
var delivery1 = new LngLat(-3.00, 55.121);
|
||||||
|
var record1 = new MedDispatchRecRequest(123, LocalDate.parse("2025-12-22"), LocalTime.parse("14:30"), reqs1, delivery1);
|
||||||
|
|
||||||
|
var reqs2 = new MedDispatchRecRequest.MedRequirement(0.75f, false, true, 13.5f);
|
||||||
|
var delivery2 = new LngLat(-3.00, 55.121);
|
||||||
|
var record2 = new MedDispatchRecRequest(456, LocalDate.parse("2025-12-25"), LocalTime.parse("11:30"), reqs2, delivery2);
|
||||||
|
|
||||||
|
MedDispatchRecRequest[] requestBody = {record1, record2};
|
||||||
|
List<String> expected = List.of("2", "7", "9");
|
||||||
|
|
||||||
|
when(droneInfoService.dronesMatchesRequirements(any(MedDispatchRecRequest[].class)))
|
||||||
|
.thenReturn(expected);
|
||||||
|
|
||||||
|
mockMvc.perform(post(API_ENDPOINT)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(requestBody)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().json(objectMapper.writeValueAsString(expected)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
package io.github.js0ny.ilp_coursework.service;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import io.github.js0ny.ilp_coursework.data.common.DroneAvailability;
|
||||||
|
import io.github.js0ny.ilp_coursework.data.common.DroneCapability;
|
||||||
|
import io.github.js0ny.ilp_coursework.data.common.LngLat;
|
||||||
|
import io.github.js0ny.ilp_coursework.data.common.TimeWindow;
|
||||||
|
import io.github.js0ny.ilp_coursework.data.external.Drone;
|
||||||
|
import io.github.js0ny.ilp_coursework.data.external.ServicePointDrones;
|
||||||
|
import io.github.js0ny.ilp_coursework.data.request.MedDispatchRecRequest;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.time.DayOfWeek;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
public class DroneInfoServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RestTemplate restTemplate;
|
||||||
|
|
||||||
|
private DroneInfoService droneInfoService;
|
||||||
|
|
||||||
|
private final String baseUrl = "http://localhost:8080/";
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
droneInfoService = new DroneInfoService(restTemplate);
|
||||||
|
ReflectionTestUtils.setField(droneInfoService, "baseUrl", baseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drone[] getMockDrones() {
|
||||||
|
return new Drone[] {
|
||||||
|
new Drone("Drone 1", "1", new DroneCapability(true, true, 10, 1000, 1, 1, 1)),
|
||||||
|
new Drone("Drone 2", "2", new DroneCapability(false, true, 20, 2000, 2, 2, 2)),
|
||||||
|
new Drone("Drone 3", "3", new DroneCapability(false, false, 30, 3000, 3, 3, 3))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("dronesWithCooling(boolean) tests")
|
||||||
|
class DronesWithCoolingTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should return drones with cooling")
|
||||||
|
void dronesWithCooling_shouldReturnDronesWithCooling() {
|
||||||
|
// Arrange
|
||||||
|
when(restTemplate.getForObject(URI.create(baseUrl + "drones"), Drone[].class))
|
||||||
|
.thenReturn(getMockDrones());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
List<String> result = droneInfoService.dronesWithCooling(true);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(result).containsExactly("1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should return drones without cooling")
|
||||||
|
void dronesWithCooling_shouldReturnDronesWithoutCooling() {
|
||||||
|
// Arrange
|
||||||
|
when(restTemplate.getForObject(URI.create(baseUrl + "drones"), Drone[].class))
|
||||||
|
.thenReturn(getMockDrones());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
List<String> result = droneInfoService.dronesWithCooling(false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(result).containsExactly("2", "3");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should return empty list when API returns null")
|
||||||
|
void dronesWithCooling_shouldReturnEmptyList_whenApiReturnsNull() {
|
||||||
|
// Arrange
|
||||||
|
when(restTemplate.getForObject(URI.create(baseUrl + "drones"), Drone[].class))
|
||||||
|
.thenReturn(null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
List<String> result = droneInfoService.dronesWithCooling(true);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(result).isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("droneDetail(String) tests")
|
||||||
|
class DroneDetailTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should return correct drone details")
|
||||||
|
void droneDetail_shouldReturnCorrectDrone() {
|
||||||
|
// Arrange
|
||||||
|
when(restTemplate.getForObject(URI.create(baseUrl + "drones"), Drone[].class))
|
||||||
|
.thenReturn(getMockDrones());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Drone result = droneInfoService.droneDetail("2");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(result.id()).isEqualTo("2");
|
||||||
|
assertThat(result.name()).isEqualTo("Drone 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should throw exception for non-existent drone")
|
||||||
|
void droneDetail_shouldThrowException_forNonExistentDrone() {
|
||||||
|
// Arrange
|
||||||
|
when(restTemplate.getForObject(URI.create(baseUrl + "drones"), Drone[].class))
|
||||||
|
.thenReturn(getMockDrones());
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
assertThatThrownBy(() -> droneInfoService.droneDetail("4"))
|
||||||
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
|
.hasMessage("drone with that ID cannot be found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("dronesMatchesRequirements(MedDispatchRecRequest[]) tests")
|
||||||
|
class DronesMatchesRequirementsTests {
|
||||||
|
private ServicePointDrones[] getMockServicePointDrones() {
|
||||||
|
TimeWindow[] timeWindows = { new TimeWindow(DayOfWeek.MONDAY, LocalTime.of(9, 0), LocalTime.of(17, 0)) };
|
||||||
|
DroneAvailability drone1Avail = new DroneAvailability("1", timeWindows);
|
||||||
|
ServicePointDrones spd = new ServicePointDrones(1, new DroneAvailability[] { drone1Avail });
|
||||||
|
return new ServicePointDrones[] { spd };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should return drones matching a single requirement")
|
||||||
|
void dronesMatchesRequirements_shouldReturnMatchingDrones_forSingleRequirement() {
|
||||||
|
// Arrange
|
||||||
|
var drones = new Drone[] {
|
||||||
|
new Drone("Drone 1", "1", new DroneCapability(true, true, 10, 1000, 1, 1, 1)),
|
||||||
|
new Drone("Drone 2", "2", new DroneCapability(false, true, 5, 2000, 2, 2, 2))
|
||||||
|
};
|
||||||
|
when(restTemplate.getForObject(URI.create(baseUrl + "drones"), Drone[].class)).thenReturn(drones);
|
||||||
|
when(restTemplate.getForObject(URI.create(baseUrl + "drones-for-service-points"),
|
||||||
|
ServicePointDrones[].class)).thenReturn(getMockServicePointDrones());
|
||||||
|
|
||||||
|
var requirement = new MedDispatchRecRequest.MedRequirement(8, true, false, 100);
|
||||||
|
var record = new MedDispatchRecRequest(1, LocalDate.now().with(DayOfWeek.MONDAY), LocalTime.of(10, 0),
|
||||||
|
requirement, new LngLat(0, 0));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
List<String> result = droneInfoService.dronesMatchesRequirements(new MedDispatchRecRequest[] { record });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(result).containsExactly("1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should return empty list if no drones match")
|
||||||
|
void dronesMatchesRequirements_shouldReturnEmptyList_whenNoDronesMatch() {
|
||||||
|
// Arrange
|
||||||
|
var drones = new Drone[] {
|
||||||
|
new Drone("Drone 1", "1", new DroneCapability(true, true, 5, 1000, 1, 1, 1)),
|
||||||
|
new Drone("Drone 2", "2", new DroneCapability(false, true, 5, 2000, 2, 2, 2))
|
||||||
|
};
|
||||||
|
when(restTemplate.getForObject(URI.create(baseUrl + "drones"), Drone[].class)).thenReturn(drones);
|
||||||
|
// No need to mock drones-for-service-points as it won't be called
|
||||||
|
|
||||||
|
var requirement = new MedDispatchRecRequest.MedRequirement(10, true, false, 100);
|
||||||
|
var record = new MedDispatchRecRequest(1, LocalDate.now().with(DayOfWeek.MONDAY), LocalTime.of(10, 0),
|
||||||
|
requirement, new LngLat(0, 0));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
List<String> result = droneInfoService.dronesMatchesRequirements(new MedDispatchRecRequest[] { record });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(result).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Should return all drones if requirements are null or empty")
|
||||||
|
void dronesMatchesRequirements_shouldReturnAllDrones_forNullOrEmptyRequirements() {
|
||||||
|
// Arrange
|
||||||
|
when(restTemplate.getForObject(URI.create(baseUrl + "drones"), Drone[].class))
|
||||||
|
.thenReturn(getMockDrones());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
List<String> resultNull = droneInfoService.dronesMatchesRequirements(null);
|
||||||
|
List<String> resultEmpty = droneInfoService.dronesMatchesRequirements(new MedDispatchRecRequest[0]);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(resultNull).containsExactly("1", "2", "3");
|
||||||
|
assertThat(resultEmpty).containsExactly("1", "2", "3");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue