Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
59c67f2
Merge pull request #4 from OpenAPITools/master
jpfinne May 11, 2026
784da69
Merge branch 'OpenAPITools:master' into master
jpfinne May 12, 2026
9ee6075
Merge branch 'OpenAPITools:master' into master
jpfinne May 14, 2026
91ce7ba
Merge branch 'OpenAPITools:master' into master
jpfinne May 15, 2026
0808d0e
Merge branch 'OpenAPITools:master' into master
jpfinne May 17, 2026
46d9c9c
Merge branch 'OpenAPITools:master' into master
jpfinne May 18, 2026
7b6cf90
Merge branch 'OpenAPITools:master' into master
jpfinne May 30, 2026
b50a337
Merge branch 'OpenAPITools:master' into master
jpfinne Jun 1, 2026
ae1bff7
Merge branch 'OpenAPITools:master' into master
jpfinne Jun 2, 2026
5229d8d
Merge branch 'OpenAPITools:master' into master
jpfinne Jun 3, 2026
cdc74eb
Merge branch 'OpenAPITools:master' into master
jpfinne Jun 13, 2026
df30bd2
Merge branch 'OpenAPITools:master' into master
jpfinne Jul 4, 2026
246b21d
Merge branch 'OpenAPITools:master' into master
jpfinne Jul 8, 2026
8e39563
Merge branch 'OpenAPITools:master' into master
jpfinne Aug 1, 2026
9fdd676
Jspecify improvement
jpfinne Aug 1, 2026
8bcd7ea
Handling of openapiNullable and useOptional
jpfinne Aug 2, 2026
7e2aba7
Regenerate java client (jspecify.yaml updated)
jpfinne Aug 2, 2026
9875949
Regenerate java client (jspecify.yaml updated)
jpfinne Aug 2, 2026
e0e3d6f
Regenerate java client (jspecify.yaml updated)
jpfinne Aug 2, 2026
6e09b1c
fix Case of nullableDataTypeWithEnum
jpfinne Aug 2, 2026
58578cc
fix Case of nullableDatatypeWithEnum
jpfinne Aug 2, 2026
54ffb35
fix toIndentedString
jpfinne Aug 2, 2026
8e86fce
Fix FILES for generate clients
jpfinne Aug 2, 2026
d722f1a
Regenerate samples
jpfinne Aug 2, 2026
249e902
Fix filename of spring-boot-4-jspecify
jpfinne Aug 2, 2026
d3b338d
Fix folder of springboot-4-jspecify
jpfinne Aug 2, 2026
3f3a174
Add assertion for optionalAcceptNullable=true/false
jpfinne Aug 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/samples-spring-jdk17.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ on:
- samples/openapi3/server/petstore/springboot-3/**
- samples/openapi3/server/petstore/springboot-4/**
- samples/openapi3/server/petstore/springboot-4-jspecify/**
- samples/openapi3/server/petstore/springboot-4-jspecify-openapiNullable/**
- samples/openapi3/server/petstore/springboot-4-jspecify-useOptional/**
- samples/server/petstore/springboot-api-response-examples/**
- samples/server/petstore/springboot-lombok-data/**
- samples/server/petstore/springboot-lombok-tostring/**
Expand All @@ -27,6 +29,8 @@ on:
- samples/openapi3/server/petstore/springboot-3/**
- samples/openapi3/server/petstore/springboot-4/**
- samples/openapi3/server/petstore/springboot-4-jspecify/**
- samples/openapi3/server/petstore/springboot-4-jspecify-openapiNullable/**
- samples/openapi3/server/petstore/springboot-4-jspecify-useOptional/**
- samples/server/petstore/springboot-api-response-examples/**
- samples/server/petstore/springboot-lombok-data/**
- samples/server/petstore/springboot-lombok-tostring/**
Expand All @@ -53,6 +57,8 @@ jobs:
- samples/openapi3/server/petstore/springboot-3
- samples/openapi3/server/petstore/springboot-4
- samples/openapi3/server/petstore/springboot-4-jspecify
- samples/openapi3/server/petstore/springboot-4-jspecify-openapiNullable
- samples/openapi3/server/petstore/springboot-4-jspecify-useOptional
- samples/server/petstore/springboot-api-response-examples
- samples/server/petstore/springboot-lombok-data
- samples/server/petstore/springboot-lombok-tostring
Expand Down
24 changes: 24 additions & 0 deletions bin/configs/spring-boot-4-jspecify-openapiNullable.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
generatorName: spring
library: spring-boot
outputDir: samples/openapi3/server/petstore/springboot-4-jspecify-openapiNullable
inputSpec: modules/openapi-generator/src/test/resources/3_0/java/jspecify.yaml
templateDir: modules/openapi-generator/src/main/resources/JavaSpring
validateSpec: false
additionalProperties:
groupId: org.openapitools.openapi3
documentationProvider: springdoc
interfaceOnly: true
artifactId: springboot
snapshotVersion: "true"
useSpringBoot4: true
useJackson3: true
useBeanValidation: true
withXml: true
hideGenerationTimestamp: "true"
generateConstructorWithAllArgs: true
generateBuilders: true
openApiNullable: true
useJspecify: true
typeMappings:
OffsetDateTime: java.time.Instant
BigDecimal: java.math.BigDecimal
25 changes: 25 additions & 0 deletions bin/configs/spring-boot-4-jspecify-useOptional.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
generatorName: spring
library: spring-boot
outputDir: samples/openapi3/server/petstore/springboot-4-jspecify-useOptional
inputSpec: modules/openapi-generator/src/test/resources/3_0/java/jspecify.yaml
templateDir: modules/openapi-generator/src/main/resources/JavaSpring
validateSpec: false
additionalProperties:
groupId: org.openapitools.openapi3
documentationProvider: springdoc
interfaceOnly: true
artifactId: springboot
snapshotVersion: "true"
useSpringBoot4: true
useJackson3: true
useBeanValidation: true
withXml: true
hideGenerationTimestamp: "true"
generateConstructorWithAllArgs: true
generateBuilders: true
useOptional: true
optionalAcceptNullable: true
useJspecify: true
typeMappings:
OffsetDateTime: java.time.Instant
BigDecimal: java.math.BigDecimal
5 changes: 4 additions & 1 deletion bin/configs/spring-boot-4-jspecify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ additionalProperties:
generateConstructorWithAllArgs: true
generateBuilders: true
openApiNullable: false
useJspecify: true
useJspecify: true
typeMappings:
OffsetDateTime: java.time.Instant
BigDecimal: java.math.BigDecimal
Original file line number Diff line number Diff line change
Expand Up @@ -2866,7 +2866,7 @@ protected ImmutableMap.Builder<String, Mustache.Lambda> addMustacheLambdas() {
Mustache.Lambda jSpecifyDatatypeLambda = (fragment, writer) -> {
String dataType = fragment.execute();
if (jSpecifyNullableLambda.isSetAndClear()) {
int idx = dataType.lastIndexOf('.');
int idx = getLastIndex(dataType);
if (idx > 0) {
// generate declaration like java.time.@Nullable Timestamp
writer.write(dataType.substring(0, idx + 1));
Expand All @@ -2886,6 +2886,14 @@ protected ImmutableMap.Builder<String, Mustache.Lambda> addMustacheLambdas() {

}

private int getLastIndex(String dataType) {
int index = dataType.indexOf('<');
if (index >= 0) {
dataType = dataType.substring(0, index);
}
return dataType.lastIndexOf('.');
}

/**
* for Jspecify, remove @Nullable before the datatype and set keptNullable to true if done.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{#lambda.trim}}{{>nullable_var_annotations}} {{#lambda.jSpecifyDatatype}}{{{datatypeWithEnum}}}{{/lambda.jSpecifyDatatype}}{{/lambda.trim}}
{{#lambda.trim}}{{>nullable_var_annotations}} {{>nullableDatatypeWithEnum}}{{/lambda.trim}}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
{{#deprecated}}
@Deprecated
{{/deprecated}}
public {{classname}}.Builder {{name}}({{#removeAnnotations}}{{{datatypeWithEnum}}}{{/removeAnnotations}} {{name}}) {
public {{classname}}.Builder {{name}}({{>nullableArgument_builder}} {{name}}) {
this.instance.{{name}}({{name}});
return this;
}
Expand All @@ -37,7 +37,7 @@
{{/vars}}
{{#parentVars}}
@Override
public {{classname}}.Builder {{name}}({{#removeAnnotations}}{{{datatypeWithEnum}}}{{/removeAnnotations}} {{name}}) {
public {{classname}}.Builder {{name}}({{>nullableArgument_builder}} {{name}}) {
this.instance.{{name}}({{name}});
return this;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{#lambda.jSpecifyNullable}}{{^required}}{{^defaultValue}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/defaultValue}}{{#defaultValue}}{{^openApiNullable}}{{#isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{/defaultValue}}{{/required}}{{/lambda.jSpecifyNullable}}
{{#paramName}}{{>nullableAnnotation_default}}{{/paramName}}{{^paramName}}{{>nullableAnnotation_jspecify}}{{^useJspecify}}{{!backward compatibility}}{{>nullableAnnotation_default}}{{/useJspecify}}{{/paramName}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{#lambda.jSpecifyNullable}}{{^required}}{{^defaultValue}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/defaultValue}}{{#defaultValue}}{{^openApiNullable}}{{#isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{/defaultValue}}{{/required}}{{/lambda.jSpecifyNullable}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{#useJspecify}}{{#lambda.jSpecifyNullable}}{{^required}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/required}}{{/lambda.jSpecifyNullable}}{{/useJspecify}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{#useJspecify}}{{^required}}{{#lambda.jSpecifyNullable}}{{^useOptional}}@Nullable {{/useOptional}}{{#useOptional}}{{#optionalAcceptNullable}}@Nullable {{/optionalAcceptNullable}}{{/useOptional}}{{/lambda.jSpecifyNullable}}{{/required}}{{#lambda.jSpecifyDatatype}}{{#removeAnnotations}}{{{datatypeWithEnum}}}{{/removeAnnotations}}{{/lambda.jSpecifyDatatype}}{{/useJspecify}}{{^useJspecify}}{{#removeAnnotations}}{{{datatypeWithEnum}}}{{/removeAnnotations}}{{/useJspecify}}
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
/**
* Constructor with all args parameters
*/
public {{classname}}({{#vendorExtensions.x-java-all-args-constructor-vars}}{{>nullableAnnotation}}{{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-java-all-args-constructor-vars}}) {
public {{classname}}({{#vendorExtensions.x-java-all-args-constructor-vars}}{{#useJspecify}}{{#lambda.jSpecifyNullable}}{{^required}}@Nullable {{/required}}{{/lambda.jSpecifyNullable}}{{#lambda.jSpecifyDatatype}}{{{datatypeWithEnum}}}{{/lambda.jSpecifyDatatype}}{{/useJspecify}}{{^useJspecify}}{{>nullableAnnotation}}{{{datatypeWithEnum}}}{{/useJspecify}} {{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-java-all-args-constructor-vars}}) {
{{#parent}}
super({{#parentVars}}{{name}}{{^-last}}, {{/-last}}{{/parentVars}});
{{/parent}}
Expand All @@ -153,7 +153,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
{{^lombok.Data}}

{{! begin feature: fluent setter methods }}
public {{classname}} {{name}}({{>nullableAnnotation}}{{{datatypeWithEnum}}} {{name}}) {
public {{classname}} {{name}}({{#useJspecify}}{{#lambda.jSpecifyNullable}}{{^required}}{{^useOptional}}@Nullable {{/useOptional}}{{#useOptional}}{{#optionalAcceptNullable}}@Nullable {{/optionalAcceptNullable}}{{/useOptional}}{{/required}}{{/lambda.jSpecifyNullable}}{{#lambda.jSpecifyDatatype}}{{{datatypeWithEnum}}}{{/lambda.jSpecifyDatatype}}{{/useJspecify}}{{^useJspecify}}{{>nullableAnnotation_default}}{{{datatypeWithEnum}}}{{/useJspecify}} {{name}}) {
{{#openApiNullable}}
this.{{name}} = {{#isNullable}}JsonNullable.of({{/isNullable}}{{#useOptional}}{{^required}}{{^isNullable}}{{^isContainer}}Optional.of{{#optionalAcceptNullable}}Nullable{{/optionalAcceptNullable}}({{/isContainer}}{{/isNullable}}{{/required}}{{/useOptional}}{{name}}{{#isNullable}}){{/isNullable}}{{#useOptional}}{{^required}}{{^isNullable}}{{^isContainer}}){{/isContainer}}{{/isNullable}}{{/required}}{{/useOptional}};
{{/openApiNullable}}
Expand Down Expand Up @@ -348,7 +348,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString({{>nullableAnnotation}}Object o) {
private String toIndentedString(@Nullable Object o) {
return o == null ? "null" : o.toString().replace("\n", "\n ");
}
{{/lombok.ToString}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4654,7 +4654,9 @@ public void testJspecify(String library, boolean useSpringBoot4, boolean hasJspe
final Map<String, File> files = generateFromContract("src/test/resources/3_0/java/jspecify.yaml", library,
Map.of(USE_JSPECIFY, true,
"containerDefaultToNull", true,
USE_SPRING_BOOT4, useSpringBoot4
USE_SPRING_BOOT4, useSpringBoot4,
JavaClientCodegen.OPENAPI_NULLABLE, false,
GENERATE_CONSTRUCTOR_WITH_ALL_ARGS, true
),
codegenConfigurator ->
codegenConfigurator
Expand Down Expand Up @@ -4683,17 +4685,23 @@ public void testJspecify(String library, boolean useSpringBoot4, boolean hasJspe
"import org.jspecify.annotations.Nullable;",
"private java.time.@Nullable Instant dt;",
"setDt(java.time.@Nullable Instant dt)",
"dt(java.time.@Nullable Instant dt)",
"Foo dt(java.time.@Nullable Instant dt)",
"setBinary(@Nullable File binary)",
"public @Nullable File getBinary()",
"List<java.time.@Nullable Instant> getListOfDt()",
"setListOfDt(List<java.time.@Nullable Instant> listOfDt)"
);
"@Nullable List<java.time.Instant> getListOfDt()",
"private java.time.@Nullable Instant nullableDt;",
"private @Nullable List<java.time.Instant> listOfDt;",
"setListOfDt(@Nullable List<java.time.Instant> listOfDt)");
if (!NATIVE.equals(library)) { // native library does not generate all arg constructors
JavaFileAssert.assertThat(files.get("Foo.java"))
.fileContains(
"public Foo(@JsonProperty(JSON_PROPERTY_DT) java.time.@Nullable Instant dt, @JsonProperty(JSON_PROPERTY_NULLABLE_DT) java.time.@Nullable Instant nullableDt, @JsonProperty(JSON_PROPERTY_BINARY) @Nullable File binary, @JsonProperty(JSON_PROPERTY_NULLABLE_BINARY) @Nullable File nullableBinary, @JsonProperty(JSON_PROPERTY_LIST_OF_DT) @Nullable List<java.time.Instant> listOfDt, @JsonProperty(JSON_PROPERTY_LIST_MIN_INTEMS) @Nullable List<java.time.Instant> listMinIntems, @JsonProperty(JSON_PROPERTY_NULLABLE_LIST_MIN_INTEMS) @Nullable List<java.time.Instant> nullableListMinIntems, @JsonProperty(JSON_PROPERTY_REQUIRED_DT) java.time.Instant requiredDt, @JsonProperty(JSON_PROPERTY_NUMBER) java.math.@Nullable BigDecimal number, @JsonProperty(JSON_PROPERTY_NULLABLE_NUMBER) java.math.@Nullable BigDecimal nullableNumber, @JsonProperty(JSON_PROPERTY_COLOR) @Nullable String color, @JsonProperty(JSON_PROPERTY_REQUIRED_COLOR) String requiredColor, @JsonProperty(JSON_PROPERTY_NULLABLE_COLOR) @Nullable String nullableColor) {");
}
if (!RESTTEMPLATE.equals(library)) {
JavaFileAssert.assertThat(files.get("DefaultApi.java"))
.fileContains(
"import org.jspecify.annotations.Nullable;",
"(java.time.@Nullable Instant dtParam, java.time.@Nullable Instant dtQuery, java.time.@Nullable Instant dtCookie)"
"(java.time.@Nullable Instant dtParam, java.time.@Nullable Instant dtQuery, java.time.@Nullable Instant dtCookie, @Nullable String color)"
);
}
JavaFileAssert.assertThat(files.get("api/package-info.java"))
Expand All @@ -4702,7 +4710,70 @@ public void testJspecify(String library, boolean useSpringBoot4, boolean hasJspe
.fileContains("@org.jspecify.annotations.NullMarked");
JavaFileAssert.assertThat(files.get("client/package-info.java"))
.fileContains("@org.jspecify.annotations.NullMarked");
}

@Test(dataProvider = "jspecifyLibraries")
public void testJspecify_openapiNullable(String library, boolean useSpringBoot4, boolean hasJspecifyDependency) throws IOException {
final Map<String, File> files = generateFromContract("src/test/resources/3_0/java/jspecify.yaml", library,
Map.of(USE_JSPECIFY, true,
"containerDefaultToNull", true,
USE_SPRING_BOOT4, useSpringBoot4,
JavaClientCodegen.OPENAPI_NULLABLE, true,
GENERATE_CONSTRUCTOR_WITH_ALL_ARGS, true
),
codegenConfigurator ->
codegenConfigurator
.setValidateSpec(false)
.addTypeMapping("OffsetDateTime", "java.time.Instant")
.addTypeMapping("BigDecimal", "java.math.BigDecimal"));

if (hasJspecifyDependency) {
assertThat(files.get("build.gradle")).content()
.contains("implementation \"org.jspecify:jspecify:1.0.0\"")
.doesNotContain("findbugs");
assertThat(files.get("pom.xml")).content()
.contains(
"<groupId>org.jspecify</groupId>",
"<artifactId>jspecify</artifactId>",
"<version>1.0.0</version>")
.doesNotContain("findbugs");
} else {
assertThat(files.get("build.gradle")).content()
.doesNotContain("org.jspecify");
assertThat(files.get("pom.xml")).content()
.doesNotContain("org.jspecify");
}
JavaFileAssert.assertThat(files.get("Foo.java"))
.fileContains(
"import org.jspecify.annotations.Nullable;",
"private java.time.@Nullable Instant dt;",
"setDt(java.time.@Nullable Instant dt)",
"Foo dt(java.time.@Nullable Instant dt)",
"setBinary(@Nullable File binary)",
"public @Nullable File getBinary()",
"@Nullable List<java.time.Instant> getListOfDt()",
"setListOfDt(@Nullable List<java.time.Instant> listOfDt)",
"JsonNullable<java.time.Instant> nullableDt = JsonNullable.<java.time.Instant>undefined()",
"private JsonNullable<File> nullableBinary = JsonNullable.<File>undefined();");
if (!NATIVE.equals(library)) { // native library does not generate all arg constructors
JavaFileAssert.assertThat(files.get("Foo.java"))
.fileContains(
"public Foo(@JsonProperty(JSON_PROPERTY_DT) java.time.@Nullable Instant dt, @JsonProperty(JSON_PROPERTY_NULLABLE_DT) java.time.@Nullable Instant nullableDt, @JsonProperty(JSON_PROPERTY_BINARY) @Nullable File binary, @JsonProperty(JSON_PROPERTY_NULLABLE_BINARY) @Nullable File nullableBinary, @JsonProperty(JSON_PROPERTY_LIST_OF_DT) @Nullable List<java.time.Instant> listOfDt, @JsonProperty(JSON_PROPERTY_LIST_MIN_INTEMS) @Nullable List<java.time.Instant> listMinIntems, @JsonProperty(JSON_PROPERTY_NULLABLE_LIST_MIN_INTEMS) @Nullable List<java.time.Instant> nullableListMinIntems, @JsonProperty(JSON_PROPERTY_REQUIRED_DT) java.time.Instant requiredDt, @JsonProperty(JSON_PROPERTY_NUMBER) java.math.@Nullable BigDecimal number, @JsonProperty(JSON_PROPERTY_NULLABLE_NUMBER) java.math.@Nullable BigDecimal nullableNumber, @JsonProperty(JSON_PROPERTY_COLOR) @Nullable String color, @JsonProperty(JSON_PROPERTY_REQUIRED_COLOR) String requiredColor, @JsonProperty(JSON_PROPERTY_NULLABLE_COLOR) @Nullable String nullableColor) {"
);
}
if (!RESTTEMPLATE.equals(library)) {
JavaFileAssert.assertThat(files.get("DefaultApi.java"))
.fileContains(
"import org.jspecify.annotations.Nullable;",
"(java.time.@Nullable Instant dtParam, java.time.@Nullable Instant dtQuery, java.time.@Nullable Instant dtCookie, @Nullable String color)"
);
}
JavaFileAssert.assertThat(files.get("api/package-info.java"))
.fileContains("@org.jspecify.annotations.NullMarked");
JavaFileAssert.assertThat(files.get("model/package-info.java"))
.fileContains("@org.jspecify.annotations.NullMarked");
JavaFileAssert.assertThat(files.get("client/package-info.java"))
.fileContains("@org.jspecify.annotations.NullMarked");
}

@DataProvider(name = "replaceOneOf")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,9 @@ public JavaFileAssert fileContains(final String... lines) {
Assertions.assertThat(actualBody)
.withFailMessage(
"File should contain lines\n====\n%s\n====\nbut actually was\n====\n%s\n====",
Arrays.stream(lines).collect(Collectors.joining(System.lineSeparator())), actualBody
Arrays.stream(lines)
.filter(line -> !actualBody.contains(line))
.collect(Collectors.joining(System.lineSeparator())), actualBody
)
.contains(lines);

Expand All @@ -249,7 +251,9 @@ public JavaFileAssert fileDoesNotContain(final String... lines) {
Assertions.assertThat(actualBody)
.withFailMessage(
"File should not contain lines\n====\n%s\n====\nbut actually was\n====\n%s\n====",
Arrays.stream(lines).collect(Collectors.joining(System.lineSeparator())), actualBody
Arrays.stream(lines)
.filter(line -> actualBody.contains(line))
.collect(Collectors.joining(System.lineSeparator())), actualBody
)
.doesNotContain(lines);

Expand Down
Loading
Loading