diff --git a/.gitignore b/.gitignore
index 1af60c9d4..db42564bb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,4 +12,6 @@ apikit-tools/apikit-studio-plugin/org.mule.tooling.apikit.deps/lib/
apikit-tools/apikit-studio-plugin/org.mule.tooling.apikit/examples/
build.log
.mule
+.codegenie
+.vscode/
mule-apikit-module/logs/
diff --git a/build.yaml b/build.yaml
index 2968666af..c47e10a8c 100644
--- a/build.yaml
+++ b/build.yaml
@@ -2,6 +2,15 @@ enableAllureTestReportStage: false
mavenSettingsXmlId: ab7820eb-e393-4e88-9962-92104044ed75
projectType: extension
additionalTestConfigs:
- testsJava17:
+ jdk17-4.9.x:
testJdkTool: OPEN-JDK17
- mavenAdditionalArgs: -DruntimeProduct=MULE_EE -DruntimeVersion=4.6.0
+ mavenAdditionalArgs: -DruntimeVersion=4.9.7-rc2 -DruntimeProduct=MULE_EE
+ jdk17-4.8.x:
+ testJdkTool: OPEN-JDK17
+ mavenAdditionalArgs: -DruntimeVersion=4.8.5 -DruntimeProduct=MULE_EE
+ jdk17-4.7.x:
+ testJdkTool: OPEN-JDK17
+ mavenAdditionalArgs: -DruntimeVersion=4.7.4 -DruntimeProduct=MULE_EE
+ jdk17-4.6.x:
+ testJdkTool: OPEN-JDK17
+ mavenAdditionalArgs: -DruntimeVersion=4.6.16 -DruntimeProduct=MULE_EE
diff --git a/pom.xml b/pom.xml
index ee631e62f..bcb724078 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
crafted
1.1.5
- 1.9.0
+ 1.10.3
1.1.6
2.1.8
@@ -35,9 +35,10 @@
2.15.1
4.4
2.17.1
+ 0.7.5
- 2.7.9
+ 3.0.0-SNAPSHOT
6.6.73
@@ -45,9 +46,9 @@
src/test/munit
${basedir}/target/test-mule/munit
- 1.2.0
- 3.1.0
- 3.1.0
+ 1.5.0
+ 3.4.0
+ 3.3.2
3.0.2
5.8.0
0.8.10
@@ -116,6 +117,12 @@
${mule.version}
provided
+
+ org.mule.sdk
+ mule-sdk-api
+ ${sdk-api.version}
+ provided
+
org.mule.apikit
diff --git a/src/main/java/org/mule/module/apikit/api/deserializing/ArrayHeaderDelimiter.java b/src/main/java/org/mule/module/apikit/api/deserializing/ArrayHeaderDelimiter.java
index 402863ab4..36295841e 100644
--- a/src/main/java/org/mule/module/apikit/api/deserializing/ArrayHeaderDelimiter.java
+++ b/src/main/java/org/mule/module/apikit/api/deserializing/ArrayHeaderDelimiter.java
@@ -11,7 +11,8 @@
*/
public enum ArrayHeaderDelimiter {
- COMMA(',');
+ COMMA(','),
+ SEMICOLON(';');
private final char delimiter;
diff --git a/src/main/java/org/mule/module/apikit/api/uri/URIResolver.java b/src/main/java/org/mule/module/apikit/api/uri/URIResolver.java
index a8bf10fb5..178d4a8ec 100644
--- a/src/main/java/org/mule/module/apikit/api/uri/URIResolver.java
+++ b/src/main/java/org/mule/module/apikit/api/uri/URIResolver.java
@@ -225,13 +225,24 @@ private URIPattern findBest(Set patterns) {
}
URIPattern best = null;
for (URIPattern p : patterns) {
- if (p.match(this._uri) && (!this._uri.endsWith("/") || p.toString().endsWith("/"))) {
+ if (p.match(this._uri) && (!this._uri.endsWith("/") || p.toString().endsWith("/")))
if (best == null || p.score() > best.score()) {
best = p;
}
- }
+ }
+ if (best == null) {
+ best = findBestMethodWithEmptyParams(patterns);
}
return best;
}
+ private URIPattern findBestMethodWithEmptyParams(Set patterns) {
+ URIPattern currentBest = null;
+ for (URIPattern p : patterns) {
+ if (p.match(this._uri) && (currentBest == null || p.score() > currentBest.score())) {
+ currentBest = p;
+ }
+ }
+ return currentBest;
+ }
}
diff --git a/src/main/java/org/mule/module/apikit/deserializing/MimeType.java b/src/main/java/org/mule/module/apikit/deserializing/MimeType.java
index baa4e1ccd..12e70f5c8 100644
--- a/src/main/java/org/mule/module/apikit/deserializing/MimeType.java
+++ b/src/main/java/org/mule/module/apikit/deserializing/MimeType.java
@@ -12,6 +12,8 @@
import java.util.stream.Collectors;
import java.util.stream.IntStream;
+import static org.mule.module.apikit.api.deserializing.ArrayHeaderDelimiter.SEMICOLON;
+
public class MimeType {
private String type;
@@ -228,9 +230,13 @@ private String nextQuotedString() throws MimeTypeParseException {
private boolean trySkip(char expected) {
if (atEnd() || peek() != expected) {
return false;
+ } else if ((peek() == SEMICOLON.getDelimiterChar() && i + 1 == input.length())) {
+ i++;
+ return false;
+ } else {
+ skip();
+ return true;
}
- skip();
- return true;
}
private void skip(char expected) throws MimeTypeParseException {
diff --git a/src/main/java/org/mule/module/apikit/input/stream/RewindableInputStream.java b/src/main/java/org/mule/module/apikit/input/stream/RewindableInputStream.java
index fa44edac9..15fdbe37f 100644
--- a/src/main/java/org/mule/module/apikit/input/stream/RewindableInputStream.java
+++ b/src/main/java/org/mule/module/apikit/input/stream/RewindableInputStream.java
@@ -63,6 +63,7 @@ public RewindableInputStream(InputStream in) {
this.in = in;
}
+ @Override
public void close() throws IOException {
if (saving) {
curBlockAvail = 0;
diff --git a/src/main/java/org/mule/module/apikit/validation/body/form/transformation/MultipartBuilder.java b/src/main/java/org/mule/module/apikit/validation/body/form/transformation/MultipartBuilder.java
index 254ac91dd..c2689d1f9 100644
--- a/src/main/java/org/mule/module/apikit/validation/body/form/transformation/MultipartBuilder.java
+++ b/src/main/java/org/mule/module/apikit/validation/body/form/transformation/MultipartBuilder.java
@@ -17,6 +17,7 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Optional;
import java.util.OptionalLong;
import java.util.Set;
import java.util.regex.Matcher;
@@ -91,7 +92,7 @@ public Multipart build() throws InvalidFormParameterException {
APIKitMultipartStream multipartStream =
new APIKitMultipartStream(inputStream, boundary.getBytes(MIME.UTF8_CHARSET), BUFFER_SIZE, sizeLimit);
- Set parametersInPayload = new HashSet<>();
+ Map parametersInPayloadToCount = new HashMap<>();
MultipartEntityBuilder multipartEntityBuilder =
defaultValues.isEmpty() && cursorProvider != null
? new MultipartEntityBuilderWithoutDefaults(contentType, cursorProvider, boundary, sizeLimit, byteLength)
@@ -106,16 +107,16 @@ public Multipart build() throws InvalidFormParameterException {
String fileName = getFileName(headers);
String contentType = getContentType(headers);
- parametersInPayload.add(name);
+ parametersInPayloadToCount.put(name, parametersInPayloadToCount.getOrDefault(name, 0) + 1);
multipartEntityBuilder.handlePart(multipartStream, formParameters.get(name), name, contentType, fileName, headers);
- nextPart = multipartStream.readBoundary();
+ nextPart = multipartStream.readBoundary(); //Checking the next part items here
multipartEntityBuilder.handleBoundary(false);
}
for (Entry defaultValue : defaultValues.entrySet()) {
- if (!parametersInPayload.contains(defaultValue.getKey())) {
+ if (!parametersInPayloadToCount.containsKey(defaultValue.getKey())) {
multipartEntityBuilder.addDefault(defaultValue.getKey(), defaultValue.getValue());
multipartEntityBuilder.handleBoundary(false);
}
@@ -125,9 +126,18 @@ public Multipart build() throws InvalidFormParameterException {
multipartStream.readEpilogue(multipartEntityBuilder);
for (Entry formParameter : formParameters.entrySet()) {
- if (!parametersInPayload.contains(formParameter.getKey()) && formParameter.getValue().isRequired()
+ if (!parametersInPayloadToCount.containsKey(formParameter.getKey()) && formParameter.getValue().isRequired()
&& formParameter.getValue().getDefaultValues().isEmpty()) {
- throw new InvalidFormParameterException("Required form parameter " + formParameter.getKey() + " not specified");
+ throw new InvalidFormParameterException("Required form parameter " + formParameter.getKey() + " not specified");//We can also validate the minItems and maxItem count here
+ } else if (parametersInPayloadToCount.containsKey(formParameter.getKey()) && formParameter.getValue().isRequired()) {
+ Optional minItemsCount = formParameter.getValue().getMinItems();
+ if (minItemsCount.isPresent() && (minItemsCount.get() > parametersInPayloadToCount.get(formParameter.getKey()))) {
+ throw new InvalidFormParameterException("parameter does not comply with minItems for " + formParameter.getKey());
+ }
+ Optional maxItemsCount = formParameter.getValue().getMaxItems();
+ if (maxItemsCount.isPresent() && parametersInPayloadToCount.get(formParameter.getKey()) > maxItemsCount.get()) {
+ throw new InvalidFormParameterException("parameter does not comply with maxItems for " + formParameter.getKey());
+ }
}
}
diff --git a/src/test/java/org/mule/module/apikit/ConfigurationTestCase.java b/src/test/java/org/mule/module/apikit/ConfigurationTestCase.java
index f305d9569..7f4c94f81 100644
--- a/src/test/java/org/mule/module/apikit/ConfigurationTestCase.java
+++ b/src/test/java/org/mule/module/apikit/ConfigurationTestCase.java
@@ -10,7 +10,9 @@
import static org.hamcrest.core.IsEqual.equalTo;
import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
import org.mule.apikit.ApiType;
+import org.mule.module.apikit.exception.NotAcceptableException;
public class ConfigurationTestCase {
@@ -20,4 +22,10 @@ public void avoidNullPointerWhenConfigNotInitialised() {
assertThat(configuration.getType(), equalTo(ApiType.AMF));
}
+
+ @Test
+ public void testNotAcceptableException() {
+ NotAcceptableException notAcceptableException = new NotAcceptableException();
+ Assertions.assertEquals(notAcceptableException.getStringRepresentation(), NotAcceptableException.STRING_REPRESENTATION);
+ }
}
diff --git a/src/test/java/org/mule/module/apikit/RoutingTableTestCase.java b/src/test/java/org/mule/module/apikit/RoutingTableTestCase.java
index b4922cf7a..02149f18f 100644
--- a/src/test/java/org/mule/module/apikit/RoutingTableTestCase.java
+++ b/src/test/java/org/mule/module/apikit/RoutingTableTestCase.java
@@ -102,14 +102,14 @@ public void URIWithTrailingForwardSlashAreNotMatched() {
patterns.add(new URIPattern("/api/"));
URIResolver resolver1 = new URIResolver("/api/hello/");
- Assert.assertNull(resolver1.find(patterns, URIResolver.MatchRule.BEST_MATCH));
+ Assert.assertNotNull(resolver1.find(patterns, URIResolver.MatchRule.BEST_MATCH));
URIResolver resolver3 = new URIResolver("/api");
Assert.assertNull(resolver3.find(patterns, URIResolver.MatchRule.BEST_MATCH));
patterns.add(new URIPattern("/{param}"));
URIResolver resolver2 = new URIResolver("/");
- Assert.assertNull(resolver2.find(patterns, URIResolver.MatchRule.BEST_MATCH));
+ Assert.assertNotNull(resolver2.find(patterns, URIResolver.MatchRule.BEST_MATCH));
}
@Test
@@ -127,4 +127,57 @@ public void getResourceByString() {
Assert.assertNotNull(routingTable.getResource("/single-resource"));
Assert.assertNotNull(routingTable.getResource("/api/sub-resource"));
}
+
+ @Test
+ public void testFindBestMethodWithEmptyParams() {
+ // Test Case 1: Empty parameter in the last
+ HashSet patterns1 = new HashSet<>();
+ patterns1.add(new URIPattern("/api/users/{id}"));
+
+ URIResolver resolver1 = new URIResolver("/api/users/");
+ URIPattern bestPattern1 = resolver1.find(patterns1, URIResolver.MatchRule.BEST_MATCH);
+ Assert.assertNotNull(bestPattern1);
+ Assert.assertEquals("/api/users/{id}", bestPattern1.toString());
+
+
+ // Test Case 2: Testing empty parameter in middle
+ HashSet patterns2 = new HashSet<>();
+ patterns2.add(new URIPattern("/users/{id}/posts"));
+ patterns2.add(new URIPattern("/users/{id}"));
+ patterns2.add(new URIPattern("/users"));
+
+ URIResolver resolver2 = new URIResolver("/users//posts");
+ URIPattern bestPattern2 = resolver2.find(patterns2, URIResolver.MatchRule.BEST_MATCH);
+ Assert.assertEquals("/users/{id}/posts", bestPattern2.toString());
+
+ // Test Case 3: Testing multiple empty parameters
+ HashSet patterns3 = new HashSet<>();
+ patterns3.add(new URIPattern("/api/{version}/users/{id}"));
+ patterns3.add(new URIPattern("/users/{id}"));
+ patterns3.add(new URIPattern("/users"));
+
+ URIResolver resolver3 = new URIResolver("/api//users/");
+ URIPattern bestPattern3 = resolver3.find(patterns3, URIResolver.MatchRule.BEST_MATCH);
+ Assert.assertEquals("/api/{version}/users/{id}", bestPattern3.toString());
+
+ // Test Case 4: Testing empty parameter with nested resources
+ HashSet patterns4 = new HashSet<>();
+ patterns4.add(new URIPattern("/users/{id}/posts/{postId}/comments"));
+ patterns4.add(new URIPattern("/users/{id}/posts/{postId}"));
+ patterns4.add(new URIPattern("/users/{id}/posts"));
+
+ URIResolver resolver4 = new URIResolver("/users//posts//comments");
+ URIPattern bestPattern4 = resolver4.find(patterns4, URIResolver.MatchRule.BEST_MATCH);
+ Assert.assertEquals("/users/{id}/posts/{postId}/comments", bestPattern4.toString());
+
+ // Test Case 5: Testing empty parameter with root path
+ HashSet patterns5 = new HashSet<>();
+ patterns5.add(new URIPattern("/{version}"));
+ patterns5.add(new URIPattern("/"));
+ patterns5.add(new URIPattern(""));
+
+ URIResolver resolver12 = new URIResolver("//");
+ URIPattern bestPattern12 = resolver12.find(patterns5, URIResolver.MatchRule.BEST_MATCH);
+ Assert.assertNull(bestPattern12);
+ }
}
diff --git a/src/test/java/org/mule/module/apikit/deserializing/MimeTypeTest.java b/src/test/java/org/mule/module/apikit/deserializing/MimeTypeTest.java
index 052cada5e..717e252c7 100644
--- a/src/test/java/org/mule/module/apikit/deserializing/MimeTypeTest.java
+++ b/src/test/java/org/mule/module/apikit/deserializing/MimeTypeTest.java
@@ -71,6 +71,14 @@ public void supportsRepeatedParameters() throws MimeTypeParseException {
assertEquals(expected, MimeType.from("text/plain; param=value1; param=value2"));
}
+ @Test
+ public void supportsEndSemiColon() throws MimeTypeParseException {
+ MimeType expected = new MimeType("text", "plain",
+ list(new Parameter("param", "value1"),
+ new Parameter("param", "value2")));
+ assertEquals(expected, MimeType.from("text/plain; param=value1; param=value2;"));
+ }
+
@Test
public void ignoresEscapedQuotesInsideQuotes() throws MimeTypeParseException {
MimeType expected = new MimeType("text", "plain",
@@ -91,10 +99,8 @@ public void failsIfAdditionalTextIsAtTheEndOfAValidMime() {
@Test
public void failsIfTextIsMissing() {
assertThrows(MimeTypeParseException.class, () -> MimeType.from("text/"));
- assertThrows(MimeTypeParseException.class, () -> MimeType.from("text/plain;"));
assertThrows(MimeTypeParseException.class, () -> MimeType.from("text/plain; param"));
assertThrows(MimeTypeParseException.class, () -> MimeType.from("text/plain; param="));
- assertThrows(MimeTypeParseException.class, () -> MimeType.from("text/plain; param=value;"));
}
@Test
diff --git a/src/test/java/org/mule/module/apikit/error/EventProcessingExceptionHandlerTest.java b/src/test/java/org/mule/module/apikit/error/EventProcessingExceptionHandlerTest.java
new file mode 100644
index 000000000..3911a1759
--- /dev/null
+++ b/src/test/java/org/mule/module/apikit/error/EventProcessingExceptionHandlerTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.module.apikit.error;
+
+import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.mule.runtime.api.event.Event;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+
+public class EventProcessingExceptionHandlerTest {
+
+ @Test
+ public void testHandle() throws Exception {
+ MuleMessagingExceptionHandler handler = new MuleMessagingExceptionHandler();
+ Event event = mock(Event.class);
+ Exception exception = mock(Exception.class);
+ Assertions.assertThrows(IllegalArgumentException.class, () -> handler.handle(event, exception));
+ }
+
+ @Test
+ public void testGetMessagingExceptionConstructor() throws Exception {
+ MuleMessagingExceptionHandler handler = new MuleMessagingExceptionHandler();
+ assertNotNull(handler);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/mule/module/apikit/input/stream/RewindableInputStreamTest.java b/src/test/java/org/mule/module/apikit/input/stream/RewindableInputStreamTest.java
new file mode 100644
index 000000000..21a3b9397
--- /dev/null
+++ b/src/test/java/org/mule/module/apikit/input/stream/RewindableInputStreamTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.module.apikit.input.stream;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mule.module.apikit.validation.body.form.MultipartFormValidatorTest;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.jupiter.api.Assertions.*;
+
+class RewindableInputStreamTest {
+
+ @Test
+ void testClose() throws IOException {
+ RewindableInputStream rewindableInputStream = new RewindableInputStream(new ByteArrayInputStream(MultipartFormValidatorTest.MULTIPART_BODY.getBytes()));
+ int i = rewindableInputStream.available();
+ Assertions.assertTrue(i > 0);
+ Assertions.assertTrue(rewindableInputStream.canRewind());
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ byte[] data = new byte[1024];
+ int nRead;
+ while ((nRead = rewindableInputStream.read(data, 0, data.length)) != -1) {
+ buffer.write(data, 0, nRead);
+ }
+ Assertions.assertTrue(rewindableInputStream.canRewind());
+ rewindableInputStream.rewind();
+ rewindableInputStream.willNotRewind();
+ Assertions.assertThrows(IllegalStateException.class, rewindableInputStream::rewind);
+ rewindableInputStream.close();
+ }
+
+ @Test
+ void testRewind() throws IOException {
+ RewindableInputStream rewindableInputStream = new RewindableInputStream(new ByteArrayInputStream(MultipartFormValidatorTest.MULTIPART_BODY.getBytes()));
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ int nRead;
+ byte[] data = new byte[1024];
+ RewindableInputStream.Block block = new RewindableInputStream.Block();
+ block.append(data[0]);
+ try {
+ while ((nRead = rewindableInputStream.read()) != -1) {
+ buffer.write(data, 0, nRead);
+ }
+ buffer.flush();
+ byte[] byteArray = buffer.toByteArray();
+ assertFalse(new String(byteArray, UTF_8).isEmpty());
+ rewindableInputStream.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ void testRewindableStreamFailure() {
+ assertThrows(NullPointerException.class, () -> {
+ RewindableInputStream rewindableInputStream = new RewindableInputStream(null);
+ });
+ }
+
+ @Test
+ public void testCloseSucces() throws IOException, NoSuchFieldException, IllegalAccessException {
+ // Create a test input stream
+ InputStream testInputStream = new InputStream() {
+ @Override
+ public int read() throws IOException {
+ return 0;
+ }
+ };
+
+ // Create a RewindableInputStream based on the test input stream
+ RewindableInputStream rewindableInputStream = new RewindableInputStream(testInputStream);
+
+ // Close the RewindableInputStream
+ rewindableInputStream.close();
+ Field eofField = RewindableInputStream.class.getDeclaredField("eof");
+ eofField.setAccessible(true);
+ boolean eof = (boolean) eofField.get(rewindableInputStream);
+
+ // Assert that the input stream is closed
+ assertFalse(eof);
+ }
+
+ @Test
+ public void testRewindSuccess() throws IOException {
+ // Create a test input stream
+ InputStream testInputStream = new InputStream() {
+ private int count = 0;
+ private final int[] data = {1, 2, 3};
+
+ @Override
+ public int read() throws IOException {
+ if (count >= data.length) {
+ return -1;
+ }
+ return data[count++];
+ }
+ };
+
+ // Create a RewindableInputStream based on the test input stream
+ RewindableInputStream rewindableInputStream = new RewindableInputStream(testInputStream);
+
+ // Read the first byte
+ rewindableInputStream.read();
+
+ // Rewind the input stream
+ rewindableInputStream.rewind();
+
+ // Assert that the first byte can be read again
+ assertEquals(1, rewindableInputStream.read());
+ }
+
+ @Test
+ public void testWillNotRewind() {
+ // Create a test input stream
+ InputStream testInputStream = new InputStream() {
+ @Override
+ public int read() throws IOException {
+ return -1;
+ }
+ };
+
+ // Create a RewindableInputStream based on the test input stream
+ RewindableInputStream rewindableInputStream = new RewindableInputStream(testInputStream);
+
+ // Call willNotRewind()
+ rewindableInputStream.willNotRewind();
+
+ // Assert that canRewind() returns false
+ assertFalse(rewindableInputStream.canRewind());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/mule/module/apikit/uri/TokenBaseDummy.java b/src/test/java/org/mule/module/apikit/uri/TokenBaseDummy.java
new file mode 100644
index 000000000..7ac75d12f
--- /dev/null
+++ b/src/test/java/org/mule/module/apikit/uri/TokenBaseDummy.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.module.apikit.uri;
+
+import java.util.Map;
+
+class TokenBaseDummy extends TokenBase{
+
+ /**
+ * Creates a new expansion token.
+ *
+ * @param exp The expression corresponding to this URI token.
+ * @throws NullPointerException If the specified expression is null.
+ */
+ public TokenBaseDummy(String exp) throws NullPointerException {
+ super(exp);
+ }
+
+ @Override
+ public boolean resolve(String expanded, Map values) {
+ return false;
+ }
+
+ public static String forTest(String exp) {
+ return TokenBase.strip(exp);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/mule/module/apikit/uri/TokenBaseDummyTest.java b/src/test/java/org/mule/module/apikit/uri/TokenBaseDummyTest.java
new file mode 100644
index 000000000..1ec5f3861
--- /dev/null
+++ b/src/test/java/org/mule/module/apikit/uri/TokenBaseDummyTest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.module.apikit.uri;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class TokenBaseDummyTest {
+
+ @Test
+ void testCreateTokenBaseFailure() {
+ Assertions.assertThrows(NullPointerException.class, () -> new TokenBaseDummy(null));
+ TokenBaseDummy tokenBaseDummyA = new TokenBaseDummy("testA");
+ TokenBaseDummy tokenBaseDummyB = new TokenBaseDummy("testB");
+ assertFalse(tokenBaseDummyA.isResolvable());
+ assertNotEquals(tokenBaseDummyA, tokenBaseDummyB);
+ TokenBaseDummy.forTest("{test}");
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/mule/module/apikit/uri/URICoderTest.java b/src/test/java/org/mule/module/apikit/uri/URICoderTest.java
new file mode 100644
index 000000000..cdca903df
--- /dev/null
+++ b/src/test/java/org/mule/module/apikit/uri/URICoderTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.module.apikit.uri;
+
+import org.junit.jupiter.api.Test;
+import org.mule.module.apikit.api.UrlUtils;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class URICoderTest {
+
+ private Set ESCAPE_CHARS = new HashSet<>(Arrays.asList('/', '{', '}'));
+
+ @Test
+ void testEncode() {
+ String input = "https://example.com/path?query=value";
+ String expectedOutput = "https%3A//example.com/path%3Fquery%3Dvalue";
+ assertEquals(expectedOutput, URICoder.encode(input, ESCAPE_CHARS));
+ }
+
+ @Test
+ void testDecode() {
+ String input = "https%3A%2F%2Fexample.com%2Fpath%3Fquery%3Dvalue";
+ String expectedOutput = "https://example.com/path?query=value";
+ assertEquals(expectedOutput, URICoder.decode(input));
+ }
+
+ @Test
+ void testEncodeNull() {
+ assertEquals("", URICoder.encode("", ESCAPE_CHARS));
+ }
+
+ @Test
+ void testDecodeNull() {
+ assertEquals("", URICoder.decode(""));
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/mule/module/apikit/uri/URITemplateSyntaxExceptionTest.java b/src/test/java/org/mule/module/apikit/uri/URITemplateSyntaxExceptionTest.java
new file mode 100644
index 000000000..dbb321526
--- /dev/null
+++ b/src/test/java/org/mule/module/apikit/uri/URITemplateSyntaxExceptionTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.module.apikit.uri;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class URITemplateSyntaxExceptionTest {
+
+ @Test
+ public void testConstructor() {
+ String input = "testInput";
+ String reason = "testReason";
+ URITemplateSyntaxException exception = new URITemplateSyntaxException(input, reason);
+ assertEquals("testReason : testInput", exception.getMessage());
+ assertEquals(input, exception.getInput());
+ assertEquals(reason, exception.getReason());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testConstructorWithNullInput() {
+ String input = null;
+ String reason = "testReason";
+ URITemplateSyntaxException exception = new URITemplateSyntaxException(input, reason);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testConstructorWithNullReason() {
+ String input = "testInput";
+ String reason = null;
+ URITemplateSyntaxException exception = new URITemplateSyntaxException(input, reason);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/mule/module/apikit/uri/URITemplateTest.java b/src/test/java/org/mule/module/apikit/uri/URITemplateTest.java
new file mode 100644
index 000000000..ae3d2354c
--- /dev/null
+++ b/src/test/java/org/mule/module/apikit/uri/URITemplateTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.module.apikit.uri;
+
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class URITemplateTest {
+
+
+ @Test
+ public void testConstructor() {
+ // Test with a valid template
+ try {
+ new URITemplate("/users/{userId}");
+ } catch (Exception e) {
+ fail("Failed to create a valid URI template");
+ }
+
+ // Test with a null template
+ try {
+ new URITemplate(null);
+ fail("Expected a NullPointerException for a null template");
+ } catch (NullPointerException e) {
+ // Expected exception
+ }
+ }
+
+ @Test
+ public void testDigest() {
+ // Test with a valid template
+ try {
+ List tokens = URITemplate.digest("/users/{userId}");
+ assertNotNull("Token list should not be null", tokens);
+ assertEquals("Token list should have 2 elements", 2, tokens.size());
+ assertEquals("First token should be a literal token", "/users/", tokens.get(0).toString());
+ assertEquals("Second token should be a variable token", "{userId}", tokens.get(1).toString());
+ } catch (URITemplateSyntaxException e) {
+ fail("Failed to digest a valid URI template");
+ }
+ }
+
+ @Test
+ public void testEquals() {
+ URITemplate template1 = new URITemplate("/users/{userId}");
+ URITemplate template2 = new URITemplate("/users/{userId}");
+ URITemplate template3 = new URITemplate("/users/{username}");
+
+ assertEquals("Template 1 should equal itself", template1, template1);
+ assertEquals("Template 1 should equal template 2", template1, template2);
+ assertNotEquals("Template 1 should not equal template 3", template1, template3);
+ assertNotEquals("Template 1 should not equal null", null, template1);
+ }
+
+ @Test
+ public void testHashCode() {
+ URITemplate template = new URITemplate("/users/{userId}");
+ assertEquals("Hash code should match", 127 * "/users/{userId}".hashCode() + "/users/{userId}".hashCode(), template.hashCode());
+ }
+
+ @Test
+ public void testToString() {
+ URITemplate template = new URITemplate("/users/{userId}");
+ assertEquals("String representation should match", "/users/{userId}", template.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/mule/module/apikit/uri/VariableTest.java b/src/test/java/org/mule/module/apikit/uri/VariableTest.java
new file mode 100644
index 000000000..424e548e4
--- /dev/null
+++ b/src/test/java/org/mule/module/apikit/uri/VariableTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.module.apikit.uri;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class VariableTest {
+
+
+ @Test
+ public void testVariable() {
+ Variable variable = new Variable("foo", "bar");
+ assertEquals("foo", variable.name());
+ assertEquals("bar", variable.defaultValue());
+ assertNull(variable.type());
+
+ variable = new Variable(Variable.Reserved.WILDCARD);
+ assertEquals("*", variable.name());
+ assertEquals("", variable.defaultValue());
+ assertNull(variable.type());
+ }
+
+ @Test
+ public void testForm() {
+ assertEquals(Variable.Form.STRING, Variable.Form.getType(""));
+ assertEquals(Variable.Form.LIST, Variable.Form.getType("@"));
+ assertEquals(Variable.Form.MAP, Variable.Form.getType("%"));
+ }
+
+ @Test
+ public void testIsValidName() {
+ assertTrue(Variable.isValidName("foo"));
+ assertTrue(Variable.isValidName("foo.bar"));
+ assertTrue(Variable.isValidName("foo_1"));
+ assertTrue(Variable.isValidName("foo-bar"));
+ assertFalse(Variable.isValidName("foo*"));
+ assertFalse(Variable.isValidName(""));
+ assertFalse(Variable.isValidName(null));
+ }
+
+ @Test
+ public void testIsValidValue() {
+ assertTrue(Variable.isValidValue("foo"));
+ assertTrue(Variable.isValidValue("foo.bar"));
+ assertTrue(Variable.isValidValue("foo_1"));
+ assertTrue(Variable.isValidValue("foo-bar"));
+ assertTrue(Variable.isValidValue("~%"));
+ assertFalse(Variable.isValidValue("foo*"));
+ //assertFalse(Variable.isValidValue(""));
+ assertFalse(Variable.isValidValue(null));
+ }
+
+
+}
+
diff --git a/src/test/java/org/mule/module/apikit/uri/VariableTypeTest.java b/src/test/java/org/mule/module/apikit/uri/VariableTypeTest.java
new file mode 100644
index 000000000..c80cbb605
--- /dev/null
+++ b/src/test/java/org/mule/module/apikit/uri/VariableTypeTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.module.apikit.uri;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.mule.module.apikit.uri.VariableType;
+
+public class VariableTypeTest {
+
+ @Test
+ public void testVariableTypeConstructor() {
+ String name = "testName";
+ VariableType variableType = new VariableType(name);
+ assertEquals(name, variableType.getName());
+ }
+
+ @Test
+ public void testGetName() {
+ String name = "testName";
+ VariableType variableType = new VariableType(name);
+ assertEquals(name, variableType.getName());
+ }
+
+ @Test
+ public void testEquals() {
+ VariableType variableType1 = new VariableType("testName");
+ VariableType variableType2 = new VariableType("testName");
+ assertEquals(variableType1, variableType2);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/mule/module/apikit/validation/body/form/MultiPartFormDataArrayShapeTest.java b/src/test/java/org/mule/module/apikit/validation/body/form/MultiPartFormDataArrayShapeTest.java
new file mode 100644
index 000000000..f28c4787a
--- /dev/null
+++ b/src/test/java/org/mule/module/apikit/validation/body/form/MultiPartFormDataArrayShapeTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.module.apikit.validation.body.form;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mule.module.apikit.api.exception.InvalidFormParameterException;
+import org.mule.module.apikit.api.exception.MuleRestException;
+import org.mule.module.apikit.validation.TestRestRequestValidator;
+import org.mule.parser.service.ParserMode;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.OptionalLong;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+@RunWith(Parameterized.class)
+public class MultiPartFormDataArrayShapeTest extends AbstractMultipartRequestValidatorTestCase {
+
+ @Parameterized.Parameter
+ public ParserMode parser;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection