Skip to content

Commit 63bc659

Browse files
committed
Issue 601: test case reproducing reported issue
1 parent 6b3240a commit 63bc659

File tree

3 files changed

+160
-7
lines changed

3 files changed

+160
-7
lines changed

boat-scaffold/pom.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
</sonar.coverage.jacoco.xmlReportPaths>
2222
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
2323
<sonar.jacoco.itReportPath>${project.basedir}/target/jacoco-it.exec</sonar.jacoco.itReportPath>
24+
<embedded.maven.version>3.9.4</embedded.maven.version>
2425
</properties>
2526

2627
<dependencies>
@@ -108,6 +109,24 @@
108109
<version>2.6</version>
109110
<scope>compile</scope>
110111
</dependency>
112+
<dependency>
113+
<groupId>org.apache.maven</groupId>
114+
<artifactId>maven-embedder</artifactId>
115+
<version>${embedded.maven.version}</version>
116+
<scope>test</scope>
117+
</dependency>
118+
<dependency>
119+
<groupId>org.apache.maven</groupId>
120+
<artifactId>maven-compat</artifactId>
121+
<version>${embedded.maven.version}</version>
122+
<scope>test</scope>
123+
</dependency>
124+
<dependency>
125+
<groupId>org.codehaus.plexus</groupId>
126+
<artifactId>plexus-utils</artifactId>
127+
<version>3.5.1</version>
128+
<scope>test</scope>
129+
</dependency>
111130
</dependencies>
112131

113132
<build>

boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatSpringTemplatesTests.java

Lines changed: 112 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,54 @@
22

33
import static java.util.Arrays.asList;
44
import static java.util.Arrays.stream;
5+
import static java.util.stream.Collectors.joining;
6+
import static java.util.stream.Collectors.toList;
7+
import static java.util.stream.Stream.concat;
58
import static org.hamcrest.MatcherAssert.assertThat;
69
import static org.hamcrest.Matchers.equalTo;
710
import static org.hamcrest.Matchers.hasSize;
811
import static org.hamcrest.Matchers.is;
912
import static org.hamcrest.Matchers.not;
1013
import static org.hamcrest.Matchers.nullValue;
11-
import static org.junit.jupiter.api.DynamicContainer.*;
12-
import static org.junit.jupiter.api.DynamicTest.*;
13-
import static java.util.stream.Collectors.joining;
14-
import static java.util.stream.Collectors.toList;
15-
import static java.util.stream.Stream.*;
14+
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
import static org.junit.jupiter.api.Assertions.assertNull;
16+
import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
17+
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
18+
19+
import com.fasterxml.jackson.databind.ObjectMapper;
20+
import com.fasterxml.jackson.databind.type.ArrayType;
21+
import com.fasterxml.jackson.databind.type.TypeBindings;
22+
import com.fasterxml.jackson.databind.type.TypeFactory;
23+
import com.google.common.base.CaseFormat;
1624
import java.io.File;
1725
import java.io.IOException;
1826
import java.lang.annotation.ElementType;
1927
import java.lang.annotation.Retention;
2028
import java.lang.annotation.RetentionPolicy;
2129
import java.lang.annotation.Target;
30+
import java.lang.reflect.Constructor;
2231
import java.lang.reflect.Method;
32+
import java.net.URL;
33+
import java.net.URLClassLoader;
2334
import java.nio.file.Files;
2435
import java.nio.file.Paths;
2536
import java.util.ArrayList;
2637
import java.util.List;
38+
import java.util.concurrent.atomic.AtomicReference;
2739
import java.util.function.Predicate;
2840
import java.util.regex.Pattern;
2941
import java.util.stream.IntStream;
3042
import java.util.stream.Stream;
31-
3243
import lombok.SneakyThrows;
3344
import lombok.extern.slf4j.Slf4j;
3445
import org.apache.commons.io.FileUtils;
46+
import org.apache.maven.cli.MavenCli;
47+
import org.codehaus.plexus.classworlds.ClassWorld;
3548
import org.junit.jupiter.api.BeforeAll;
3649
import org.junit.jupiter.api.DynamicNode;
3750
import org.junit.jupiter.api.DynamicTest;
3851
import org.junit.jupiter.api.TestFactory;
52+
import org.junit.platform.commons.util.StringUtils;
3953
import org.openapitools.codegen.ClientOptInput;
4054
import org.openapitools.codegen.CodegenConstants;
4155
import org.openapitools.codegen.DefaultGenerator;
@@ -222,6 +236,98 @@ void useWithModifiers() {
222236
equalTo(this.param.useWithModifiers));
223237
}
224238

239+
@Check
240+
void checkCompiles() throws Exception {
241+
final var projectDir = new File(TEST_OUTPUT, param.name);
242+
assertThat(projectDir + " is not a directory", projectDir.isDirectory());
243+
compileGeneratedProject(projectDir);
244+
verifyGeneratedClasses(projectDir);
245+
}
246+
247+
private static void compileGeneratedProject(File projectDir) {
248+
var mavenCli = new MavenCli(new ClassWorld("myRealm", BoatSpringTemplatesTests.class.getClassLoader()));
249+
final String initialDir = System.getProperty(MavenCli.MULTIMODULE_PROJECT_DIRECTORY);
250+
try {
251+
System.setProperty(MavenCli.MULTIMODULE_PROJECT_DIRECTORY, projectDir.getAbsolutePath());
252+
String[] args = {"clean", "compile"};
253+
int compileStatus = mavenCli.doMain(args, projectDir.getAbsolutePath(), System.out, System.out);
254+
assertEquals(0, compileStatus, "Could not compile generated project in dir: " + projectDir);
255+
} finally {
256+
if (StringUtils.isBlank(initialDir)) {
257+
System.clearProperty(MavenCli.MULTIMODULE_PROJECT_DIRECTORY);
258+
} else {
259+
System.setProperty(MavenCli.MULTIMODULE_PROJECT_DIRECTORY, initialDir);
260+
}
261+
}
262+
}
263+
264+
private void verifyGeneratedClasses(File projectDir) throws Exception {
265+
var classesDir = new File(projectDir, "target/classes");
266+
var classLoader = URLClassLoader.newInstance(
267+
new URL[]{classesDir.toURI().toURL()},
268+
BoatSpringTemplatesTests.class.getClassLoader()
269+
);
270+
String testedModelClassName = buildTestedModelClassName();
271+
verifyModelClassSerializesAndDeserializesFromJson(classLoader, testedModelClassName);
272+
}
273+
274+
private void verifyModelClassSerializesAndDeserializesFromJson(ClassLoader classLoader,
275+
String testedModelClassName) throws InterruptedException {
276+
var objectMapper = new ObjectMapper();
277+
final AtomicReference<Exception> exceptionRef = new AtomicReference<>();
278+
Runnable verification = () -> {
279+
try {
280+
Class<?> modelClass = classLoader.loadClass(testedModelClassName);
281+
Constructor<?> constructor = modelClass.getConstructor(String.class, String.class, String.class);
282+
Object modelObject1 = constructor.newInstance("OK_status", "ref123", "EUR");
283+
Object modelObject2 = constructor.newInstance("BAD_status", "ref456", "USD");
284+
List<?> modelObjects = List.of(modelObject1, modelObject2);
285+
286+
String serializedObjects = objectMapper.writeValueAsString(modelObjects);
287+
Object[] deserializedModelObjects = objectMapper.readValue(
288+
serializedObjects,
289+
ArrayType.construct(
290+
TypeFactory.defaultInstance().constructFromCanonical(modelClass.getName()),
291+
TypeBindings.emptyBindings()
292+
)
293+
);
294+
295+
assertEquals(modelObjects.size(), deserializedModelObjects.length);
296+
assertEquals(modelObject1.getClass(), deserializedModelObjects[0].getClass());
297+
298+
} catch (Exception e) {
299+
log.warn("Verification error", e);
300+
exceptionRef.set(e);
301+
}
302+
};
303+
304+
runVerification(verification, classLoader).join();
305+
assertNull(exceptionRef.get(), "Classes verification failed");
306+
}
307+
308+
/**
309+
* Build proper class name for `ReceivableRequest`.
310+
*/
311+
private String buildTestedModelClassName() {
312+
var modelPackage = param.name.replace('-', '.') + ".model";
313+
var classNameSuffix = org.apache.commons.lang3.StringUtils.capitalize(
314+
param.name.indexOf('-') > -1
315+
? CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, param.name)
316+
: param.name
317+
);
318+
return modelPackage + ".ReceivableRequest" + classNameSuffix;
319+
}
320+
321+
private Thread runVerification(Runnable verification, ClassLoader classLoader) {
322+
var verificationThread = new Thread(verification);
323+
verificationThread.setName("verify-classes-" + param.name);
324+
verificationThread.setContextClassLoader(classLoader);
325+
verificationThread.setUncaughtExceptionHandler(
326+
(t1, e) -> log.error("Uncaught exception in classes verifier: ", e));
327+
verificationThread.start();
328+
return verificationThread;
329+
}
330+
225331
private boolean findPattern(String filePattern, String linePattern) {
226332
final Predicate<String> fileMatch = Pattern.compile(filePattern).asPredicate();
227333
log.info("Files: {}", files);

boat-scaffold/src/test/resources/boat-spring/openapi.yaml

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,32 @@ components:
6969
format: date-time
7070
x-java-type: java.time.OffsetDateTime
7171
x-java-type: com.backbase.integration.transaction.external.rest.spec.v2.transactions.TransactionsPatchRequestBody
72-
72+
ReceivableRequest:
73+
allOf:
74+
- $ref: '#/components/schemas/PaymentRequest'
75+
- required:
76+
- status
77+
- currencyCode
78+
- referenceNumber
79+
type: object
80+
properties:
81+
status:
82+
type: string
83+
description: Status of the request
84+
paymentRequestType:
85+
type: string
86+
default: ReceivableRequest
87+
PaymentRequest:
88+
required:
89+
- currencyCode
90+
- referenceNumber
91+
type: object
92+
discriminator:
93+
propertyName: paymentRequestType
94+
properties:
95+
referenceNumber:
96+
type: string
97+
description: Reference number
98+
currencyCode:
99+
type: string
100+
description: Currency of the payment request

0 commit comments

Comments
 (0)