Skip to content

Commit 43ad02a

Browse files
Merge pull request #755 from IgnatiosSar/feature/743
ISSUE-743 # Support external CSV header referencing and add withHeaders
2 parents 34f8a7c + 4b3502a commit 43ad02a

File tree

16 files changed

+166
-34
lines changed

16 files changed

+166
-34
lines changed

core/src/main/java/org/jsmart/zerocode/core/domain/Parameterized.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919
public class Parameterized {
2020
private final List<Object> valueSource;
2121
private final List<String> csvSource;
22-
private final Boolean ignoreHeader;
22+
private final Boolean withHeaders;
2323

2424
public Parameterized(
2525
@JsonProperty("valueSource") List<Object> valueSource,
2626
@JsonProperty("csvSource") JsonNode csvSourceJsonNode,
27-
@JsonProperty("ignoreHeader") Boolean ignoreHeader) {
27+
@JsonProperty("withHeaders") Boolean withHeaders) {
2828
this.valueSource = valueSource;
29-
this.ignoreHeader = Optional.ofNullable(ignoreHeader).orElse(false);
29+
this.withHeaders = Optional.ofNullable(withHeaders).orElse(false);
3030
this.csvSource = Optional.ofNullable(csvSourceJsonNode).map(this::getCsvSourceFrom).orElse(Collections.emptyList());
3131
}
3232

@@ -65,16 +65,16 @@ private List<String> readCsvSourceFromExternalCsvFile(JsonNode csvSourceJsonNode
6565
List<String> csvSourceFileLines = Files.lines(path)
6666
.filter(StringUtils::isNotBlank)
6767
.collect(Collectors.toList());
68-
if (this.ignoreHeader) {
69-
return csvSourceFileLines.stream()
70-
.skip(1)
71-
.collect(Collectors.toList());
72-
}
68+
7369
return csvSourceFileLines;
7470
}
7571
return Collections.emptyList();
7672
}
7773

74+
public Boolean isWithHeaders() {
75+
return withHeaders;
76+
}
77+
7878
@Override
7979
public String toString() {
8080
return "Parameterized{" +

core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeParameterizedProcessorImpl.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import java.util.List;
1616
import java.util.Map;
1717
import java.util.stream.IntStream;
18+
import java.util.regex.Matcher;
19+
import java.util.regex.Pattern;
1820

1921
import static org.jsmart.zerocode.core.constants.ZerocodeConstants.DSL_FORMAT;
2022
import static org.jsmart.zerocode.core.di.provider.CsvParserProvider.LINE_SEPARATOR;
@@ -116,7 +118,7 @@ private ScenarioSpec resolveParamsCsv(ScenarioSpec scenario, int paramIndex) {
116118
return scenario;
117119
}
118120

119-
String[] headers = retrieveCsvHeaders(parameterizedCsvList.get(0));
121+
String[] headers = retrieveCsvHeaders(parameterizedCsvList.get(0),scenario.getParameterized().isWithHeaders());
120122

121123
paramIndex = headers == null ? paramIndex : paramIndex+1;
122124

@@ -126,17 +128,29 @@ private ScenarioSpec resolveParamsCsv(ScenarioSpec scenario, int paramIndex) {
126128

127129
String resultantStepJson = replaceWithValues(stepJson, valuesMap);
128130

131+
ensureAllParamsResolved(resultantStepJson, headers);
132+
129133
return objectMapper.readValue(resultantStepJson, ScenarioSpec.class);
130134

131135
} catch (Exception exx) {
132136
throw new RuntimeException("Error while resolving parameterizedCsv values - " + exx);
133137
}
134138
}
135139

136-
private String[] retrieveCsvHeaders(String csvHeaderLine) {
140+
private String[] retrieveCsvHeaders(String csvHeaderLine, Boolean isWithHeaders) {
137141
String[] parsedHeaderLine = csvParser.parseLine(csvHeaderLine + LINE_SEPARATOR);
138-
boolean hasHeader = parsedHeaderLine.length > 0 && Arrays.stream(parsedHeaderLine).allMatch(s -> s.matches("^\\|.*\\|$"));
139-
return !hasHeader ? null : Arrays.stream(parsedHeaderLine).map(s -> s.substring(1,s.length()-1)).toArray(String[]::new);
142+
if (isWithHeaders && parsedHeaderLine.length > 0) {
143+
boolean hasPipes = Arrays.stream(parsedHeaderLine).allMatch(s -> s.matches("^\\|.*\\|$"));
144+
if (hasPipes) {
145+
LOGGER.warn("DEPRECATION WARNING: The '||' (piped) syntax in CSV headers is deprecated. " +
146+
"Please stop using it and start using standard header syntax with 'withHeaders: true' field instead. " +
147+
"Visit the documentation for more details.");
148+
return Arrays.stream(parsedHeaderLine).map(s -> s.substring(1,s.length()-1)).toArray(String[]::new);
149+
}
150+
return parsedHeaderLine;
151+
} else {
152+
return null;
153+
}
140154
}
141155

142156
private Map<String, Object> resolveCsvLine(String csvLine, String[] headers) {
@@ -159,4 +173,19 @@ private String replaceWithValues(String stepJson, Map<String, Object> valuesMap)
159173
return sub.replace(stepJson);
160174
}
161175

176+
private void ensureAllParamsResolved(String resultantStepJson, String[] availableHeaders) {
177+
Pattern pattern = Pattern.compile("\\$\\{PARAM\\.([^}]+)\\}");
178+
Matcher matcher = pattern.matcher(resultantStepJson);
179+
180+
if (matcher.find()) {
181+
String msg;
182+
if (availableHeaders == null || availableHeaders.length == 0) {
183+
msg = "No available headers found. Ensure your CSV file is not empty and that '\"withHeaders\": true' is defined in the scenario.";
184+
} else {
185+
msg = "Available headers are: [" + String.join(", ", availableHeaders) + "].";
186+
}
187+
throw new RuntimeException("Invalid CSV header referenced in scenario. The column '" + matcher.group(1) + "' does not exist. " + msg);
188+
189+
}
190+
}
162191
}

core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -565,8 +565,7 @@ private int getParameterSize(Parameterized parameterized) {
565565
int csvSourceSize = 0;
566566

567567
if (csvSource != null && !csvSource.isEmpty()){
568-
String[] parsedHeaderLine = csvParser.parseLine(csvSource.get(0) + LINE_SEPARATOR);
569-
boolean hasHeader = parsedHeaderLine.length > 0 && Arrays.stream(parsedHeaderLine).allMatch(s -> s.matches("^\\|.*\\|$"));
568+
Boolean hasHeader = parameterized.isWithHeaders();
570569
csvSourceSize = hasHeader ? csvSource.size() -1 : csvSource.size();
571570
}
572571

core/src/test/java/org/jsmart/zerocode/core/domain/ParameterizedTest.java

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public void testSerDe_valueSource() throws Exception {
5252
assertThat(parameterized.getValueSource(), hasItem(true));
5353

5454
String actualJson = mapper.writeValueAsString(parameterized);
55-
assertThat(actualJson, is("{\"valueSource\":[\"hello\",123,true],\"csvSource\":[\"1, 2, 200\",\"11, 22, 400\"]}"));
55+
assertThat(actualJson, is("{\"valueSource\":[\"hello\",123,true],\"csvSource\":[\"1, 2, 200\",\"11, 22, 400\"],\"withHeaders\":false}"));
5656
}
5757

5858
@Test
@@ -79,19 +79,4 @@ public void shouldReadCsvSourceFromCsvFile() throws IOException {
7979
assertThat(parameterized.getCsvSource(), hasItem("siddhagalaxy,Sidd,UK,33847730"));
8080
}
8181

82-
@Test
83-
public void shouldReadCsvSourceFromCsvFileIgnoringHeader() throws IOException {
84-
//given
85-
String jsonDocumentAsString =
86-
smartUtils.getJsonDocumentAsString("unit_test_files/engine_unit_test_jsons/08.2_parameterized_csv_source_from_file_containing_header.json");
87-
88-
//when
89-
Parameterized parameterized = mapper.readValue(jsonDocumentAsString, Parameterized.class);
90-
91-
//then
92-
assertThat(parameterized.getCsvSource(), hasItem("octocat,The Octocat,San Francisco,583231"));
93-
assertThat(parameterized.getCsvSource(), hasItem("siddhagalaxy,Sidd,UK,33847730"));
94-
assertThat(parameterized.getCsvSource(), everyItem(not(is("user,name,city,userid"))));//assert header is ignored
95-
}
96-
9782
}

core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeParameterizedProcessorImplTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,38 @@ public void testProcessParameterized_csv_with_named_random() throws Exception {
108108
assertThat(scenarioSpecResolved.getSteps().get(0).getUrl(), is("/anUrl/11/22"));
109109
assertThat(scenarioSpecResolved.getSteps().get(0).getAssertions().get("status").asInt(), is(400));
110110
}
111+
112+
@Test
113+
public void testProcessParameterized_csv_with_headers() throws Exception {
114+
String jsonDocumentAsString = smartUtils
115+
.getJsonDocumentAsString("unit_test_files/engine_unit_test_jsons/11.3_scenario_parameterized_csv_with_headers.json");
116+
ScenarioSpec scenarioSpec = mapper.readValue(jsonDocumentAsString, ScenarioSpec.class);
117+
118+
ScenarioSpec scenarioSpecResolved = parameterizedProcessor.resolveParameterized(scenarioSpec, 0);
119+
120+
121+
assertThat(scenarioSpecResolved.getSteps().get(0).getUrl(), is("/users/1"));
122+
assertThat(scenarioSpecResolved.getSteps().get(0).getRequest().get("body").get("name").asText(), is("octocat"));
123+
}
124+
125+
@Test
126+
public void testProcessParameterized_csv_with_invalid_header_throws_exception() throws Exception {
127+
String jsonDocumentAsString = smartUtils
128+
.getJsonDocumentAsString("unit_test_files/engine_unit_test_jsons/11.4_scenario_parameterized_csv_with_invalid_header.json");
129+
ScenarioSpec scenarioSpec = mapper.readValue(jsonDocumentAsString, ScenarioSpec.class);
130+
131+
132+
try {
133+
parameterizedProcessor.resolveParameterized(scenarioSpec, 0);
134+
135+
org.junit.Assert.fail("Expected RuntimeException was not thrown for missing CSV header");
136+
137+
} catch (RuntimeException ex) {
138+
org.junit.Assert.assertTrue("Exception message should contain the missing column name",
139+
ex.getMessage().contains("The column 'typoHere' does not exist"));
140+
141+
org.junit.Assert.assertTrue("Message should contain the available headers",
142+
ex.getMessage().contains("Available headers are: [id, name]."));
143+
}
144+
}
111145
}

core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public void willGetJsonFileIntoA_JavaString() throws Exception {
7676
@Test
7777
public void willReadAllfileNamesFrom_TestResource() {
7878
List<String> allTestCaseFiles = SmartUtils.getAllEndPointFiles("unit_test_files/engine_unit_test_jsons");
79-
assertThat(allTestCaseFiles.size(), is(23));
79+
assertThat(allTestCaseFiles.size(), is(25));
8080
assertThat(allTestCaseFiles.get(0), is("unit_test_files/engine_unit_test_jsons/00_test_json_single_step_verifications.json"));
8181
}
8282

core/src/test/java/org/jsmart/zerocode/parameterized/ParameterisedCsvDemoTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@ public void testParameterizedCsv() throws Exception {
1919
@JsonTestCase("integration_test_files/parameterized/parameterized_sample_csv_with_headers_test.json")
2020
public void testParameterizedCsvWithHeaders() throws Exception {
2121
}
22+
23+
@Test
24+
@JsonTestCase("integration_test_files/parameterized/parameterized_sample_external_csv_with_headers.json")
25+
public void testPrameterizedExternalCsvWithHeadersNoPipes() throws Exception {
26+
}
2227
}

core/src/test/resources/integration_test_files/parameterized/parameterized_sample_csv_with_headers_test.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
}
2121
],
2222
"parameterized": {
23+
"withHeaders" : true,
2324
"csvSource":[
2425
"|id|, |AddressId|, |status|",
2526
"${RANDOM.NUMBER}, ${RANDOM.NUMBER}, 200",
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"scenarioName": "Parameterized test scenario demo for external csv",
3+
"steps": [
4+
{
5+
"name": "get_user",
6+
"url": "",
7+
"operation": "",
8+
"request": {
9+
"status": "${PARAM.status}",
10+
"body": {
11+
"login": "octocat-${PARAM.id}-${PARAM.AddressId}"
12+
}
13+
},
14+
"assertions": {
15+
"status": "${$.get_user.response.status}",
16+
"body": {
17+
"login": "octocat-${PARAM.id}-${PARAM.AddressId}"
18+
}
19+
}
20+
}
21+
],
22+
"parameterized": {
23+
"withHeaders" : true,
24+
"csvSource": "integration_test_files/parameterized/params_with_headers.csv"
25+
}
26+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
id, AddressId, status
2+
12345, 67890, 200
3+
11, 22, 400

0 commit comments

Comments
 (0)