Skip to content

Commit a0f9137

Browse files
author
Adrien GAREAU
committed
add test + reorganized file test
1 parent 1480c75 commit a0f9137

16 files changed

+360
-11
lines changed

src/main/java/com/happn/agareau/techtest/densitypop/controller/FileUploadController.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class FileUploadController {
2121
TSVService tsvService;
2222

2323
@PostMapping(value = "/upload", produces = "application/json")
24-
public ResponseEntity<?> handleFileUpload(@RequestParam("file") MultipartFile file) {
24+
public ResponseEntity<?> uploadTsvFile(@RequestParam("file") MultipartFile file) {
2525
return tsvService.uploadAndReadTSVFileAndReturnListPOI(file)
2626
.fold(errors -> status(INTERNAL_SERVER_ERROR).body(errors.toJavaList()),
2727
pointOfInterests -> status(CREATED).body(pointOfInterests));

src/main/java/com/happn/agareau/techtest/densitypop/controller/ZoneController.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ public class ZoneController {
2121
ZoneService zoneService;
2222

2323
@GetMapping(value = "/zone/number-poi/{min-lat}/{min-lon}", produces = "application/json")
24-
public long getList(@PathVariable("min-lat") double minLat, @PathVariable("min-lon") double minLon) {
24+
public long getNbPoiInZone(@PathVariable("min-lat") double minLat, @PathVariable("min-lon") double minLon) {
2525
Zone zone = new Zone(minLat, minLon, minLat + 0.5, minLon + 0.5);
2626
return zoneService.nbPoiInZone(zone);
2727
}
2828

2929
@GetMapping(value = "/zones/{nb-zones}/most-dense-zone", produces = "application/json")
30-
public ResponseEntity<?> getList(@PathVariable("nb-zones") int nbZones) {
30+
public ResponseEntity<?> getMostDensestZones(@PathVariable("nb-zones") int nbZones) {
3131
return zoneService.findMostDenseZone(nbZones)
3232
.fold(errors -> status(BAD_REQUEST).body(errors.toJavaList()),
3333
mostDenseZones -> status(OK).body(mostDenseZones));

src/main/java/com/happn/agareau/techtest/densitypop/error/Error.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public ReadFileError(String name, Throwable throwable) {
2727

2828
@Override
2929
public String getMessage() {
30-
return format("Unable to read file : %s%n%s", name, throwable);
30+
return format("Unable to read file : %s %s", name, throwable);
3131
}
3232
}
3333

@@ -42,7 +42,7 @@ public NbZonesNegError(int nbZones, Throwable throwable) {
4242

4343
@Override
4444
public String getMessage() {
45-
return format("Number of zones cannot be negative: %d%n%s", nbZones, throwable);
45+
return format("Number of zones cannot be negative: %d %s", nbZones, throwable);
4646
}
4747
}
4848

@@ -57,7 +57,7 @@ public CreatePOIEror(String name, Throwable throwable) {
5757

5858
@Override
5959
public String getMessage() {
60-
return format("Unable to create POI : %s%n%s", name, throwable);
60+
return format("Unable to create POI : %s %s", name, throwable);
6161
}
6262
}
6363

@@ -73,7 +73,7 @@ public UploadFileError(String name, Throwable throwable) {
7373

7474
@Override
7575
public String getMessage() {
76-
return format("Unable to upload file : %s%n%s", name, throwable);
76+
return format("Unable to upload file : %s %s", name, throwable);
7777
}
7878
}
7979

src/test/java/com/happn/agareau/techtest/densitypop/TSVServiceTest.java

+49-4
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,71 @@
88
import io.vavr.control.Validation;
99
import org.junit.jupiter.api.Test;
1010
import org.junit.jupiter.api.extension.ExtendWith;
11+
import org.mockito.Mockito;
1112
import org.mockito.junit.jupiter.MockitoExtension;
1213
import org.springframework.core.io.ClassPathResource;
1314
import org.springframework.core.io.Resource;
15+
import org.springframework.mock.web.MockMultipartFile;
1416

17+
import java.io.File;
1518
import java.io.IOException;
1619
import java.util.List;
1720

1821
import static org.assertj.vavr.api.VavrAssertions.assertThat;
1922
import static org.junit.jupiter.api.Assertions.*;
23+
import static org.mockito.ArgumentMatchers.anyString;
24+
import static org.mockito.Mockito.when;
2025

2126
@ExtendWith(MockitoExtension.class)
2227
class TSVServiceTest {
2328

29+
2430
private final SingletonListPOI singletonListPOI = new SingletonListPOI();
2531
private final TSVService tsvService = new TSVService(singletonListPOI);
2632

33+
@Test
34+
void should_upload_and_return_list() throws IOException {
35+
//GIVEN
36+
Resource resource = new ClassPathResource("test-coordinates/coordinatesTest.tsv");
37+
MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt",
38+
"text/plain", resource.getInputStream().readAllBytes());
39+
40+
//WHEN
41+
Validation<Seq<Error>, List<PointOfInterest>> listValidation = tsvService.uploadAndReadTSVFileAndReturnListPOI(multipartFile);
42+
43+
44+
//THEN
45+
assertThat(listValidation).isValid();
46+
assertNotNull(singletonListPOI.getPointOfInterests());
47+
assertNotNull(listValidation.get());
48+
assertEquals(9, listValidation.get().size());
49+
PointOfInterest pointOfInterest = listValidation.get().get(0);
50+
assertNotNull(pointOfInterest);
51+
assertEquals(-48.6, pointOfInterest.getLatitude());
52+
53+
}
54+
55+
// @Test
56+
// void upload_should_fail() throws IOException {
57+
// //GIVEN
58+
// MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt",
59+
// "text/plain", "Toto".getBytes());
60+
// when(File.createTempFile("temp", "toto")).thenThrow(new IOException());
61+
//
62+
// //WHEN
63+
// Validation<Seq<Error>, List<PointOfInterest>> lists = tsvService.uploadAndReadTSVFileAndReturnListPOI(multipartFile);
64+
//
65+
// //THEN
66+
// assertThat(lists).isInvalid();
67+
// assertNotNull(lists.getError());
68+
//
69+
//
70+
// }
71+
2772
@Test
2873
void should_return_list_poi() throws IOException {
2974
//GIVEN
30-
Resource resource = new ClassPathResource("coordinatesTest.tsv");
75+
Resource resource = new ClassPathResource("test-coordinates/coordinatesTest.tsv");
3176

3277
//WHEN
3378
Validation<Seq<Error>, List<PointOfInterest>> listPOIFromFile = tsvService.createListPOIFromFile(resource.getFile());
@@ -44,7 +89,7 @@ void should_return_list_poi() throws IOException {
4489
@Test
4590
void should_return_filtered_list_poi() throws IOException {
4691
//GIVEN
47-
Resource resource = new ClassPathResource("coordinatesTestWithIncorrectAndEmptyLines.tsv");
92+
Resource resource = new ClassPathResource("test-coordinates/coordinatesTestWithIncorrectAndEmptyLines.tsv");
4893

4994
//WHEN
5095
Validation<Seq<Error>, List<PointOfInterest>> listPOIFromFile = tsvService.createListPOIFromFile(resource.getFile());
@@ -61,7 +106,7 @@ void should_return_filtered_list_poi() throws IOException {
61106
@Test
62107
void should_return_empty_list() throws IOException {
63108
//GIVEN
64-
Resource resource = new ClassPathResource("empty.tsv");
109+
Resource resource = new ClassPathResource("test-coordinates/empty.tsv");
65110

66111

67112
//WHEN
@@ -75,7 +120,7 @@ void should_return_empty_list() throws IOException {
75120
@Test
76121
void should_handle_large_file_and_return_list_poi() throws IOException {
77122
//GIVEN
78-
Resource resource = new ClassPathResource("LargeFile100000lines.tsv");
123+
Resource resource = new ClassPathResource("test-coordinates/LargeFile100000lines.tsv");
79124

80125
//WHEN
81126
Validation<Seq<Error>, List<PointOfInterest>> listPOIFromFile = tsvService.createListPOIFromFile(resource.getFile());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package com.happn.agareau.techtest.densitypop.controller;
2+
3+
import com.happn.agareau.techtest.densitypop.domain.PointOfInterest;
4+
import com.happn.agareau.techtest.densitypop.error.Error;
5+
import com.happn.agareau.techtest.densitypop.properties.CoordinatesProperties;
6+
import com.happn.agareau.techtest.densitypop.service.TSVService;
7+
import io.vavr.collection.Seq;
8+
import io.vavr.control.Validation;
9+
import org.junit.jupiter.api.Test;
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.beans.factory.annotation.Value;
12+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
13+
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
14+
import org.springframework.boot.test.mock.mockito.MockBean;
15+
import org.springframework.core.io.Resource;
16+
import org.springframework.http.MediaType;
17+
import org.springframework.mock.web.MockMultipartFile;
18+
import org.springframework.test.web.servlet.MockMvc;
19+
import org.springframework.test.web.servlet.ResultActions;
20+
21+
import java.io.BufferedReader;
22+
import java.io.IOException;
23+
import java.io.InputStreamReader;
24+
import java.util.List;
25+
import java.util.stream.Collectors;
26+
27+
import static com.happn.agareau.techtest.densitypop.error.Error.ReadFileError;
28+
import static com.happn.agareau.techtest.densitypop.error.Error.UploadFileError;
29+
import static io.vavr.API.Seq;
30+
import static io.vavr.control.Validation.invalid;
31+
import static org.mockito.Mockito.when;
32+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
33+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
34+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
35+
36+
@WebMvcTest(FileUploadController.class)
37+
@EnableConfigurationProperties(CoordinatesProperties.class)
38+
class FileUploadControllerTest {
39+
40+
@Autowired
41+
private MockMvc mockMvc;
42+
43+
@MockBean
44+
private TSVService tsvService;
45+
46+
@Value("classpath:/response/uploadTsvFileOKResponse.json")
47+
private Resource uploadTsvFileOKResponse;
48+
49+
@Value("classpath:/response/uploadTsvFileKOResponse.json")
50+
private Resource uploadTsvFileKOResponse;
51+
52+
@Test
53+
void upload_file_and_return_list_should_success() throws Exception {
54+
55+
MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt",
56+
"text/plain", "Test upload".getBytes());
57+
58+
when(tsvService.uploadAndReadTSVFileAndReturnListPOI(multipartFile)).thenReturn(Validation.valid(createListPOI()));
59+
60+
61+
ResultActions resultActions = this.mockMvc
62+
.perform(multipart("/upload")
63+
.file(multipartFile));
64+
65+
// ResultActions resultActions = this.mockMvc.perform(post("/upload")
66+
// .contentType(MediaType.APPLICATION_JSON)
67+
// .content(Objects.requireNonNull(readResource(betResourceRequest))));
68+
69+
70+
resultActions.andExpect(status().is2xxSuccessful())
71+
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
72+
.andExpect(content().json(readResource(uploadTsvFileOKResponse), true));
73+
74+
}
75+
76+
@Test
77+
void upload_file_should_fail() throws Exception {
78+
79+
Seq<Error> fileError = Seq(new UploadFileError("cannot upload file", new Throwable()),
80+
new ReadFileError("cannot read file", new IOException()));
81+
82+
MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt",
83+
"text/plain", "Test upload".getBytes());
84+
85+
when(tsvService.uploadAndReadTSVFileAndReturnListPOI(multipartFile)).thenReturn(invalid(fileError));
86+
87+
ResultActions resultActions = this.mockMvc
88+
.perform(multipart("/upload")
89+
.file(multipartFile));
90+
91+
resultActions.andExpect(status().is5xxServerError())
92+
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
93+
.andExpect(content().json(readResource(uploadTsvFileKOResponse), true));
94+
95+
}
96+
97+
98+
private String readResource(Resource resource) throws IOException {
99+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
100+
return reader.lines().collect(Collectors.joining("\n"));
101+
}
102+
}
103+
104+
private List<PointOfInterest> createListPOI() {
105+
106+
PointOfInterest pointOfInterest = new PointOfInterest("id0", -48.6, -37.7);
107+
PointOfInterest pointOfInterest1 = new PointOfInterest("id1", -27.1, 8.4);
108+
PointOfInterest pointOfInterest2 = new PointOfInterest("id2", 6.6, -6.9);
109+
PointOfInterest pointOfInterest3 = new PointOfInterest("id3", -2.3, 38.3);
110+
PointOfInterest pointOfInterest4 = new PointOfInterest("id4", 6.8, -6.9);
111+
PointOfInterest pointOfInterest5 = new PointOfInterest("id5", -2.5, 38.3);
112+
PointOfInterest pointOfInterest6 = new PointOfInterest("id6", 0.1, -0.1);
113+
PointOfInterest pointOfInterest7 = new PointOfInterest("id7", -2.1, 38.1);
114+
PointOfInterest pointOfInterest8 = new PointOfInterest("id8", -2.1, 33.1);
115+
return List.of(pointOfInterest, pointOfInterest1, pointOfInterest2, pointOfInterest3, pointOfInterest4, pointOfInterest5, pointOfInterest6, pointOfInterest7, pointOfInterest8);
116+
117+
}
118+
119+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package com.happn.agareau.techtest.densitypop.controller;
2+
3+
import com.happn.agareau.techtest.densitypop.domain.PointOfInterest;
4+
import com.happn.agareau.techtest.densitypop.domain.Zone;
5+
import com.happn.agareau.techtest.densitypop.error.Error;
6+
import com.happn.agareau.techtest.densitypop.properties.CoordinatesProperties;
7+
import com.happn.agareau.techtest.densitypop.service.ZoneService;
8+
import io.vavr.control.Validation;
9+
import org.junit.jupiter.api.Test;
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.beans.factory.annotation.Value;
12+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
13+
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
14+
import org.springframework.boot.test.mock.mockito.MockBean;
15+
import org.springframework.core.io.Resource;
16+
import org.springframework.http.MediaType;
17+
import org.springframework.test.web.servlet.MockMvc;
18+
import org.springframework.test.web.servlet.ResultActions;
19+
20+
import java.io.BufferedReader;
21+
import java.io.IOException;
22+
import java.io.InputStreamReader;
23+
import java.util.List;
24+
import java.util.stream.Collectors;
25+
26+
import static io.vavr.API.Seq;
27+
import static org.mockito.ArgumentMatchers.any;
28+
import static org.mockito.Mockito.when;
29+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
30+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
31+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
32+
33+
@WebMvcTest(ZoneController.class)
34+
@EnableConfigurationProperties(CoordinatesProperties.class)
35+
class ZoneControllerTest {
36+
37+
@Autowired
38+
private MockMvc mockMvc;
39+
40+
@MockBean
41+
private ZoneService zoneService;
42+
43+
@Value("classpath:/response/mostDensestZonesOKResponse.json")
44+
private Resource mostDensestZonesOkResponse;
45+
46+
@Value("classpath:/response/mostDensestZonesKOResponse.json")
47+
private Resource mostDensestZonesKOResponse;
48+
49+
@Test
50+
void get_number_poi_should_success() throws Exception {
51+
52+
53+
when(zoneService.nbPoiInZone(any())).thenReturn(2L);
54+
55+
56+
ResultActions resultActions = this.mockMvc.perform(get("/zone/number-poi/1.0/2.0")
57+
.contentType(MediaType.APPLICATION_JSON));
58+
59+
60+
resultActions.andExpect(status().is2xxSuccessful())
61+
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
62+
.andExpect(content().string("2"));
63+
64+
}
65+
66+
@Test
67+
void get_most_densest_zones_should_success() throws Exception {
68+
69+
when(zoneService.findMostDenseZone(2)).thenReturn(Validation.valid(mostDensestZones()));
70+
71+
72+
ResultActions resultActions = this.mockMvc.perform(get("/zones/2/most-dense-zone")
73+
.contentType(MediaType.APPLICATION_JSON));
74+
75+
76+
resultActions.andExpect(status().is2xxSuccessful())
77+
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
78+
.andExpect(content().json(readResource(mostDensestZonesOkResponse), true));
79+
80+
}
81+
82+
@Test
83+
void get_most_densest_zones_should_fail_because_nb_zones_incorrect() throws Exception {
84+
85+
when(zoneService.findMostDenseZone(-3)).thenReturn(Validation.invalid(Seq(new Error.NbZonesNegError(-3, new Throwable()))));
86+
87+
88+
ResultActions resultActions = this.mockMvc.perform(get("/zones/-3/most-dense-zone")
89+
.contentType(MediaType.APPLICATION_JSON));
90+
91+
92+
resultActions.andExpect(status().isBadRequest())
93+
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
94+
.andExpect(content().json(readResource(mostDensestZonesKOResponse), true));
95+
96+
}
97+
98+
private List<Zone> mostDensestZones() {
99+
Zone zone1 = new Zone(-2.5, 38.0, -2.0, 38.5);
100+
Zone zone2 = new Zone(6.5, -7.0, 7.0, -6.5);
101+
return List.of(zone1, zone2);
102+
}
103+
104+
private String readResource(Resource resource) throws IOException {
105+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
106+
return reader.lines().collect(Collectors.joining("\n"));
107+
}
108+
}
109+
110+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mock-maker-inline

0 commit comments

Comments
 (0)