Base library for Apache Flink architecture tests that provides common ArchUnit extensions and utilities for validating architectural constraints in both production and test code
—
ArchUnit conditions for validating method signatures, including deep analysis of parameter types, return types, and exception types. This module provides powerful capabilities for ensuring architectural constraints on method-level APIs.
Generic condition to check fulfillment of any predicate for elements with names.
/**
* Generic condition to check fulfillment of a predicate
* @param predicate The predicate that items must satisfy
* @return ArchCondition that validates items against the predicate
*/
public static <T extends HasName> ArchCondition<T> fulfill(DescribedPredicate<T> predicate);Usage Example:
import static org.apache.flink.architecture.common.Conditions.fulfill;
import com.tngtech.archunit.base.DescribedPredicate;
// Ensure all public methods follow naming convention
DescribedPredicate<JavaMethod> followsNamingConvention =
method -> method.getName().matches("^[a-z][a-zA-Z0-9]*$");
ArchRule methodNamingRule = methods()
.that().arePublic()
.should(fulfill(followsNamingConvention));
methodNamingRule.check(classes);Tests all leaf types used by a method (arguments, return type, and exceptions) against a given predicate.
/**
* Tests leaf types of method arguments, return types, and exceptions
* @param typePredicate Predicate to test against all leaf types
* @return ArchCondition that validates all method leaf types
*/
public static ArchCondition<JavaMethod> haveLeafTypes(
DescribedPredicate<JavaClass> typePredicate);Usage Example:
import static org.apache.flink.architecture.common.Conditions.haveLeafTypes;
import static org.apache.flink.architecture.common.SourcePredicates.areJavaClasses;
// Ensure all method types are from allowed packages
DescribedPredicate<JavaClass> allowedTypes = areJavaClasses().and(
clazz -> clazz.getPackageName().startsWith("org.apache.flink") ||
clazz.getPackageName().startsWith("java.") ||
clazz.getPackageName().startsWith("javax.")
);
ArchRule apiTypesRule = methods()
.that().arePublic()
.and().areNotConstructors()
.should(haveLeafTypes(allowedTypes));Tests leaf return types of methods against a given predicate.
/**
* Tests leaf return types of a method against the given predicate
* @param typePredicate Predicate to test against return type leaf types
* @return ArchCondition that validates method return types
*/
public static ArchCondition<JavaMethod> haveLeafReturnTypes(
DescribedPredicate<JavaClass> typePredicate);Usage Example:
import static org.apache.flink.architecture.common.Conditions.haveLeafReturnTypes;
// Ensure API methods don't return internal implementation types
DescribedPredicate<JavaClass> notInternalTypes =
clazz -> !clazz.getPackageName().contains(".internal.");
ArchRule publicApiReturnTypesRule = methods()
.that().arePublic()
.and().areDeclaredInClassesThat().haveSimpleNameEndingWith("API")
.should(haveLeafReturnTypes(notInternalTypes));Tests leaf argument types of methods against a given predicate.
/**
* Tests leaf argument types of a method against the given predicate
* @param typePredicate Predicate to test against argument type leaf types
* @return ArchCondition that validates method argument types
*/
public static ArchCondition<JavaMethod> haveLeafArgumentTypes(
DescribedPredicate<JavaClass> typePredicate);Usage Example:
import static org.apache.flink.architecture.common.Conditions.haveLeafArgumentTypes;
// Ensure constructor arguments are serializable for certain classes
DescribedPredicate<JavaClass> isSerializable =
clazz -> clazz.isAssignableTo(java.io.Serializable.class) ||
clazz.isPrimitive() ||
clazz.getPackageName().startsWith("java.lang");
ArchRule serializableConstructorArgs = constructors()
.that().areDeclaredInClassesThat().implement(Serializable.class)
.should(haveLeafArgumentTypes(isSerializable));Tests leaf exception types declared by methods against a given predicate.
/**
* Tests leaf exception types of a method against the given predicate
* @param typePredicate Predicate to test against exception type leaf types
* @return ArchCondition that validates method exception types
*/
public static ArchCondition<JavaMethod> haveLeafExceptionTypes(
DescribedPredicate<JavaClass> typePredicate);Usage Example:
import static org.apache.flink.architecture.common.Conditions.haveLeafExceptionTypes;
// Ensure public API methods only throw documented exception types
DescribedPredicate<JavaClass> allowedExceptions =
clazz -> clazz.isAssignableTo(RuntimeException.class) ||
clazz.getName().equals("java.io.IOException") ||
clazz.getPackageName().startsWith("org.apache.flink.api.common.exceptions");
ArchRule apiExceptionTypesRule = methods()
.that().arePublic()
.and().areDeclaredInClassesThat().areAnnotatedWith(PublicEvolving.class)
.should(haveLeafExceptionTypes(allowedExceptions));The leaf type analysis system recursively analyzes complex type structures:
String[] → String)List<Map<String, Integer>> → List, Map, String, Integer)// Method signature:
public CompletableFuture<List<ProcessingResult<String>>> processData(
Map<String, Configuration> configs,
Optional<ExecutionEnvironment> env
) throws ValidationException, ProcessingException
// Leaf types analyzed:
// Return type: CompletableFuture, List, ProcessingResult, String
// Arguments: Map, String, Configuration, Optional, ExecutionEnvironment
// Exceptions: ValidationException, ProcessingException// Comprehensive API validation
DescribedPredicate<JavaClass> apiCompliantTypes =
areJavaClasses().and(
clazz -> clazz.getPackageName().startsWith("org.apache.flink.api") ||
clazz.getPackageName().startsWith("java.") ||
clazz.isPrimitive()
);
ArchRule comprehensiveApiRule = methods()
.that().arePublic()
.and().areDeclaredInClassesThat().haveSimpleNameEndingWith("API")
.should(haveLeafTypes(apiCompliantTypes))
.andShould().haveRawReturnType(not(Object.class))
.andShould().notHaveRawParameterTypes(Object.class);// Ensure streaming API methods use streaming types
DescribedPredicate<JavaClass> streamingTypes =
clazz -> clazz.getPackageName().contains("streaming") ||
clazz.getPackageName().startsWith("java.util.concurrent") ||
clazz.isPrimitive();
ArchRule streamingApiTypesRule = methods()
.that().arePublic()
.and().areDeclaredInClassesThat().haveNameMatching(".*Stream.*")
.should(haveLeafReturnTypes(streamingTypes))
.andShould(haveLeafArgumentTypes(streamingTypes));// Validate that service methods handle exceptions properly
DescribedPredicate<JavaClass> serviceExceptions =
clazz -> clazz.isAssignableTo(ServiceException.class) ||
clazz.isAssignableTo(RuntimeException.class);
ArchRule serviceExceptionRule = methods()
.that().arePublic()
.and().areDeclaredInClassesThat().haveSimpleNameEndingWith("Service")
.should(haveLeafExceptionTypes(serviceExceptions));.and() and .or()Install with Tessl CLI
npx tessl i tessl/maven-org-apache-flink--flink-architecture-tests-base