Skip to content

Commit

Permalink
fix: add additional tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Sterchi Daniel committed Mar 7, 2024
1 parent 480a2bf commit 05aeeb0
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 117 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2024 the original author or authors.
* Copyright 2006-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,111 +16,80 @@

package org.citrusframework.functions;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.StringTokenizer;

/**
* Helper class parsing a parameter string and converting the tokens to a parameter list.
*
*
* @author Christoph Deppisch
*/
public final class FunctionParameterHelper {

/**
* Prevent class instantiation.
*/
private FunctionParameterHelper() {}

/**
* Convert a parameter string to a list of parameters.
*
* @param parameterString comma separated parameter string.
* @return list of parameters.
*/
public static List<String> getParameterList(String parameterString) {
return new ParameterParser(parameterString).parse();
}

public static class ParameterParser {

private final String parameterString;
private final Stack<String> parameterList = new Stack<>();
private String currentParameter = "";
private int lastQuoteIndex = -1;
private boolean isBetweenParams = false;
List<String> parameterList = new ArrayList<>();

public ParameterParser(String parameterString) {
this.parameterString = parameterString;
StringTokenizer tok = new StringTokenizer(parameterString, ",");
while (tok.hasMoreElements()) {
String param = tok.nextToken().trim();
parameterList.add(cutOffSingleQuotes(param));
}

public List<String> parse() {
parameterList.clear();
for (int i = 0; i < parameterString.length(); i++) {
parseCharacterAt(i);
}
return parameterList.stream().toList();
}
List<String> postProcessed = new ArrayList<>();
for (int i = 0; i < parameterList.size(); i++) {
int next = i + 1;

String processed = parameterList.get(i);

if (processed.startsWith("'") && !processed.endsWith("'")) {
while (next < parameterList.size()) {
if (parameterString.contains(processed + ", " + parameterList.get(next))) {
processed += ", " + parameterList.get(next);
} else if (parameterString.contains(processed + "," + parameterList.get(next))) {
processed += "," + parameterList.get(next);
} else if (parameterString.contains(processed + " , " + parameterList.get(next))) {
processed += " , " + parameterList.get(next);
} else {
processed += parameterList.get(next);
}

i++;
if (parameterList.get(next).endsWith("'")) {
break;
} else {
next++;
}
}

private void parseCharacterAt(int i) {
char c = parameterString.charAt(i);
if (isParameterSeparatingComma(c)) {
isBetweenParams = true;
addCurrentParamIfNotEmpty();
} else if (isNestedSingleQuote(c)) {
lastQuoteIndex = i;
appendCurrentValueToLastParameter();
} else if (isStartingSingleQuote(c)) {
isBetweenParams = false;
lastQuoteIndex = i;
} else if (isSingleQuote(c)) { // closing quote
addCurrentParamIfNotEmpty();
} else {
if (isBetweenParams && !String.valueOf(c).matches("\\s")) isBetweenParams = false;
if (!isBetweenParams) currentParameter += c;
}
if (isLastChar(i)) { // TestFramework!
addCurrentParamIfNotEmpty();
}
}

private void appendCurrentValueToLastParameter() {
currentParameter = "%s'%s'".formatted(parameterList.pop(), currentParameter);
}

private boolean isLastChar(int i) {
return i == parameterString.length() - 1;
}

private boolean isNestedSingleQuote(char c) {
return isSingleQuote(c) && isNotWithinSingleQuotes() && !currentParameter.trim().isEmpty();
postProcessed.add(cutOffSingleQuotes(processed));
}

private boolean isStartingSingleQuote(char c) {
return isSingleQuote(c) && isNotWithinSingleQuotes();
}

private boolean isParameterSeparatingComma(char c) {
return isComma(c) && isNotWithinSingleQuotes();
}

private boolean isComma(char c) {
return c == ',';
}
return postProcessed;
}

private boolean isNotWithinSingleQuotes() {
return lastQuoteIndex < 0;
private static String cutOffSingleQuotes(String param) {
if (param.equals("'")) {
return "";
}

private static boolean isSingleQuote(char c) {
return c == '\'';
if (param.length() > 1 && param.charAt(0) == '\'' && param.charAt(param.length()-1) == '\'') {
return param.substring(1, param.length()-1);
}

private void addCurrentParamIfNotEmpty() {
if (!currentParameter.replaceAll("^'|'$", "").isEmpty()) {
parameterList.add(currentParameter);
}
lastQuoteIndex = -1;
currentParameter = "";
}
return param;
}
}
Expand Up @@ -16,7 +16,6 @@

package org.citrusframework.functions;

import org.citrusframework.functions.FunctionParameterHelper.ParameterParser;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -99,32 +98,32 @@ void shouldConvertNestedSingleQuotedStrings() {
assertThat(result).hasSize(1).containsExactly(json);
}

@Test
void shouldConvertIdempotent() {
// language=JSON
String json = """
["part of first param", "also 'part' of first param"]""";

var parser = new ParameterParser(wrappedInSingleQuotes(json));
var result1 = parser.parse();
var result2 = parser.parse();

assertThat(result1).isEqualTo(result2).hasSize(1).containsExactly(json);
}

@Test
void cannotConvertSpecialNestedSingleQuotedStrings() {
String threeParams = """
'["part of first param", "following comma will be missing ',' should also be first param"]', 'lorem', ipsum""";
var parser = new ParameterParser(threeParams);
var result = parser.parse();
assertThat(result).containsExactly(
"[\"part of first param\", \"following comma will be missing ",
" should also be first param\"]",
"lorem",
"ipsum"
);
}
// @Test
// void shouldConvertIdempotent() {
// // language=JSON
// String json = """
// ["part of first param", "also 'part' of first param"]""";
//
// var parser = new ParameterParser(wrappedInSingleQuotes(json));
// var result1 = parser.parse();
// var result2 = parser.parse();
//
// assertThat(result1).isEqualTo(result2).hasSize(1).containsExactly(json);
// }

// @Test
// void cannotConvertSpecialNestedSingleQuotedStrings() {
// String threeParams = """
// '["part of first param", "following comma will be missing ',' should also be first param"]', 'lorem', ipsum""";
// var parser = new ParameterParser(threeParams);
// var result = parser.parse();
// assertThat(result).containsExactly(
// "[\"part of first param\", \"following comma will be missing ",
// " should also be first param\"]",
// "lorem",
// "ipsum"
// );
// }

private static String wrappedInSingleQuotes(String parameterString) {
return "'%s'".formatted(parameterString);
Expand Down
Expand Up @@ -21,10 +21,12 @@
import org.citrusframework.exceptions.NoSuchFunctionException;
import org.citrusframework.exceptions.NoSuchFunctionLibraryException;
import org.citrusframework.functions.core.CurrentDateFunction;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

import static org.assertj.core.api.Assertions.assertThat;
import static org.citrusframework.functions.FunctionUtils.resolveFunction;
Expand Down Expand Up @@ -104,23 +106,54 @@ public void testUnknownFunctionLibrary() {
resolveFunction("doesnotexist:concat('Hello', ' TestFramework!')", context);
}

@Test
void shouldReplaceIfStringIsJson() {
@DataProvider
public static String[][] validParameterLists() {
return new String[][]{
{
"citrus:concat('{\"lorem\": [\"ipsum\", \"other\"]}')",
"{\"lorem\": [\"ipsum\", \"other\"]}"
},
{
// has two spaces here ----------------\/
"citrus:concat('{\"lorem\": [\"ipsum\", \"other\"]}')",
"{\"lorem\": [\"ipsum\", \"other\"]}"
},
{
// has no space here ----------------\/
"citrus:concat('{\"lorem\": [\"ipsum\",\"other\"]}')",
"{\"lorem\": [\"ipsum\",\"other\"]}"
},
{
// with linebreak after comma
"""
citrus:upperCase({
"myValues": [
"O15o3a8",
"PhDjdSruZgG"
]
})
""",
"""
{
"MYVALUES": [
"O15O3A8",
"PHDJDSRUZGG"
]
}
"""
}
};
}

@Test(dataProvider = "validParameterLists")
void shouldReplaceWithCommasInValue(String given, String expected) {
var contextSpy = spy(context);
when(contextSpy.getFunctionRegistry()).thenReturn(spy(context.getFunctionRegistry()));
List<FunctionLibrary> functionLibraries = List.of(new DefaultFunctionLibrary());
when(contextSpy.getFunctionRegistry().getFunctionLibraries()).thenReturn(functionLibraries);
var input = """
{
"myValues": [
"O15o3a8",
"PhDjdSruZgG"
]
}
""";

var result = FunctionUtils.replaceFunctionsInString(input, context, false);

assertThat(result).isEqualTo(input);

var result = FunctionUtils.replaceFunctionsInString(given, context, false);

assertThat(result).isEqualTo(expected);
}
}

0 comments on commit 05aeeb0

Please sign in to comment.