Skip to content

Commit

Permalink
feat(#1175) core citrus changes
Browse files Browse the repository at this point in the history
* test: disable tests when network is unreachable

- Added a condition checker to skip tests if the network is unreachable
- Helps avoid failures when running behind a company proxy
- support deactivation a container test by skip property

* feat: add randomPattern and randomNumberGenerator function

- Add min length feature to randomString

* feat: add validation methods to SchemaValidator interface

- Delegate applicability check to implementor
- Added `canValidate(Message, boolean)` to check if a message can be validated based on schema validation settings.
- Added `validate(Message, TestContext, String, String)` to perform validation against a specified schema repository and schema.

Co-authored-by: Timon Borter <[email protected]>

* feat: delegates validator requirement check to implementor

Co-authored-by: Timon Borter <[email protected]>

* feat: extract abstract BaseRepository

Co-authored-by: Timon Borter <[email protected]>

* feat: add support for validation of arrays in query parameters

Co-authored-by: Timon Borter <[email protected]>

* feat: add support for loaders that are capable to indicate that tests ought to be skipped

Co-authored-by: Timon Borter <[email protected]>

* feat: add string utils methods

- hasNoText
- isNotEmpty
- appendSegmentToUrlPath
- quote
- trimTrailingComma
- convertFirstChartToUpperCase
- normalizeWhitespace

Co-authored-by: Timon Borter <[email protected]>

* fix: copy queryParams on copy constructor

Co-authored-by: Timon Borter <[email protected]>

* fix: allow lower case accept header name

Co-authored-by: Timon Borter <[email protected]>

* feat: add method to extract a query parameter map from HttpMessage

Co-authored-by: Timon Borter <[email protected]>

* feat: add support for subclassing Http ActionParser and Builders

- To allow for subclassing in OpenAPI TestAPI Parsers and Builders

Co-authored-by: Timon Borter <[email protected]>

* chore: misc changes

- Broaden scope of utility method
- Add Reflection util method to copy fields between beans

Co-authored-by: Timon Borter <[email protected]>

* chore: comment suspicious code block

---------

Co-authored-by: Thorsten Schlathoelter <[email protected]>
Co-authored-by: Timon Borter <[email protected]>
  • Loading branch information
3 people authored Feb 25, 2025
1 parent 80a9fba commit 84303ff
Show file tree
Hide file tree
Showing 74 changed files with 2,831 additions and 852 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,23 @@
import org.citrusframework.jbang.UnitTestSupport;
import org.citrusframework.spi.Resource;
import org.citrusframework.spi.Resources;
import org.citrusframework.util.TestUtils;
import org.testng.Assert;
import org.testng.SkipException;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class JBangActionTest extends UnitTestSupport {

private final Resource helloScript = Resources.fromClasspath("org/citrusframework/jbang/hello.java");

@BeforeClass
public static void beforeClass() {
if (!TestUtils.isNetworkReachable()) {
throw new SkipException("Test skipped because network is not reachable. We are probably running behind a proxy and JBang download is not possible.");
}
}

@Test
public void testScriptOrFile() {
JBangAction jbang = new JBangAction.Builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,23 @@
import org.citrusframework.TestCase;
import org.citrusframework.TestCaseMetaInfo;
import org.citrusframework.jbang.actions.JBangAction;
import org.citrusframework.util.TestUtils;
import org.citrusframework.xml.XmlTestLoader;
import org.citrusframework.xml.actions.XmlTestActionBuilder;
import org.testng.Assert;
import org.testng.SkipException;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class JBangTest extends AbstractXmlActionTest {

@BeforeClass
public static void beforeClass() {
if (!TestUtils.isNetworkReachable()) {
throw new SkipException("Test skipped because network is not reachable. We are probably running behind a proxy and JBang download is not possible.");
}
}

@Test
public void shouldLoadJBangActions() {
XmlTestLoader testLoader = createTestLoader("classpath:org/citrusframework/jbang/xml/jbang-test.xml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,23 @@
import org.citrusframework.TestCase;
import org.citrusframework.TestCaseMetaInfo;
import org.citrusframework.jbang.actions.JBangAction;
import org.citrusframework.util.TestUtils;
import org.citrusframework.yaml.YamlTestLoader;
import org.citrusframework.yaml.actions.YamlTestActionBuilder;
import org.testng.Assert;
import org.testng.SkipException;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class JBangTest extends AbstractYamlActionTest {

@BeforeClass
public static void beforeClass() {
if (!TestUtils.isNetworkReachable()) {
throw new SkipException("Test skipped because network is not reachable. We are probably running behind a proxy and JBang download is not possible.");
}
}

@Test
public void shouldLoadJBangActions() {
YamlTestLoader testLoader = createTestLoader("classpath:org/citrusframework/jbang/yaml/jbang-test.yaml");
Expand Down
23 changes: 23 additions & 0 deletions connectors/citrus-testcontainers/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
<artifactId>citrus-testcontainers</artifactId>
<name>Citrus :: Connectors :: Testcontainers</name>

<properties>
<skipContainerTests>false</skipContainerTests>
</properties>

<dependencies>
<dependency>
<groupId>org.citrusframework</groupId>
Expand Down Expand Up @@ -138,4 +142,23 @@
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>${skipContainerTests}</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<skip>${skipContainerTests}</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ public static Set<String> getTestFileNamePattern(String type) {
* @param def the default value
* @return first value encountered, which is not null. May return null, if default value is null.
*/
private static String getPropertyEnvOrDefault(String prop, String env, String def) {
public static String getPropertyEnvOrDefault(String prop, String env, String def) {
return getProperty(prop, getenv(env) != null ? getenv(env) : def);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
import java.lang.reflect.Modifier;
import java.util.Arrays;

import jakarta.annotation.Nonnull;
import org.citrusframework.exceptions.CitrusRuntimeException;

import static java.lang.String.format;

/**
* Helper for working with reflection on classes.
* <p/>
Expand Down Expand Up @@ -224,6 +227,7 @@ public static Object invokeMethod(Method method, Object target, Object... args)
}
}

@SuppressWarnings("java:S3011")
public static void setField(Field f, Object instance, Object value) {
try {
if (!Modifier.isPublic(f.getModifiers()) && !f.canAccess(instance)) {
Expand All @@ -235,6 +239,7 @@ public static void setField(Field f, Object instance, Object value) {
}
}

@SuppressWarnings("java:S3011")
public static Object getField(Field f, Object instance) {
try {
if ((!Modifier.isPublic(f.getModifiers()) ||
Expand All @@ -253,4 +258,29 @@ public static Object getField(Field f, Object instance) {
return null;
}
}

/**
* Copies the values of all declared fields from a source object to a target object for the specified class.
*/
@SuppressWarnings("java:S3011")
public static void copyFields(@Nonnull Class<?> clazz, @Nonnull Object source, @Nonnull Object target) {
Class<?> currentClass = clazz;

while (currentClass != null) {
Field[] fields = currentClass.getDeclaredFields();

for (Field field : fields) {
try {
field.setAccessible(true);
field.set(target, field.get(source));
} catch (IllegalAccessException e) {
throw new CitrusRuntimeException(format(
"Unable to reflectively copy fields from source to target. clazz=%s sourceClass=%s targetClass=%s",
clazz, source.getClass(), target.getClass()));
}
}

currentClass = currentClass.getSuperclass();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,16 @@ static Optional<SchemaValidator<? extends SchemaValidationContext>> lookup(Strin
* @return true if the message/message type can be validated by this validator
*/
boolean supportsMessageType(String messageType, Message message);

/**
* @param message the message which is subject of validation
* @param schemaValidationEnabled flag to indicate whether schema validation is explicitly enabled
* @return true, if the validator can validate the given message
*/
boolean canValidate(Message message, boolean schemaValidationEnabled);

/**
* Validate the message against the given schemaRepository and schema.
*/
void validate(Message message, TestContext context, String schemaRepository, String schema);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
*/
public interface ValidationContext {

/**
* Indicates whether this validation context requires a validator.
* @return true if a validator is required; false otherwise.
*/
default boolean requiresValidator() {
return false;
}

/**
* Fluent builder
* @param <T> context type
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.citrusframework.util;

import org.testng.Assert;
import org.testng.annotations.Test;

public class ReflectionHelperTest {

@Test
public void copyFields() {
SubClass source = new SubClass("John Doe", 30, "Super John", 60);

SubClass target = new SubClass(null, 0, null, 0);

ReflectionHelper.copyFields(SubClass.class, source, target);

Assert.assertEquals(target.name, "John Doe");
Assert.assertEquals(target.age, 30);

Assert.assertEquals(target.superName, "Super John");
Assert.assertEquals(target.superAge, 60);

}

private static class SuperClass {

String superName;
int superAge;

public SuperClass(String superName, int superAge) {
this.superName = superName;
this.superAge = superAge;
}
}

private static class SubClass extends SuperClass {

String name;
int age;

public SubClass(String name, int age, String superName, int superAge) {
super(superName, superAge);
this.name = name;
this.age = age;
}
}

}
6 changes: 5 additions & 1 deletion core/citrus-base/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>

<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.mifmif</groupId>
<artifactId>generex</artifactId>
<version>1.0.2</version>
</dependency>

<!-- Optional dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@
import org.citrusframework.validation.context.ValidationContext;
import org.citrusframework.validation.json.JsonMessageValidationContext;
import org.citrusframework.validation.json.JsonPathMessageValidationContext;
import org.citrusframework.validation.script.ScriptValidationContext;
import org.citrusframework.validation.xml.XmlMessageValidationContext;
import org.citrusframework.validation.xml.XpathMessageValidationContext;
import org.citrusframework.variable.VariableExtractor;
import org.citrusframework.variable.dictionary.DataDictionary;
import org.slf4j.Logger;
Expand Down Expand Up @@ -245,14 +243,12 @@ protected void validateMessage(Message message, TestContext context) {
}
} else {
boolean mustFindValidator = validationContexts.stream()
.anyMatch(item -> JsonPathMessageValidationContext.class.isAssignableFrom(item.getClass()) ||
XpathMessageValidationContext.class.isAssignableFrom(item.getClass()) ||
ScriptValidationContext.class.isAssignableFrom(item.getClass()));
.anyMatch(ValidationContext::requiresValidator);

List<MessageValidator<? extends ValidationContext>> validators =
List<MessageValidator<? extends ValidationContext>> activeValidators =
context.getMessageValidatorRegistry().findMessageValidators(messageType, message, mustFindValidator);

for (MessageValidator<? extends ValidationContext> messageValidator : validators) {
for (MessageValidator<? extends ValidationContext> messageValidator : activeValidators) {
messageValidator.validateMessage(message, controlMessage, context, validationContexts);
}
}
Expand Down Expand Up @@ -422,7 +418,7 @@ public MessageBuilder getMessageBuilder() {
/**
* Action builder.
*/
public static final class Builder extends ReceiveMessageActionBuilder<ReceiveMessageAction, ReceiveMessageActionBuilderSupport, Builder> {
public static class Builder extends ReceiveMessageActionBuilder<ReceiveMessageAction, ReceiveMessageActionBuilderSupport, Builder> {

/**
* Fluent API action building entry method used in Java DSL.
Expand Down
Loading

0 comments on commit 84303ff

Please sign in to comment.