test: Add GpsCalculationServiceTest.java

This commit is contained in:
js0ny 2025-10-18 20:38:37 +01:00
parent 0e87787beb
commit 26e1c80326
11 changed files with 569 additions and 18 deletions

View file

@ -6,10 +6,11 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import io.github.js0ny.ilp_coursework.dto.DistanceRequestDto; import io.github.js0ny.ilp_coursework.data.DistanceRequestDto;
import io.github.js0ny.ilp_coursework.dto.LngLatDto; import io.github.js0ny.ilp_coursework.data.LngLatDto;
import io.github.js0ny.ilp_coursework.dto.MovementRequestDto; import io.github.js0ny.ilp_coursework.data.MovementRequestDto;
import io.github.js0ny.ilp_coursework.dto.RegionCheckRequestDto; import io.github.js0ny.ilp_coursework.data.RegionCheckRequestDto;
import io.github.js0ny.ilp_coursework.data.RegionDto;
import io.github.js0ny.ilp_coursework.service.GpsCalculationService; import io.github.js0ny.ilp_coursework.service.GpsCalculationService;
@RestController @RestController
@ -52,6 +53,8 @@ public class ApiController {
@PostMapping("/isInRegion") @PostMapping("/isInRegion")
public boolean getIsInRegion(@RequestBody RegionCheckRequestDto request) { public boolean getIsInRegion(@RequestBody RegionCheckRequestDto request) {
return true; LngLatDto position = request.position();
RegionDto region = request.region();
return gpsService.checkIsInRegion(position, region);
} }
} }

View file

@ -1,4 +1,4 @@
package io.github.js0ny.ilp_coursework.dto; package io.github.js0ny.ilp_coursework.data;
public record DistanceRequestDto(LngLatDto position1, LngLatDto position2) { public record DistanceRequestDto(LngLatDto position1, LngLatDto position2) {
} }

View file

@ -1,4 +1,4 @@
package io.github.js0ny.ilp_coursework.dto; package io.github.js0ny.ilp_coursework.data;
public record LngLatDto(double lng, double lat) { public record LngLatDto(double lng, double lat) {

View file

@ -1,4 +1,4 @@
package io.github.js0ny.ilp_coursework.dto; package io.github.js0ny.ilp_coursework.data;
/** /**
* Represents the data transfer object for a movement action request. * Represents the data transfer object for a movement action request.

View file

@ -0,0 +1,5 @@
package io.github.js0ny.ilp_coursework.data;
public record RegionCheckRequestDto(LngLatDto position, RegionDto region) {
}

View file

@ -1,11 +1,11 @@
package io.github.js0ny.ilp_coursework.dto; package io.github.js0ny.ilp_coursework.data;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
public record RegionDto(String name, List<LngLatDto> vertices) { public record RegionDto(String name, List<LngLatDto> vertices) {
public boolean isClose() { public boolean isClosed() {
// Magic number 4: For a polygon, 3 edges is required. // Magic number 4: For a polygon, 3 edges is required.
// In this dto, edges + 1 vertices is required. // In this dto, edges + 1 vertices is required.
if (vertices == null || vertices.size() < 4) { if (vertices == null || vertices.size() < 4) {

View file

@ -1,5 +0,0 @@
package io.github.js0ny.ilp_coursework.dto;
public record RegionCheckRequestDto(LngLatDto position) {
}

View file

@ -1,5 +1,34 @@
package io.github.js0ny.ilp_coursework.exception; package io.github.js0ny.ilp_coursework.exception;
public class GlobalExceptionHandler { import java.util.Map;
import java.util.Optional;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> handleHttpMessageNotReadable(HttpMessageNotReadableException ex) {
return Map.of("status", "400", "error", "Invalid JSON request body.");
}
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> handleIllegalArgument(IllegalArgumentException ex) {
String errorMessage = Optional.ofNullable(ex.getMessage())
.orElse("Invalid argument provided.");
return Map.of("status", "400", "error", errorMessage);
}
// @ExceptionHandler(NullPointerException.class)
// @ResponseStatus(HttpStatus.BAD_REQUEST)
// public Map<String, String> handleNullPointerException(NullPointerException
// ex) {
// return Map.of("error", "Invalid JSON request body.");
// }
} }

View file

@ -1,11 +1,15 @@
package io.github.js0ny.ilp_coursework.service; package io.github.js0ny.ilp_coursework.service;
import io.github.js0ny.ilp_coursework.data.LngLatDto;
import io.github.js0ny.ilp_coursework.data.RegionDto;
import java.util.List;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import io.github.js0ny.ilp_coursework.dto.LngLatDto;
@Service @Service
public class GpsCalculationService { public class GpsCalculationService {
private static final double STEP = 0.00015; private static final double STEP = 0.00015;
private static final double CLOSE_THRESHOLD = STEP; private static final double CLOSE_THRESHOLD = STEP;
@ -20,6 +24,16 @@ public class GpsCalculationService {
return distance < CLOSE_THRESHOLD; return distance < CLOSE_THRESHOLD;
} }
/**
* Called from <code>ApiController.getNextPosition</code>.
* <p>
* Returns the next position moved from <code>start</code> in the direction with <code>angle</code>, with step size
* 0.00015
*
* @param start The coordinate of the original start point.
* @param angle The direction to be moved in angle.
* @return The next position moved from <code>start</code>
*/
public LngLatDto nextPosition(LngLatDto start, double angle) { public LngLatDto nextPosition(LngLatDto start, double angle) {
double rad = Math.toRadians(angle); double rad = Math.toRadians(angle);
double newLng = Math.cos(rad) * STEP + start.lng(); double newLng = Math.cos(rad) * STEP + start.lng();
@ -27,4 +41,100 @@ public class GpsCalculationService {
return new LngLatDto(newLng, newLat); return new LngLatDto(newLng, newLat);
} }
/**
* Called from <code>ApiController.getIsInRegion</code>.
* <p>
* Used to check if the given <code>position</code>
* is inside the <code>region</code>, on edge and vertex is considered as inside.
*
* @param position The coordinate of the position.
* @param region A <code>RegionDto</code> that contains name and a list of <code>LngLatDto</code>
* @return true if <code>position</code> is inside the <code>region</code>.
* @throws IllegalArgumentException If <code>region</code> is not closed
*/
public boolean checkIsInRegion(LngLatDto position, RegionDto region) throws IllegalArgumentException {
if (!region.isClosed()) { // call method from RegionDto to check if not closed
throw new IllegalArgumentException("Region is not closed.");
}
return rayCasting(position, region.vertices());
}
/**
* Helper function to <code>checkIsInRegion</code>, use of ray-casting algorithm
* to check if inside the polygon
*
* @param point The point to check
* @param polygon The region that forms a polygon to check if <code>point</code>
* sits inside.
* @return If the <code>point</code> sits inside the <code>polygon</code> then
* return True
*/
private boolean rayCasting(LngLatDto point, List<LngLatDto> polygon) {
int intersections = 0;
int n = polygon.size();
for (int i = 0; i < n; ++i) {
LngLatDto a = polygon.get(i);
LngLatDto b = polygon.get((i + 1) % n); // Next vertex
if (isPointOnEdge(point, a, b)) {
return true;
}
// Ensure that a is norther than b, in order to easy classification
if (a.lat() > b.lat()) {
LngLatDto temp = a;
a = b;
b = temp;
}
// The point is not between a and b in latitude mean, skip this loop
if (point.lat() < a.lat() || point.lat() >= b.lat()) {
continue;
}
// Skip the case of horizontal edge, already handled in `isPointOnEdge`:w
if (a.lat() == b.lat()) {
continue;
}
double xIntersection = a.lng() + ((point.lat() - a.lat()) * (b.lng() - a.lng())) / (b.lat() - a.lat());
// // The point is on the edge
// if (xIntersection == point.lng()) {
// return true;
// }
if (xIntersection > point.lng()) {
++intersections;
}
}
// If intersections are odd, ray-casting returns true, which the point sits
// inside the polygon;
// If intersections are even, the point does not sit inside the polygon.
return intersections % 2 == 1;
}
/**
* Helper function from <code>rayCasting</code> that used to simply calculation <br>
* Used to check if point <code>p</code> is on the edge formed by <code>a</code> and <code>b</code>
*
* @param p point to be checked on the edge
* @param a point that forms the edge
* @param b point that forms the edge
* @return boolean, if <code>p</code> is on <code>ab</code> then true
*/
private boolean isPointOnEdge(LngLatDto p, LngLatDto a, LngLatDto b) {
// Cross product: (p - a) × (b - a)
double crossProduct = (p.lng() - a.lng()) * (b.lat() - a.lat())
- (p.lat() - a.lat()) * (b.lng() - a.lng());
if (Math.abs(crossProduct) > 1e-9) {
return false;
}
boolean isWithinLng = p.lng() >= Math.min(a.lng(), b.lng()) && p.lng() <= Math.max(a.lng(), b.lng());
boolean isWithinLat = p.lat() >= Math.min(a.lat(), b.lat()) && p.lat() <= Math.max(a.lat(), b.lat());
return isWithinLng && isWithinLat;
}
} }

View file

@ -0,0 +1,55 @@
package io.github.js0ny.ilp_coursework.controller;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.js0ny.ilp_coursework.data.DistanceRequestDto;
import io.github.js0ny.ilp_coursework.data.LngLatDto;
import io.github.js0ny.ilp_coursework.service.GpsCalculationService;
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;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
@WebMvcTest(ApiController.class)
public class ApiControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockitoBean
private GpsCalculationService gpsCalculationService;
@Test
void getUid_shouldReturnStudentIdFromService() throws Exception {
String endpoint = "/api/v1/uid";
String expected = "s2522255";
var mock = mockMvc.perform(get(endpoint));
mock.andExpect(MockMvcResultMatchers.status().isOk());
mock.andExpect(MockMvcResultMatchers.content().string(expected));
}
@Test
void getDistance_shouldReturnDoubleFromService_whenCorrectInput() throws Exception {
double expected = 5.0;
String endpoint = "/api/v1/distanceTo";
LngLatDto p1 = new LngLatDto(0, 4.0);
LngLatDto p2 = new LngLatDto(3.0, 0);
var req = new DistanceRequestDto(p1, p2);
var mock = mockMvc.perform(
post(endpoint)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(req))
);
mock.andExpect(MockMvcResultMatchers.status().isOk());
mock.andExpect(MockMvcResultMatchers.content().string(String.valueOf(expected)));
}
}

View file

@ -0,0 +1,354 @@
package io.github.js0ny.ilp_coursework.service;
import io.github.js0ny.ilp_coursework.data.LngLatDto;
import io.github.js0ny.ilp_coursework.data.RegionDto;
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 java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.within;
public class GpsCalculationServiceTest {
private static final double STEP = 0.00015;
private static final double CLOSE_THRESHOLD = STEP;
private static final double PRECISION = 1e-9;
private GpsCalculationService service;
@BeforeEach
void setUpService() {
service = new GpsCalculationService();
}
@Nested
@DisplayName("Test for calculateDistance(LngLatDto, LngLatDto) -> double")
class CalculateDistanceTests {
@Test
@DisplayName("False: Given Example For Testing")
void isCloseTo_shouldReturnFalse_givenExample() {
var p1 = new LngLatDto(-3.192473, 55.946233);
var p2 = new LngLatDto(-3.192473, 55.942617);
double expected = 0.0036;
double actual = service.calculateDistance(p1, p2);
assertThat(actual).isCloseTo(expected, within(1e-4));
}
@Test
@DisplayName("General Case: 3-4-5 Triangle")
void calculateDistance_shouldReturnCorrectEuclideanDistance_forGeneralCase() {
var p1 = new LngLatDto(0, 3.0);
var p2 = new LngLatDto(4.0, 0);
double expected = 5.0;
double actual = service.calculateDistance(p1, p2);
assertThat(actual).isCloseTo(expected, within(PRECISION));
}
@Test
@DisplayName("Edge Case: Points are Identical")
void calculateDistance_shouldReturnZero_whenPointsAreIdentical() {
var p1 = new LngLatDto(123.85, 983.2119);
double expected = 0.0;
double actual = service.calculateDistance(p1, p1);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("Edge Case: Longitudinal-only movement")
void calculateDistance_shouldReturnCorrectDistance_forPointsWithSameLatitude() {
var p1 = new LngLatDto(123.85, 983.2119);
var p2 = new LngLatDto(133.85, 983.2119);
double expected = 10.0;
double actual = service.calculateDistance(p1, p2);
assertThat(actual).isCloseTo(expected, within(PRECISION));
}
@Test
@DisplayName("Edge Case: Latitude-only movement")
void calculateDistance_shouldReturnCorrectDistance_forPointsWithSameLongitude() {
var p1 = new LngLatDto(123.85, 983.2119);
var p2 = new LngLatDto(123.85, 973.2119);
double expected = 10.0;
double actual = service.calculateDistance(p1, p2);
assertThat(actual).isCloseTo(expected, within(PRECISION));
}
@Test
@DisplayName("General Case: Calculate with negative Coordinates")
void calculateDistance_shouldReturnCorrectEuclideanDistance_forNegativeCoordinates() {
LngLatDto p1 = new LngLatDto(-1.0, -2.0);
LngLatDto p2 = new LngLatDto(2.0, 2.0); // lngDiff = 3, latDiff = 4
double expected = 5.0;
double actual = service.calculateDistance(p1, p2);
assertThat(actual).isCloseTo(expected, within(PRECISION));
}
}
@Nested
@DisplayName("Test for isCloseTo(LngLatDto, LngLatDto) -> boolean")
class IsCloseToTests {
@Test
@DisplayName("False: Given Example For Testing")
void isCloseTo_shouldReturnFalse_givenExample() {
var p1 = new LngLatDto(-3.192473, 55.946233);
var p2 = new LngLatDto(-3.192473, 55.942617);
boolean expected = false;
boolean actual = service.isCloseTo(p1, p2);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("True: Two points are the same")
void isCloseTo_shouldReturnTrue_whenPointsAreIdentical() {
var p1 = new LngLatDto(151.86, 285.37);
boolean expected = true;
boolean actual = service.isCloseTo(p1, p1);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("True: Two points are close to each other and near threshold")
void isCloseTo_shouldReturnTrue_whenCloseAndSmallerThanThreshold() {
var p1 = new LngLatDto(0.0, 0.0);
var p2 = new LngLatDto(0.0, 0.00014);
boolean expected = true;
boolean actual = service.isCloseTo(p1, p2);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("False: Distance nears the threshold")
void isCloseTo_shouldReturnFalse_whenEqualsToThreshold() {
var p1 = new LngLatDto(0.0, 0.0);
var p2 = new LngLatDto(0.0, 0.00015);
boolean expected = false;
boolean actual = service.isCloseTo(p1, p2);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("False: Distance larger to threshold")
void isCloseTo_shouldReturnFalse_whenNotCloseAndLargerThanThreshold() {
var p1 = new LngLatDto(0.0, 0.0);
var p2 = new LngLatDto(0.0, 0.00016);
boolean expected = false;
boolean actual = service.isCloseTo(p1, p2);
assertThat(actual).isEqualTo(expected);
}
}
@Nested
@DisplayName("Test for nextPosition(LngLatDto, double) -> LngLatDto")
class NextPositionTests {
@Test
@DisplayName("General Case: nextPosition in East direction (0 degrees)")
void nextPosition_shouldMoveEast_forAngleZero() {
var start = new LngLatDto(0.0, 0.0);
double angle = 0;
// For 0 degrees, cos(0)=1, sin(0)=0. Move happens entirely on lng axis.
var expected = new LngLatDto(STEP, 0.0);
var actual = service.nextPosition(start, angle);
assertThat(actual.lng()).isCloseTo(expected.lng(), within(PRECISION));
assertThat(actual.lat()).isCloseTo(expected.lat(), within(PRECISION));
}
@Test
@DisplayName("Cardinal Direction: nextPosition in North direction (90 degrees)")
void nextPosition_shouldMoveNorth_forAngle90() {
var start = new LngLatDto(0.0, 0.0);
double angle = 90;
// For 90 degrees, cos(90)=0, sin(90)=1. Move happens entirely on lat axis.
var expected = new LngLatDto(0.0, STEP);
var actual = service.nextPosition(start, angle);
assertThat(actual.lng()).isCloseTo(expected.lng(), within(PRECISION));
assertThat(actual.lat()).isCloseTo(expected.lat(), within(PRECISION));
}
@Test
@DisplayName("Cardinal Direction: nextPosition in West direction (180 degrees)")
void nextPosition_shouldMoveWest_forAngle180() {
var start = new LngLatDto(0.0, 0.0);
double angle = 180;
// For 180 degrees, cos(180)=-1, sin(180)=0. Move happens entirely on negative lng axis.
var expected = new LngLatDto(-STEP, 0.0);
var actual = service.nextPosition(start, angle);
assertThat(actual.lng()).isCloseTo(expected.lng(), within(PRECISION));
assertThat(actual.lat()).isCloseTo(expected.lat(), within(PRECISION));
}
@Test
@DisplayName("Cardinal Direction: nextPosition in South direction (270 degrees)")
void nextPosition_shouldMoveSouth_forAngle270() {
var start = new LngLatDto(0.0, 0.0);
double angle = 270;
// For 270 degrees, cos(270)=0, sin(270)=-1. Move happens entirely on negative lat axis.
var expected = new LngLatDto(0.0, -STEP);
var actual = service.nextPosition(start, angle);
assertThat(actual.lng()).isCloseTo(expected.lng(), within(PRECISION));
assertThat(actual.lat()).isCloseTo(expected.lat(), within(PRECISION));
}
@Test
@DisplayName("Intercardinal Direction: nextPosition in Northeast direction (45 degrees)")
void nextPosition_shouldMoveNortheast_forAngle45() {
var start = new LngLatDto(0.0, 0.0);
double angle = 45;
// Δlng = step * cos(45°), Δlat = step * sin(45°)
double expectedLng = STEP * Math.cos(Math.toRadians(angle));
double expectedLat = STEP * Math.sin(Math.toRadians(angle));
var expected = new LngLatDto(expectedLng, expectedLat);
var actual = service.nextPosition(start, angle);
assertThat(actual.lng()).isCloseTo(expected.lng(), within(PRECISION));
assertThat(actual.lat()).isCloseTo(expected.lat(), within(PRECISION));
}
@Test
@DisplayName("Edge Case: Angle larger than 360 should wrap around")
void nextPosition_shouldHandleAngleGreaterThan360() {
var start = new LngLatDto(0.0, 0.0);
// 405 degrees is equivalent to 45 degrees (405 % 360 = 45).
double angle = 405;
double equivalentAngle = 45;
double expectedLng = STEP * Math.cos(Math.toRadians(equivalentAngle));
double expectedLat = STEP * Math.sin(Math.toRadians(equivalentAngle));
var expected = new LngLatDto(expectedLng, expectedLat);
var actual = service.nextPosition(start, angle);
assertThat(actual.lng()).isCloseTo(expected.lng(), within(PRECISION));
assertThat(actual.lat()).isCloseTo(expected.lat(), within(PRECISION));
}
@Test
@DisplayName("Edge Case: Negative angle should work correctly")
void nextPosition_shouldHandleNegativeAngle() {
var start = new LngLatDto(0.0, 0.0);
// A negative angle of -45° corresponds to the Southeast direction.
double angle = -45;
double expectedLng = STEP * Math.cos(Math.toRadians(angle));
double expectedLat = STEP * Math.sin(Math.toRadians(angle));
var expected = new LngLatDto(expectedLng, expectedLat);
var actual = service.nextPosition(start, angle);
assertThat(actual.lng()).isCloseTo(expected.lng(), within(PRECISION));
assertThat(actual.lat()).isCloseTo(expected.lat(), within(PRECISION));
}
}
@Nested
@DisplayName("Test for checkIsInRegion(LngLatDto, RegionDto) -> boolean")
class CheckIsInRegionTests {
public static final RegionDto RECTANGLE_REGION = new RegionDto("rectangle", List.of(new LngLatDto(0.0, 0.0), new LngLatDto(2.0, 0.0), new LngLatDto(2.0, 2.0), new LngLatDto(0.0, 2.0), new LngLatDto(0.0, 0.0)));
@Test
@DisplayName("General Case: Given Example for Testing")
void isInRegion_shouldReturnFalse_givenPolygonCentral() {
var position = new LngLatDto(1.234, 1.222);
var region = new RegionDto("central", List.of(new LngLatDto(-3.192473, 55.946233), new LngLatDto(-3.192473, 55.942617), new LngLatDto(-3.184319, 55.942617), new LngLatDto(-3.184319, 55.946233), new LngLatDto(-3.192473, 55.946233)));
boolean expected = false;
boolean actual = service.checkIsInRegion(position, region);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("General Case: Simple Rectangle")
void isInRegion_shouldReturnTrue_forSimpleRectangle() {
var position = new LngLatDto(1.0, 1.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("General Case: Simple Rectangle")
void isInRegion_shouldReturnFalse_forSimpleRectangle() {
var position = new LngLatDto(3.0, 1.0);
boolean expected = false;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("General Case: Simple Hexagon")
void isInRegion_shouldReturnTrue_forSimpleHexagon() {
var position = new LngLatDto(2.0, 2.0);
var region = new RegionDto("hexagon", List.of(new LngLatDto(1.0, 0.0), new LngLatDto(4.0, 0.0), new LngLatDto(5.0, 2.0), new LngLatDto(4.0, 4.0), new LngLatDto(1.0, 4.0), new LngLatDto(0.0, 2.0), new LngLatDto(1.0, 0.0)));
boolean expected = true;
boolean actual = service.checkIsInRegion(position, region);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("Edge Case: Small Triangle")
void isInRegion_shouldReturnTrue_forSmallTriangle() {
var position = new LngLatDto(0.00001, 0.00001);
var region = new RegionDto("triangle", List.of(new LngLatDto(0.0, 0.0), new LngLatDto(0.0001, 0.0), new LngLatDto(0.00005, 0.0001), new LngLatDto(0.0, 0.0)));
boolean expected = true;
boolean actual = service.checkIsInRegion(position, region);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("Edge Case: Point on Lower Edge of Rectangle")
void isInRegion_shouldReturnTrue_whenPointOnLowerEdge() {
var position = new LngLatDto(0.0, 1.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("Edge Case: Point on Upper Edge of Rectangle")
void isInRegion_shouldReturnTrue_whenPointOnUpperEdge() {
var position = new LngLatDto(2.0, 1.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("Edge Case: Point on Left Edge")
void isInRegion_shouldReturnTrue_whenPointOnLeftEdge() {
var position = new LngLatDto(0.0, 1.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("Edge Case: Point on Lower Vertex")
void isInRegion_shouldReturnTrue_whenPointOnLowerVertex() {
var position = new LngLatDto(0.0, 0.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
}
@Test
@DisplayName("Edge Case: Point on Upper Vertex")
void isInRegion_shouldReturnTrue_whenPointOnUpperVertex() {
var position = new LngLatDto(2.0, 2.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
}
}
}