Skip to content

Commit bb82fc0

Browse files
authored
Merge pull request johnlpage#15 from ifeelgood/main
Remove mongoTemplate dependency from test classes
2 parents 92e7c0e + 677fc01 commit bb82fc0

16 files changed

Lines changed: 262 additions & 172 deletions

.github/workflows/maven-verify.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@ on:
1414
pull_request:
1515
branches: [ "main" ]
1616
workflow_dispatch:
17-
17+
1818
jobs:
1919
build:
20-
2120
runs-on: ubuntu-latest
22-
21+
2322
steps:
2423
- uses: actions/checkout@v4
2524
- name: Set up JDK 17
@@ -46,5 +45,3 @@ jobs:
4645
# Github personal token to add comments to Pull Request
4746
token: ${{ secrets.GITHUB_TOKEN }}
4847
title: '# Java Code Coverage Report'
49-
50-

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
/.idea
99
*.iml
10+
.java-version
1011

1112
# Compiled class file
1213
*.class

memex/src/main/java/com/johnlpage/memex/controller/VehicleInspectionController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public ResponseEntity<PageDto<VehicleInspection>> getInspectionsByModel(
131131
* This is a very "Raw" API interface that lets the caller design their own query and projection
132132
* etc.
133133
*/
134-
@PostMapping("/inspections/query")
134+
@PostMapping(value ="/inspections/query", produces = MediaType.APPLICATION_JSON_VALUE)
135135
public ResponseEntity<String> mongoQuery(@RequestBody String requestBody) {
136136
List<VehicleInspection> result = queryService.mongoDbNativeQuery(requestBody);
137137
try {

memex/src/test/java/com/johnlpage/memex/cucumber/steps/CucumberSpringContextConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
@CucumberContextConfiguration
1717
@SpringBootTest(classes = CucumberTestsContainersConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
1818
@ActiveProfiles("test")
19-
@EmbeddedKafka(partitions = 1, topics = {"test"})
19+
@EmbeddedKafka
2020
public class CucumberSpringContextConfig {
2121

2222
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package com.johnlpage.memex.cucumber.steps;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import com.johnlpage.memex.cucumber.service.VehicleInspectionIdRangeValidator;
6+
import com.johnlpage.memex.model.VehicleInspection;
7+
import io.cucumber.datatable.DataTable;
8+
import io.cucumber.java.en.Given;
9+
import lombok.extern.slf4j.Slf4j;
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.beans.factory.annotation.Value;
12+
import org.springframework.core.ParameterizedTypeReference;
13+
import org.springframework.http.MediaType;
14+
import org.springframework.http.ResponseEntity;
15+
import org.springframework.web.client.RestClient;
16+
17+
import java.util.ArrayList;
18+
import java.util.Collections;
19+
import java.util.List;
20+
import java.util.Map;
21+
import java.util.stream.Collectors;
22+
import java.util.stream.LongStream;
23+
24+
import static org.junit.jupiter.api.Assertions.assertNotNull;
25+
26+
@Slf4j
27+
public class InspectionsPreConditionSteps {
28+
29+
@Value("${memex.base-url}")
30+
private String apiBaseUrl;
31+
32+
private final RestClient restClient;
33+
private final ObjectMapper objectMapper;
34+
private final VehicleInspectionIdRangeValidator idRangeValidator;
35+
36+
@Autowired
37+
public InspectionsPreConditionSteps(RestClient.Builder restClientBuilder, ObjectMapper objectMapper,
38+
VehicleInspectionIdRangeValidator idRangeValidator) {
39+
this.restClient = restClientBuilder.baseUrl(apiBaseUrl).build();
40+
this.objectMapper = objectMapper;
41+
this.idRangeValidator = idRangeValidator;
42+
}
43+
44+
@Given("the following vehicle inspections exist:")
45+
public void givenVehicleInspectionsExist(DataTable dataTable) throws JsonProcessingException {
46+
List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
47+
List<VehicleInspection> inspections = new ArrayList<>();
48+
49+
for (Map<String, String> row : rows) {
50+
String json = row.values().iterator().next(); // Assuming each row has a single JSON string value
51+
VehicleInspection inspection = objectMapper.readValue(json, VehicleInspection.class);
52+
inspections.add(inspection);
53+
Long testId = inspection.getTestid();
54+
assertNotNull(testId, "testid is expected to be part of the data input");
55+
idRangeValidator.validate(testId);
56+
}
57+
58+
upsertInspectionsViaApi(inspections);
59+
}
60+
61+
@Given("the vehicle inspection with id {long} does not exist")
62+
public void theFollowingVehicleInspectionDoesNotExist(long testId) throws JsonProcessingException {
63+
idRangeValidator.validate(testId);
64+
deleteInspectionsViaApi(Collections.singletonList(testId));
65+
}
66+
67+
@Given("the vehicle inspections in range {long}-{long} do not exist")
68+
public void theFollowingVehicleInspectionsInRangeDoesNotExist(long startId, long endId) throws JsonProcessingException {
69+
idRangeValidator.validateRange(startId, endId);
70+
List<Long> idsForDeletion = LongStream.rangeClosed(startId, endId)
71+
.boxed()
72+
.collect(Collectors.toList());
73+
74+
deleteInspectionsViaApi(idsForDeletion);
75+
}
76+
77+
@Given("the following vehicle inspections do not exist:")
78+
public void givenTheFollowingVehicleInspectionsDoNotExist(DataTable dataTable) throws JsonProcessingException {
79+
List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
80+
List<Long> idsToDelete = new ArrayList<>();
81+
82+
for (Map<String, String> row : rows) {
83+
if (row.size() != 1) {
84+
throw new IllegalArgumentException("Only one column per row is supported in this step for deletion criteria.");
85+
}
86+
Map.Entry<String, String> entry = row.entrySet().iterator().next();
87+
String key = entry.getKey();
88+
String inputQuery = entry.getValue();
89+
90+
String rangeCheck = "\"_id\": {\"$gte\": " + idRangeValidator.getRangeStart() + ", \"$lte\": " + idRangeValidator.getRangeEnd() + "}";
91+
String mongoQueryJson = String.format("{\"filter\": {%s, %s}}", rangeCheck, inputQuery.substring(inputQuery.indexOf('{') + 1, inputQuery.lastIndexOf('}'))); // Basic example, might need more robust query building
92+
93+
try {
94+
ResponseEntity<List<VehicleInspection>> responseEntity = restClient.post()
95+
.uri(apiBaseUrl + "/api/inspections/query")
96+
.contentType(MediaType.APPLICATION_JSON)
97+
.body(mongoQueryJson)
98+
.retrieve()
99+
.toEntity(new ParameterizedTypeReference<List<VehicleInspection>>() {
100+
});
101+
102+
if (responseEntity.getStatusCode().is2xxSuccessful() && responseEntity.getBody() != null) {
103+
List<VehicleInspection> inspections = responseEntity.getBody();
104+
inspections.forEach(insp -> idsToDelete.add(insp.getTestid()));
105+
} else {
106+
log.warn("Query {} returned status {} or empty body. No IDs added for deletion.",
107+
mongoQueryJson, responseEntity.getStatusCode());
108+
}
109+
} catch (Exception e) {
110+
log.error("Error querying API for a query {}", mongoQueryJson, e);
111+
throw new RuntimeException("Error querying API for a query: " + mongoQueryJson, e);
112+
}
113+
}
114+
115+
deleteInspectionsViaApi(idsToDelete);
116+
}
117+
118+
private void deleteInspectionsViaApi(List<Long> idsToDelete) throws JsonProcessingException {
119+
if (idsToDelete.isEmpty()) {
120+
log.info("Delete IDs list is empty, skipping API call.");
121+
return;
122+
}
123+
124+
List<Map<String, Object>> payload = idsToDelete.stream()
125+
.map(id -> Map.<String, Object>of("testid", id, "deleted", true))
126+
.collect(Collectors.toList());
127+
128+
String jsonPayload = objectMapper.writeValueAsString(payload);
129+
130+
try {
131+
log.info("apiBaseUrl: {}", apiBaseUrl);
132+
ResponseEntity<String> response = restClient.post()
133+
.uri(apiBaseUrl + "/api/inspections?updateStrategy=UPDATEWITHHISTORY&futz=true")
134+
.contentType(MediaType.APPLICATION_JSON)
135+
.body(jsonPayload)
136+
.retrieve()
137+
.toEntity(String.class);
138+
139+
if (response.getStatusCode().is2xxSuccessful()) {
140+
log.info("Successfully marked inspections for deletion. IDs count: {}", idsToDelete.size());
141+
} else {
142+
log.error("Failed to mark inspections for deletion. Status: {}, Body: {}",
143+
response.getStatusCode(), response.getBody());
144+
throw new RuntimeException("Failed to mark inspections for deletion: " + response.getStatusCode());
145+
}
146+
} catch (Exception e) {
147+
log.error("Error calling API to mark inspections for deletion. IDs count: {}", idsToDelete.size(), e);
148+
throw new RuntimeException("Error calling API to mark inspections for deletion: " + e.getMessage());
149+
}
150+
}
151+
152+
private void upsertInspectionsViaApi(List<VehicleInspection> inspections) throws JsonProcessingException {
153+
if (inspections.isEmpty()) {
154+
log.info("No inspections to upsert, skipping API call.");
155+
return;
156+
}
157+
158+
String jsonPayload = objectMapper.writeValueAsString(inspections);
159+
160+
try {
161+
ResponseEntity<String> response = restClient.post()
162+
.uri(apiBaseUrl + "/api/inspections?updateStrategy=UPDATEWITHHISTORY&futz=true")
163+
.contentType(MediaType.APPLICATION_JSON)
164+
.body(jsonPayload)
165+
.retrieve()
166+
.toEntity(String.class);
167+
168+
if (response.getStatusCode().is2xxSuccessful()) {
169+
log.info("Successfully upserted {} inspections.", inspections.size());
170+
} else {
171+
log.error("Failed to upsert inspections. Status: {}, Body: {}",
172+
response.getStatusCode(), response.getBody());
173+
throw new RuntimeException("Failed to upsert inspections: " + response.getStatusCode());
174+
}
175+
} catch (Exception e) {
176+
log.error("Error calling API to upsert inspections. Count: {}", inspections.size(), e);
177+
throw new RuntimeException("Error calling API to upsert inspections: " + e.getMessage());
178+
}
179+
}
180+
}
Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,54 @@
11
package com.johnlpage.memex.cucumber.steps;
22

3+
import static org.junit.jupiter.api.Assertions.*;
4+
35
import com.fasterxml.jackson.core.JsonProcessingException;
46
import com.fasterxml.jackson.databind.JsonNode;
57
import com.fasterxml.jackson.databind.ObjectMapper;
68
import com.johnlpage.memex.cucumber.service.VehicleInspectionIdRangeValidator;
79
import com.johnlpage.memex.model.VehicleInspection;
810
import io.cucumber.java.en.Then;
911
import io.cucumber.java.en.When;
12+
import jakarta.annotation.PostConstruct;
1013
import org.assertj.core.api.Assertions;
1114
import org.springframework.beans.factory.annotation.Autowired;
12-
import org.springframework.data.mongodb.core.MongoTemplate;
13-
import org.springframework.data.mongodb.core.query.Criteria;
14-
import org.springframework.data.mongodb.core.query.Query;
15+
import org.springframework.beans.factory.annotation.Value;
16+
import org.springframework.core.ParameterizedTypeReference;
17+
import org.springframework.http.MediaType;
18+
import org.springframework.http.ResponseEntity;
1519
import org.springframework.kafka.core.KafkaTemplate;
20+
import org.springframework.web.client.RestClient;
1621

1722
import java.util.Iterator;
1823
import java.util.List;
1924
import java.util.Map;
2025

21-
2226
public class KafkaConsumerSteps {
2327

28+
@Value("${memex.base-url}")
29+
private String apiBaseUrl;
30+
2431
@Autowired
2532
private KafkaTemplate<String, String> kafkaTemplate;
2633

2734
@Autowired
28-
private MongoTemplate mongoTemplate;
35+
private RestClient.Builder restClientBuilder;
36+
37+
private RestClient restClient;
38+
39+
@PostConstruct
40+
public void init() {
41+
this.restClient = restClientBuilder.baseUrl(apiBaseUrl).build();
42+
}
2943

3044
@Autowired
3145
private ObjectMapper objectMapper;
3246

3347
@Autowired
3448
private VehicleInspectionIdRangeValidator idRangeValidator;
3549

36-
@When("I send {int} vehicle inspections starting with id {long} to kafka with:")
37-
public void sendVehicleInspectionsToKafka(int count, long startId, String jsonTemplate) throws JsonProcessingException {
50+
@When("I send {int} vehicle inspections starting with id {long} to kafka {string} topic with:")
51+
public void sendVehicleInspectionsToKafka(int count, long startId, String topicName, String jsonTemplate) throws JsonProcessingException {
3852
idRangeValidator.validate(startId);
3953
long endIdInclusive = startId + count - 1;
4054
idRangeValidator.validate(endIdInclusive);
@@ -45,29 +59,44 @@ public void sendVehicleInspectionsToKafka(int count, long startId, String jsonTe
4559
vehicleInspection.setTestid(testId);
4660

4761
String message = objectMapper.writeValueAsString(vehicleInspection);
48-
kafkaTemplate.send("test", message);
62+
kafkaTemplate.send(topicName, message);
4963
}
5064
}
5165

52-
@Then("verify {int} vehicle inspections are saved starting from id {long} in mongo with:")
66+
@Then("{int} vehicle inspections starting from id {long} do exist with:")
5367
public void verifyVehicleInspectionsSaved(int count, long startId, String expectedJson) throws JsonProcessingException {
5468
long endId = startId + count - 1;
5569
idRangeValidator.validateRange(startId, endId);
5670

5771
JsonNode expectedNode = objectMapper.readTree(expectedJson);
5872

59-
Query query = Query.query(Criteria.where("testid").gte(startId).lte(endId));
60-
List<VehicleInspection> inspections = mongoTemplate.find(query, VehicleInspection.class);
61-
62-
Assertions.assertThat(inspections).hasSize(count);
63-
64-
for (VehicleInspection inspection : inspections) {
65-
JsonNode inspectionJson = objectMapper.readTree(objectMapper.writeValueAsString(inspection));
66-
assertJsonContains(expectedNode, inspectionJson);
67-
}
73+
String rangeCheck = "\"_id\": {\"$gte\": " + startId + ", \"$lte\": " + endId + "}";
74+
String mongoQueryJson = String.format("{\"filter\": {%s}}", rangeCheck);
75+
76+
ResponseEntity<List<VehicleInspection>> responseEntity = restClient.post()
77+
.uri(apiBaseUrl + "/api/inspections/query")
78+
.contentType(MediaType.APPLICATION_JSON)
79+
.body(mongoQueryJson)
80+
.retrieve()
81+
.toEntity(new ParameterizedTypeReference<List<VehicleInspection>>() {
82+
});
83+
84+
assertTrue(responseEntity.getStatusCode().is2xxSuccessful());
85+
List<VehicleInspection> inspections = responseEntity.getBody();
86+
87+
assertNotNull(inspections);
88+
assertEquals(count, inspections.size());
89+
inspections.forEach((inspection) -> {
90+
JsonNode actualJson = null;
91+
try {
92+
actualJson = objectMapper.readTree(objectMapper.writeValueAsString(inspection));
93+
} catch (JsonProcessingException e) {
94+
throw new RuntimeException("Vehicle inspection verification failed for testid: " + inspection.getTestid(), e);
95+
}
96+
assertJsonContains(expectedNode, actualJson);
97+
});
6898
}
6999

70-
71100
private void assertJsonContains(JsonNode expected, JsonNode actual) {
72101
for (Iterator<Map.Entry<String, JsonNode>> it = expected.fields(); it.hasNext(); ) {
73102
Map.Entry<String, JsonNode> field = it.next();
@@ -86,5 +115,4 @@ private void assertJsonContains(JsonNode expected, JsonNode actual) {
86115
}
87116
}
88117
}
89-
90-
}
118+
}

0 commit comments

Comments
 (0)