From 8108678bc5f726c8769fe5aa09997ba6240cfaff Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Thu, 16 Oct 2025 12:29:15 +0200 Subject: [PATCH] Add nullability annotations to tests in cli/spring-boot-cli See gh-47263 --- cli/spring-boot-cli/build.gradle | 8 +++++++ .../CommandRunnerIntegrationTests.java | 3 ++- .../boot/cli/command/CommandRunnerTests.java | 5 ++++- .../init/AbstractHttpClientMockTests.java | 17 +++++++------- .../cli/command/init/InitCommandTests.java | 11 +++++++++- .../init/InitializrServiceMetadataTests.java | 22 ++++++++++++++----- .../command/init/InitializrServiceTests.java | 4 +++- .../init/ProjectGenerationRequestTests.java | 3 ++- 8 files changed, 54 insertions(+), 19 deletions(-) diff --git a/cli/spring-boot-cli/build.gradle b/cli/spring-boot-cli/build.gradle index b0ea8b54de2..a4a26bcd992 100644 --- a/cli/spring-boot-cli/build.gradle +++ b/cli/spring-boot-cli/build.gradle @@ -162,3 +162,11 @@ publishing { eclipse.classpath { // https://github.com/eclipse/buildship/issues/939 plusConfigurations += [ configurations.compileOnlyProject ] } + +tasks.named("compileTestJava") { + options.nullability.checking = "tests" +} + +tasks.named("compileIntTestJava") { + options.nullability.checking = "tests" +} diff --git a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/CommandRunnerIntegrationTests.java b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/CommandRunnerIntegrationTests.java index d2b03a989fc..5609e16072e 100644 --- a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/CommandRunnerIntegrationTests.java +++ b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/CommandRunnerIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.cli.command; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -58,7 +59,7 @@ class CommandRunnerIntegrationTests { static class ArgHandlingCommand extends AbstractCommand { - private String[] args; + private String @Nullable [] args; ArgHandlingCommand() { super("args", ""); diff --git a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/CommandRunnerTests.java b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/CommandRunnerTests.java index 6d33e21cafb..3d8f92839e6 100644 --- a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/CommandRunnerTests.java +++ b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/CommandRunnerTests.java @@ -19,6 +19,7 @@ package org.springframework.boot.cli.command; import java.util.EnumSet; import java.util.Set; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -47,9 +48,11 @@ class CommandRunnerTests { private CommandRunner commandRunner; @Mock + @SuppressWarnings("NullAway.Init") private Command regularCommand; @Mock + @SuppressWarnings("NullAway.Init") private Command anotherCommand; private final Set calls = EnumSet.noneOf(Call.class); @@ -74,7 +77,7 @@ class CommandRunnerTests { } @Override - protected boolean errorMessage(String message) { + protected boolean errorMessage(@Nullable String message) { CommandRunnerTests.this.calls.add(Call.ERROR_MESSAGE); return super.errorMessage(message); } diff --git a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/AbstractHttpClientMockTests.java b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/AbstractHttpClientMockTests.java index 4821cdf07e9..6baa2fe7561 100644 --- a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/AbstractHttpClientMockTests.java +++ b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/AbstractHttpClientMockTests.java @@ -29,6 +29,7 @@ import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.message.BasicHeader; import org.json.JSONException; import org.json.JSONObject; +import org.jspecify.annotations.Nullable; import org.mockito.ArgumentMatcher; import org.springframework.core.io.ClassPathResource; @@ -91,7 +92,7 @@ public abstract class AbstractHttpClientMockTests { given(this.http.executeOpen(any(HttpHost.class), argThat(getForNonMetadata()), isNull())).willReturn(response); } - protected void mockProjectGenerationError(int status, String message) throws IOException, JSONException { + protected void mockProjectGenerationError(int status, @Nullable String message) throws IOException, JSONException { // Required for project generation as the metadata is read first mockSuccessfulMetadataGet(false); ClassicHttpResponse response = mock(ClassicHttpResponse.class); @@ -107,7 +108,7 @@ public abstract class AbstractHttpClientMockTests { given(this.http.executeOpen(any(HttpHost.class), isA(HttpGet.class), isNull())).willReturn(response); } - protected HttpEntity mockHttpEntity(ClassicHttpResponse response, byte[] content, String contentType) { + protected HttpEntity mockHttpEntity(ClassicHttpResponse response, byte[] content, @Nullable String contentType) { try { HttpEntity entity = mock(HttpEntity.class); given(entity.getContent()).willReturn(new ByteArrayInputStream(content)); @@ -126,7 +127,7 @@ public abstract class AbstractHttpClientMockTests { given(response.getCode()).willReturn(status); } - protected void mockHttpHeader(ClassicHttpResponse response, String headerName, String value) { + protected void mockHttpHeader(ClassicHttpResponse response, String headerName, @Nullable String value) { Header header = (value != null) ? new BasicHeader(headerName, value) : null; given(response.getFirstHeader(headerName)).willReturn(header); } @@ -146,7 +147,7 @@ public abstract class AbstractHttpClientMockTests { return "attachment; filename=\"" + fileName + "\""; } - private String createJsonError(int status, String message) throws JSONException { + private String createJsonError(int status, @Nullable String message) throws JSONException { JSONObject json = new JSONObject(); json.put("status", status); if (message != null) { @@ -157,17 +158,17 @@ public abstract class AbstractHttpClientMockTests { static class MockHttpProjectGenerationRequest { - String contentType; + @Nullable String contentType; - String fileName; + @Nullable String fileName; byte[] content = new byte[] { 0, 0, 0, 0 }; - MockHttpProjectGenerationRequest(String contentType, String fileName) { + MockHttpProjectGenerationRequest(@Nullable String contentType, @Nullable String fileName) { this(contentType, fileName, new byte[] { 0, 0, 0, 0 }); } - MockHttpProjectGenerationRequest(String contentType, String fileName, byte[] content) { + MockHttpProjectGenerationRequest(@Nullable String contentType, @Nullable String fileName, byte[] content) { this.contentType = (contentType != null) ? contentType : "application/text"; this.fileName = fileName; this.content = content; diff --git a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java index 635698fcbcf..9399ded93ec 100644 --- a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java +++ b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitCommandTests.java @@ -26,6 +26,7 @@ import java.util.zip.ZipOutputStream; import joptsimple.OptionSet; import org.apache.hc.core5.http.HttpHost; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; @@ -255,6 +256,7 @@ class InitCommandTests extends AbstractHttpClientMockTests { this.command.run("-g=org.demo", "-a=acme", "-v=1.2.3-SNAPSHOT", "-n=acme-sample", "--description=Acme sample project", "--package-name=demo.foo", "-t=ant-project", "--build=grunt", "--format=web", "-p=war", "-j=1.9", "-l=groovy", "-b=1.2.0.RELEASE", "-d=web,data-jpa"); + assertThat(this.handler.lastRequest).isNotNull(); assertThat(this.handler.lastRequest.getGroupId()).isEqualTo("org.demo"); assertThat(this.handler.lastRequest.getArtifactId()).isEqualTo("acme"); assertThat(this.handler.lastRequest.getVersion()).isEqualTo("1.2.3-SNAPSHOT"); @@ -281,6 +283,7 @@ class InitCommandTests extends AbstractHttpClientMockTests { "--description=Acme sample project", "--packageName=demo.foo", "--type=ant-project", "--build=grunt", "--format=web", "--packaging=war", "--javaVersion=1.9", "--language=groovy", "--bootVersion=1.2.0.RELEASE", "--dependencies=web,data-jpa"); + assertThat(this.handler.lastRequest).isNotNull(); assertThat(this.handler.lastRequest.getGroupId()).isEqualTo("org.demo"); assertThat(this.handler.lastRequest.getArtifactId()).isEqualTo("acme"); assertThat(this.handler.lastRequest.getVersion()).isEqualTo("1.2.3-SNAPSHOT"); @@ -307,6 +310,7 @@ class InitCommandTests extends AbstractHttpClientMockTests { "--description=Acme sample project", "--package-name=demo.foo", "--type=ant-project", "--build=grunt", "--format=web", "--packaging=war", "--java-version=1.9", "--language=groovy", "--boot-version=1.2.0.RELEASE", "--dependencies=web,data-jpa"); + assertThat(this.handler.lastRequest).isNotNull(); assertThat(this.handler.lastRequest.getGroupId()).isEqualTo("org.demo"); assertThat(this.handler.lastRequest.getArtifactId()).isEqualTo("acme"); assertThat(this.handler.lastRequest.getVersion()).isEqualTo("1.2.3-SNAPSHOT"); @@ -344,6 +348,7 @@ class InitCommandTests extends AbstractHttpClientMockTests { void parseTypeOnly() throws Exception { this.handler.disableProjectGeneration(); this.command.run("-t=ant-project"); + assertThat(this.handler.lastRequest).isNotNull(); assertThat(this.handler.lastRequest.getBuild()).isEqualTo("gradle"); assertThat(this.handler.lastRequest.getFormat()).isEqualTo("project"); assertThat(this.handler.lastRequest.isDetectType()).isFalse(); @@ -354,6 +359,7 @@ class InitCommandTests extends AbstractHttpClientMockTests { void parseBuildOnly() throws Exception { this.handler.disableProjectGeneration(); this.command.run("--build=ant"); + assertThat(this.handler.lastRequest).isNotNull(); assertThat(this.handler.lastRequest.getBuild()).isEqualTo("ant"); assertThat(this.handler.lastRequest.getFormat()).isEqualTo("project"); assertThat(this.handler.lastRequest.isDetectType()).isTrue(); @@ -364,6 +370,7 @@ class InitCommandTests extends AbstractHttpClientMockTests { void parseFormatOnly() throws Exception { this.handler.disableProjectGeneration(); this.command.run("--format=web"); + assertThat(this.handler.lastRequest).isNotNull(); assertThat(this.handler.lastRequest.getBuild()).isEqualTo("gradle"); assertThat(this.handler.lastRequest.getFormat()).isEqualTo("web"); assertThat(this.handler.lastRequest.isDetectType()).isTrue(); @@ -374,6 +381,7 @@ class InitCommandTests extends AbstractHttpClientMockTests { void parseLocation() throws Exception { this.handler.disableProjectGeneration(); this.command.run("foobar.zip"); + assertThat(this.handler.lastRequest).isNotNull(); assertThat(this.handler.lastRequest.getOutput()).isEqualTo("foobar.zip"); } @@ -381,6 +389,7 @@ class InitCommandTests extends AbstractHttpClientMockTests { void parseLocationWithSlash() throws Exception { this.handler.disableProjectGeneration(); this.command.run("foobar/"); + assertThat(this.handler.lastRequest).isNotNull(); assertThat(this.handler.lastRequest.getOutput()).isEqualTo("foobar"); assertThat(this.handler.lastRequest.isExtract()).isTrue(); } @@ -415,7 +424,7 @@ class InitCommandTests extends AbstractHttpClientMockTests { private boolean disableProjectGeneration; - private ProjectGenerationRequest lastRequest; + private @Nullable ProjectGenerationRequest lastRequest; TestableInitCommandOptionHandler(InitializrService initializrService) { super(initializrService); diff --git a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceMetadataTests.java b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceMetadataTests.java index a13975c3e9b..6394e4420f2 100644 --- a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceMetadataTests.java +++ b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceMetadataTests.java @@ -60,12 +60,22 @@ class InitializrServiceMetadataTests { assertThat(metadata.getDependencies()).hasSize(5); // Security description - assertThat(metadata.getDependency("aop").getName()).isEqualTo("AOP"); - assertThat(metadata.getDependency("security").getName()).isEqualTo("Security"); - assertThat(metadata.getDependency("security").getDescription()).isEqualTo("Security description"); - assertThat(metadata.getDependency("jdbc").getName()).isEqualTo("JDBC"); - assertThat(metadata.getDependency("data-jpa").getName()).isEqualTo("JPA"); - assertThat(metadata.getDependency("data-mongodb").getName()).isEqualTo("MongoDB"); + Dependency aop = metadata.getDependency("aop"); + assertThat(aop).isNotNull(); + assertThat(aop.getName()).isEqualTo("AOP"); + Dependency security = metadata.getDependency("security"); + assertThat(security).isNotNull(); + assertThat(security.getName()).isEqualTo("Security"); + assertThat(security.getDescription()).isEqualTo("Security description"); + Dependency jdbc = metadata.getDependency("jdbc"); + assertThat(jdbc).isNotNull(); + assertThat(jdbc.getName()).isEqualTo("JDBC"); + Dependency dataJpa = metadata.getDependency("data-jpa"); + assertThat(dataJpa).isNotNull(); + assertThat(dataJpa.getName()).isEqualTo("JPA"); + Dependency dataMongodb = metadata.getDependency("data-mongodb"); + assertThat(dataMongodb).isNotNull(); + assertThat(dataMongodb.getName()).isEqualTo("MongoDB"); } @Test diff --git a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceTests.java b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceTests.java index 300bdf25698..e5b4ea7e889 100644 --- a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceTests.java +++ b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/InitializrServiceTests.java @@ -19,6 +19,7 @@ package org.springframework.boot.cli.command.init; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpHost; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -140,7 +141,8 @@ class InitializrServiceTests extends AbstractHttpClientMockTests { return entity; } - private static void assertProjectEntity(ProjectGenerationResponse entity, String mimeType, String fileName) { + private static void assertProjectEntity(ProjectGenerationResponse entity, @Nullable String mimeType, + @Nullable String fileName) { if (mimeType == null) { assertThat(entity.getContentType()).isNull(); } diff --git a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/ProjectGenerationRequestTests.java b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/ProjectGenerationRequestTests.java index 4489fa4d4c2..9614a1106a6 100644 --- a/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/ProjectGenerationRequestTests.java +++ b/cli/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/init/ProjectGenerationRequestTests.java @@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.boot.cli.json.JSONException; @@ -219,7 +220,7 @@ class ProjectGenerationRequestTests { return createUrl("/starter.zip" + param); } - void setBuildAndFormat(String build, String format) { + void setBuildAndFormat(@Nullable String build, @Nullable String format) { this.request.setBuild((build != null) ? build : "maven"); this.request.setFormat((format != null) ? format : "project"); this.request.setDetectType(true);