Add nullability annotations to tests in buildpack/spring-boot-buildpack-platform

See gh-47263
This commit is contained in:
Moritz Halbritter
2025-10-16 11:24:51 +02:00
parent c2286abdf9
commit 2850ba06c8
47 changed files with 244 additions and 51 deletions
@@ -37,3 +37,11 @@ dependencies {
testImplementation(project(":test-support:spring-boot-test-support"))
}
tasks.named("compileTestJava") {
options.nullability.checking = "tests"
}
tasks.named("compileDockerTestJava") {
options.nullability.checking = "tests"
}
@@ -19,6 +19,7 @@ package org.springframework.boot.buildpack.platform.build;
import java.util.LinkedHashMap;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -35,7 +36,7 @@ class BuildOwnerTests {
@Test
void fromEnvReturnsOwner() {
Map<String, String> env = new LinkedHashMap<>();
Map<String, @Nullable String> env = new LinkedHashMap<>();
env.put("CNB_USER_ID", "123");
env.put("CNB_GROUP_ID", "456");
BuildOwner owner = BuildOwner.fromEnv(env);
@@ -45,6 +46,7 @@ class BuildOwnerTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void fromEnvWhenEnvIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuildOwner.fromEnv(null))
.withMessage("'env' must not be null");
@@ -52,7 +54,7 @@ class BuildOwnerTests {
@Test
void fromEnvWhenUserPropertyIsMissingThrowsException() {
Map<String, String> env = new LinkedHashMap<>();
Map<String, @Nullable String> env = new LinkedHashMap<>();
env.put("CNB_GROUP_ID", "456");
assertThatIllegalStateException().isThrownBy(() -> BuildOwner.fromEnv(env))
.withMessage("Missing 'CNB_USER_ID' value from the builder environment '" + env + "'");
@@ -60,7 +62,7 @@ class BuildOwnerTests {
@Test
void fromEnvWhenGroupPropertyIsMissingThrowsException() {
Map<String, String> env = new LinkedHashMap<>();
Map<String, @Nullable String> env = new LinkedHashMap<>();
env.put("CNB_USER_ID", "123");
assertThatIllegalStateException().isThrownBy(() -> BuildOwner.fromEnv(env))
.withMessage("Missing 'CNB_GROUP_ID' value from the builder environment '" + env + "'");
@@ -68,7 +70,7 @@ class BuildOwnerTests {
@Test
void fromEnvWhenUserPropertyIsMalformedThrowsException() {
Map<String, String> env = new LinkedHashMap<>();
Map<String, @Nullable String> env = new LinkedHashMap<>();
env.put("CNB_USER_ID", "nope");
env.put("CNB_GROUP_ID", "456");
assertThatIllegalStateException().isThrownBy(() -> BuildOwner.fromEnv(env))
@@ -77,7 +79,7 @@ class BuildOwnerTests {
@Test
void fromEnvWhenGroupPropertyIsMalformedThrowsException() {
Map<String, String> env = new LinkedHashMap<>();
Map<String, @Nullable String> env = new LinkedHashMap<>();
env.put("CNB_USER_ID", "123");
env.put("CNB_GROUP_ID", "nope");
assertThatIllegalStateException().isThrownBy(() -> BuildOwner.fromEnv(env))
@@ -61,6 +61,7 @@ class BuildRequestTests {
private static final ZoneId UTC = ZoneId.of("UTC");
@TempDir
@SuppressWarnings("NullAway.Init")
File tempDir;
@Test
@@ -86,6 +87,7 @@ class BuildRequestTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void forJarFileWhenJarFileIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuildRequest.forJarFile(null))
.withMessage("'jarFile' must not be null");
@@ -214,6 +216,7 @@ class BuildRequestTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void withEnvWhenKeyIsNullThrowsException() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
assertThatIllegalArgumentException().isThrownBy(() -> request.withEnv(null, "test"))
@@ -221,6 +224,7 @@ class BuildRequestTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void withEnvWhenValueIsNullThrowsException() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
assertThatIllegalArgumentException().isThrownBy(() -> request.withEnv("test", null))
@@ -238,6 +242,7 @@ class BuildRequestTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void withBuildpacksWhenBuildpacksIsNullThrowsException() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
assertThatIllegalArgumentException().isThrownBy(() -> request.withBuildpacks((List<BuildpackReference>) null))
@@ -255,6 +260,7 @@ class BuildRequestTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void withBindingsWhenBindingsIsNullThrowsException() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
assertThatIllegalArgumentException().isThrownBy(() -> request.withBindings((List<Binding>) null))
@@ -280,6 +286,7 @@ class BuildRequestTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void withTagsWhenTagsIsNullThrowsException() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
assertThatIllegalArgumentException().isThrownBy(() -> request.withTags((List<ImageReference>) null))
@@ -319,6 +326,7 @@ class BuildRequestTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void withBuildVolumeCacheWhenCacheIsNullThrowsException() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
assertThatIllegalArgumentException().isThrownBy(() -> request.withBuildCache(null))
@@ -342,6 +350,7 @@ class BuildRequestTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void withLaunchVolumeCacheWhenCacheIsNullThrowsException() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
assertThatIllegalArgumentException().isThrownBy(() -> request.withLaunchCache(null))
@@ -51,6 +51,7 @@ class BuilderBuildpackTests extends AbstractJsonTests {
void resolveWhenFullyQualifiedBuildpackWithVersionResolves() throws Exception {
BuildpackReference reference = BuildpackReference.of("urn:cnb:builder:paketo-buildpacks/spring-boot@3.5.0");
Buildpack buildpack = BuilderBuildpack.resolve(this.resolverContext, reference);
assertThat(buildpack).isNotNull();
assertThat(buildpack.getCoordinates())
.isEqualTo(BuildpackCoordinates.of("paketo-buildpacks/spring-boot", "3.5.0"));
assertThatNoLayersAreAdded(buildpack);
@@ -60,6 +61,7 @@ class BuilderBuildpackTests extends AbstractJsonTests {
void resolveWhenFullyQualifiedBuildpackWithoutVersionResolves() throws Exception {
BuildpackReference reference = BuildpackReference.of("urn:cnb:builder:paketo-buildpacks/spring-boot");
Buildpack buildpack = BuilderBuildpack.resolve(this.resolverContext, reference);
assertThat(buildpack).isNotNull();
assertThat(buildpack.getCoordinates())
.isEqualTo(BuildpackCoordinates.of("paketo-buildpacks/spring-boot", "3.5.0"));
assertThatNoLayersAreAdded(buildpack);
@@ -69,6 +71,7 @@ class BuilderBuildpackTests extends AbstractJsonTests {
void resolveWhenUnqualifiedBuildpackWithVersionResolves() throws Exception {
BuildpackReference reference = BuildpackReference.of("paketo-buildpacks/spring-boot@3.5.0");
Buildpack buildpack = BuilderBuildpack.resolve(this.resolverContext, reference);
assertThat(buildpack).isNotNull();
assertThat(buildpack.getCoordinates())
.isEqualTo(BuildpackCoordinates.of("paketo-buildpacks/spring-boot", "3.5.0"));
assertThatNoLayersAreAdded(buildpack);
@@ -78,6 +81,7 @@ class BuilderBuildpackTests extends AbstractJsonTests {
void resolveWhenUnqualifiedBuildpackWithoutVersionResolves() throws Exception {
BuildpackReference reference = BuildpackReference.of("paketo-buildpacks/spring-boot");
Buildpack buildpack = BuilderBuildpack.resolve(this.resolverContext, reference);
assertThat(buildpack).isNotNull();
assertThat(buildpack.getCoordinates())
.isEqualTo(BuildpackCoordinates.of("paketo-buildpacks/spring-boot", "3.5.0"));
assertThatNoLayersAreAdded(buildpack);
@@ -88,6 +88,7 @@ class BuilderMetadataTests extends AbstractJsonTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void fromImageWhenImageIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuilderMetadata.fromImage(null))
.withMessage("'image' must not be null");
@@ -150,6 +151,7 @@ class BuilderMetadataTests extends AbstractJsonTests {
BuilderMetadata metadata = BuilderMetadata.fromImage(image);
ImageConfig imageConfigCopy = imageConfig.copy(metadata::attachTo);
String label = imageConfigCopy.getLabels().get("io.buildpacks.builder.metadata");
assertThat(label).isNotNull();
BuilderMetadata metadataCopy = BuilderMetadata.fromJson(label);
assertThat(metadataCopy.getStack().getRunImage().getImage())
.isEqualTo(metadata.getStack().getRunImage().getImage());
@@ -20,7 +20,9 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URI;
import java.util.Objects;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.stubbing.Answer;
@@ -77,6 +79,7 @@ class BuilderTests {
private static final ImageReference BASE_CNB = ImageReference.of("docker.io/cloudfoundry/run:base-cnb");
@Test
@SuppressWarnings("NullAway") // Test null check
void createWhenLogIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new Builder((BuildLog) null))
.withMessage("'log' must not be null");
@@ -106,6 +109,7 @@ class BuilderTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void buildWhenRequestIsNullThrowsException() {
Builder builder = new Builder();
assertThatIllegalArgumentException().isThrownBy(() -> builder.build(null))
@@ -131,7 +135,9 @@ class BuilderTests {
then(docker.image()).should().pull(eq(DEFAULT_BUILDER), isNull(), any(), isNull());
then(docker.image()).should().pull(eq(BASE_CNB), eq(ImagePlatform.from(builderImage)), any(), isNull());
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
then(docker.image()).shouldHaveNoMoreInteractions();
}
@@ -161,7 +167,9 @@ class BuilderTests {
.pull(eq(BASE_CNB), eq(ImagePlatform.from(builderImage)), any(), regAuthEq(builderToken));
then(docker.image()).should().push(eq(request.getName()), any(), regAuthEq(publishToken));
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
then(docker.image()).shouldHaveNoMoreInteractions();
}
@@ -184,7 +192,9 @@ class BuilderTests {
assertThat(out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
}
@Test
@@ -207,7 +217,9 @@ class BuilderTests {
assertThat(out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
}
@Test
@@ -227,7 +239,9 @@ class BuilderTests {
assertThat(out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
}
@Test
@@ -249,7 +263,9 @@ class BuilderTests {
assertThat(out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
}
@Test
@@ -271,7 +287,9 @@ class BuilderTests {
assertThat(out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
then(docker.image()).should(never()).pull(any(), any(), any());
then(docker.image()).should(times(2)).inspect(any());
}
@@ -295,7 +313,9 @@ class BuilderTests {
assertThat(out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
then(docker.image()).should(times(2)).pull(any(), any(), any(), isNull());
then(docker.image()).should(never()).inspect(any());
}
@@ -325,7 +345,9 @@ class BuilderTests {
assertThat(out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
then(docker.image()).should(times(2)).inspect(any());
then(docker.image()).should(times(2)).pull(any(), any(), any(), isNull());
}
@@ -349,7 +371,9 @@ class BuilderTests {
then(docker.image()).should().tag(eq(request.getName()), eq(ImageReference.of("my-application:1.2.3")));
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
}
@Test
@@ -385,7 +409,9 @@ class BuilderTests {
then(docker.image()).should().push(eq(builtImageReference), any(), regAuthEq(publishToken));
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
then(docker.image()).shouldHaveNoMoreInteractions();
}
@@ -408,7 +434,9 @@ class BuilderTests {
then(docker.image()).should().pull(eq(DEFAULT_BUILDER), eq(platform), any(), isNull());
then(docker.image()).should().pull(eq(BASE_CNB), eq(platform), any(), isNull());
then(docker.image()).should().load(archive.capture(), any());
then(docker.image()).should().remove(archive.getValue().getTag(), true);
ImageReference tag = archive.getValue().getTag();
assertThat(tag).isNotNull();
then(docker.image()).should().remove(tag, true);
then(docker.image()).shouldHaveNoMoreInteractions();
}
@@ -483,7 +511,7 @@ class BuilderTests {
return mockDockerApi(null);
}
private DockerApi mockDockerApi(ImagePlatform platform) throws IOException {
private DockerApi mockDockerApi(@Nullable ImagePlatform platform) throws IOException {
ContainerApi containerApi = mock(ContainerApi.class);
ContainerReference reference = ContainerReference.of("container-ref");
given(containerApi.create(any(), eq(platform), any())).willReturn(reference);
@@ -531,7 +559,7 @@ class BuilderTests {
}
private static String regAuthEq(DockerRegistryAuthentication authentication) {
return argThat(authentication.getAuthHeader()::equals);
return argThat((arg) -> Objects.equals(authentication.getAuthHeader(), arg));
}
static class TestPrintStream extends PrintStream {
@@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
@@ -97,6 +98,7 @@ class BuildpackCoordinatesTests extends AbstractJsonTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void fromBuildpackMetadataWhenMetadataIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackCoordinates.fromBuildpackMetadata(null))
.withMessage("'buildpackMetadata' must not be null");
@@ -111,6 +113,7 @@ class BuildpackCoordinatesTests extends AbstractJsonTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenIdIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackCoordinates.of(null, null))
.withMessage("'id' must not be empty");
@@ -155,7 +158,8 @@ class BuildpackCoordinatesTests extends AbstractJsonTests {
assertThat(c1a).hasSameHashCodeAs(c1b);
}
private InputStream createTomlStream(String id, String version, boolean includeStacks, boolean includeOrder) {
private InputStream createTomlStream(@Nullable String id, @Nullable String version, boolean includeStacks,
boolean includeOrder) {
StringBuilder builder = new StringBuilder();
builder.append("[buildpack]\n");
if (id != null) {
@@ -53,6 +53,7 @@ class BuildpackLayersMetadataTests extends AbstractJsonTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void fromImageWhenImageIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackLayersMetadata.fromImage(null))
.withMessage("'image' must not be null");
@@ -47,6 +47,7 @@ class BuildpackMetadataTests extends AbstractJsonTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void fromImageWhenImageIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackMetadata.fromImage(null))
.withMessage("'image' must not be null");
@@ -50,6 +50,7 @@ import static org.mockito.Mockito.mock;
class DirectoryBuildpackTests {
@TempDir
@SuppressWarnings("NullAway.Init")
File temp;
private File buildpackDir;
@@ -35,6 +35,7 @@ import java.util.Map;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -61,6 +62,7 @@ class EphemeralBuilderTests extends AbstractJsonTests {
private static final int EXISTING_IMAGE_LAYER_COUNT = 43;
@TempDir
@SuppressWarnings("NullAway.Init")
File temp;
private final BuildOwner owner = BuildOwner.of(123, 456);
@@ -73,7 +75,7 @@ class EphemeralBuilderTests extends AbstractJsonTests {
private Map<String, String> env;
private Buildpacks buildpacks;
private @Nullable Buildpacks buildpacks;
private final Creator creator = Creator.withVersion("dev");
@@ -112,6 +114,7 @@ class EphemeralBuilderTests extends AbstractJsonTests {
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata,
this.creator, this.env, this.buildpacks);
ImageReference tag = builder.getArchive(null).getTag();
assertThat(tag).isNotNull();
assertThat(tag.toString()).startsWith("pack.local/builder/").endsWith(":latest");
}
@@ -30,6 +30,7 @@ import java.util.Random;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.invocation.InvocationOnMock;
@@ -79,6 +80,7 @@ class ImageBuildpackTests extends AbstractJsonTests {
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(eq(imageReference), any());
BuildpackReference reference = BuildpackReference.of("docker://example/buildpack1:1.0.0");
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
assertThat(buildpack).isNotNull();
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
assertAppliesExpectedLayers(buildpack);
}
@@ -93,6 +95,7 @@ class ImageBuildpackTests extends AbstractJsonTests {
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(eq(imageReference), any());
BuildpackReference reference = BuildpackReference.of("example/buildpack1:1.0.0");
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
assertThat(buildpack).isNotNull();
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
assertAppliesExpectedLayers(buildpack);
}
@@ -107,6 +110,7 @@ class ImageBuildpackTests extends AbstractJsonTests {
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(eq(imageReference), any());
BuildpackReference reference = BuildpackReference.of("example/buildpack1");
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
assertThat(buildpack).isNotNull();
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
assertAppliesExpectedLayers(buildpack);
}
@@ -122,6 +126,7 @@ class ImageBuildpackTests extends AbstractJsonTests {
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(eq(imageReference), any());
BuildpackReference reference = BuildpackReference.of("example/buildpack1@" + digest);
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
assertThat(buildpack).isNotNull();
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
assertAppliesExpectedLayers(buildpack);
}
@@ -137,6 +142,7 @@ class ImageBuildpackTests extends AbstractJsonTests {
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(eq(imageReference), any());
BuildpackReference reference = BuildpackReference.of("docker://example/buildpack1:1.0.0");
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
assertThat(buildpack).isNotNull();
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
assertAppliesNoLayers(buildpack);
}
@@ -177,7 +183,7 @@ class ImageBuildpackTests extends AbstractJsonTests {
assertThat(buildpack).isNull();
}
private Object withMockLayers(InvocationOnMock invocation) {
private @Nullable Object withMockLayers(InvocationOnMock invocation) {
try {
IOBiConsumer<String, TarArchive> consumer = invocation.getArgument(1);
File tarFile = File.createTempFile("create-builder-test-", null);
@@ -29,6 +29,7 @@ import java.util.Map;
import com.sun.jna.Platform;
import org.json.JSONException;
import org.json.JSONObject;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@@ -500,7 +501,9 @@ class LifecycleTests {
then(this.docker.container()).should().start(containerReference);
then(this.docker.container()).should().logs(eq(containerReference), any());
then(this.docker.container()).should().remove(containerReference, true);
configConsumer.accept(this.configs.get(containerReference.toString()));
ContainerConfig containerConfig = this.configs.get(containerReference.toString());
assertThat(containerConfig).isNotNull();
configConsumer.accept(containerConfig);
}
private IOConsumer<ContainerConfig> withExpectedConfig(String name) {
@@ -527,7 +530,7 @@ class LifecycleTests {
static class TestLifecycle extends Lifecycle {
TestLifecycle(BuildLog log, DockerApi docker, ResolvedDockerHost dockerHost, BuildRequest request,
TestLifecycle(BuildLog log, DockerApi docker, @Nullable ResolvedDockerHost dockerHost, BuildRequest request,
EphemeralBuilder builder) {
super(log, docker, dockerHost, request, builder);
}
@@ -29,6 +29,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
class LifecycleVersionTests {
@Test
@SuppressWarnings("NullAway") // Test null check
void parseWhenValueIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> LifecycleVersion.parse(null))
.withMessage("'value' must not be empty");
@@ -36,6 +36,7 @@ import static org.mockito.Mockito.mock;
class StackIdTests {
@Test
@SuppressWarnings("NullAway") // Test null check
void fromImageWhenImageIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> StackId.fromImage(null))
.withMessage("'image' must not be null");
@@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.build;
import java.io.IOException;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.buildpack.platform.docker.type.Layer;
import org.springframework.boot.buildpack.platform.io.Content;
import org.springframework.boot.buildpack.platform.io.IOConsumer;
@@ -34,7 +36,7 @@ class TestBuildpack implements Buildpack {
private final BuildpackCoordinates coordinates;
TestBuildpack(String id, String version) {
TestBuildpack(String id, @Nullable String version) {
this.coordinates = BuildpackCoordinates.of(id, version);
}
@@ -32,6 +32,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
class ApiVersionTests {
@Test
@SuppressWarnings("NullAway") // Test null check
void parseWhenVersionIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ApiVersion.parse(null))
.withMessage("'value' must not be empty");
@@ -29,6 +29,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.message.BasicHeader;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -102,6 +103,7 @@ class DockerApiTests {
private static final String VOLUMES_URL = API_URL + "/volumes";
@Mock
@SuppressWarnings("NullAway.Init")
private HttpTransport http;
private DockerApi dockerApi;
@@ -119,7 +121,7 @@ class DockerApiTests {
return responseOf(null);
}
private Response responseOf(String name) {
private Response responseOf(@Nullable String name) {
return new Response() {
@Override
@@ -129,7 +131,7 @@ class DockerApiTests {
@Override
public InputStream getContent() {
if (name == null) {
return null;
return new ByteArrayInputStream(new byte[0]);
}
return getClass().getResourceAsStream(name);
}
@@ -142,11 +144,11 @@ class DockerApiTests {
@Override
public InputStream getContent() {
return null;
return new ByteArrayInputStream(new byte[0]);
}
@Override
public Header getHeader(String name) {
public @Nullable Header getHeader(String name) {
return Arrays.stream(headers)
.filter((header) -> header.getName().equals(name))
.findFirst()
@@ -172,15 +174,19 @@ class DockerApiTests {
private ImageApi api;
@Mock
@SuppressWarnings("NullAway.Init")
private UpdateListener<PullImageUpdateEvent> pullListener;
@Mock
@SuppressWarnings("NullAway.Init")
private UpdateListener<PushImageUpdateEvent> pushListener;
@Mock
@SuppressWarnings("NullAway.Init")
private UpdateListener<LoadImageUpdateEvent> loadListener;
@Captor
@SuppressWarnings("NullAway.Init")
private ArgumentCaptor<IOConsumer<OutputStream>> writer;
@BeforeEach
@@ -189,12 +195,14 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void pullWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.pull(null, null, this.pullListener))
.withMessage("'reference' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void pullWhenListenerIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> this.api.pull(ImageReference.of("ubuntu"), null, null))
@@ -262,12 +270,14 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void pushWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.push(null, this.pushListener, null))
.withMessage("'reference' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void pushWhenListenerIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> this.api.push(ImageReference.of("ubuntu"), null, null))
@@ -297,12 +307,14 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void loadWhenArchiveIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.load(null, UpdateListener.none()))
.withMessage("'archive' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void loadWhenListenerIsNullThrowsException() {
ImageArchive archive = mock(ImageArchive.class);
assertThatIllegalArgumentException().isThrownBy(() -> this.api.load(archive, null))
@@ -347,6 +359,7 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void removeWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.remove(null, true))
.withMessage("'reference' must not be null");
@@ -375,6 +388,7 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void inspectWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.inspect(null))
.withMessage("'reference' must not be null");
@@ -446,6 +460,7 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void tagWhenReferenceIsNullThrowsException() {
ImageReference tag = ImageReference.of("localhost:5000/ubuntu");
assertThatIllegalArgumentException().isThrownBy(() -> this.api.tag(null, tag))
@@ -453,6 +468,7 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void tagWhenTargetIsNullThrowsException() {
ImageReference reference = ImageReference.of("localhost:5000/ubuntu");
assertThatIllegalArgumentException().isThrownBy(() -> this.api.tag(reference, null))
@@ -487,9 +503,11 @@ class DockerApiTests {
private ContainerApi api;
@Captor
@SuppressWarnings("NullAway.Init")
private ArgumentCaptor<IOConsumer<OutputStream>> writer;
@Mock
@SuppressWarnings("NullAway.Init")
private UpdateListener<LogUpdateEvent> logListener;
@BeforeEach
@@ -498,6 +516,7 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void createWhenConfigIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.create(null, null))
.withMessage("'config' must not be null");
@@ -553,7 +572,7 @@ class DockerApiTests {
createWithPlatform(null);
}
private void createWithPlatform(String apiVersion) throws IOException, URISyntaxException {
private void createWithPlatform(@Nullable String apiVersion) throws IOException, URISyntaxException {
ImageReference imageReference = ImageReference.of("ubuntu:bionic");
ContainerConfig config = ContainerConfig.of(imageReference, (update) -> update.withCommand("/bin/bash"));
ImagePlatform platform = ImagePlatform.of("linux/arm64/v1");
@@ -585,6 +604,7 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void startWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.start(null))
.withMessage("'reference' must not be null");
@@ -600,12 +620,14 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void logsWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.logs(null, UpdateListener.none()))
.withMessage("'reference' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void logsWhenListenerIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> this.api.logs(ContainerReference.of("e90e34656806"), null))
@@ -625,6 +647,7 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void waitWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.wait(null))
.withMessage("'reference' must not be null");
@@ -640,6 +663,7 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void removeWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.remove(null, true))
.withMessage("'reference' must not be null");
@@ -676,6 +700,7 @@ class DockerApiTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void deleteWhenNameIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.delete(null, false))
.withMessage("'name' must not be null");
@@ -41,13 +41,17 @@ abstract class ProgressUpdateEventTests<E extends ProgressUpdateEvent> {
@Test
void getProgressDetailReturnsProgressDetails() {
ProgressUpdateEvent event = createEvent();
assertThat(event.getProgressDetail().asPercentage()).isEqualTo(50);
ProgressDetail progressDetail = event.getProgressDetail();
assertThat(progressDetail).isNotNull();
assertThat(progressDetail.asPercentage()).isEqualTo(50);
}
@Test
void getProgressDetailReturnsProgressDetailsForLongNumbers() {
ProgressUpdateEvent event = createEvent("status", new ProgressDetail(4000000000L, 8000000000L), "progress");
assertThat(event.getProgressDetail().asPercentage()).isEqualTo(50);
ProgressDetail progressDetail = event.getProgressDetail();
assertThat(progressDetail).isNotNull();
assertThat(progressDetail.asPercentage()).isEqualTo(50);
}
@Test
@@ -18,6 +18,7 @@ package org.springframework.boot.buildpack.platform.docker;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.ProgressUpdateEvent.ProgressDetail;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import static org.assertj.core.api.Assertions.assertThat;
@@ -36,7 +37,9 @@ class PullUpdateEventTests extends AbstractJsonTests {
PullImageUpdateEvent.class);
assertThat(event.getId()).isEqualTo("4f4fb700ef54");
assertThat(event.getStatus()).isEqualTo("Extracting");
assertThat(event.getProgressDetail().asPercentage()).isEqualTo(50);
ProgressDetail progressDetail = event.getProgressDetail();
assertThat(progressDetail).isNotNull();
assertThat(progressDetail.asPercentage()).isEqualTo(50);
assertThat(event.getProgress()).isEqualTo("[==================================================>] 32B/32B");
}
@@ -19,6 +19,7 @@ package org.springframework.boot.buildpack.platform.docker;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.ProgressUpdateEvent.ProgressDetail;
import org.springframework.boot.buildpack.platform.docker.PushImageUpdateEvent.ErrorDetail;
import static org.assertj.core.api.Assertions.assertThat;
@@ -37,9 +38,11 @@ class PushImageUpdateEventTests extends ProgressUpdateEventTests<PushImageUpdate
@Test
void getErrorReturnsErrorDetail() {
PushImageUpdateEvent event = new PushImageUpdateEvent(null, null, null, null,
new PushImageUpdateEvent.ErrorDetail("test message"));
assertThat(event.getErrorDetail().getMessage()).isEqualTo("test message");
PushImageUpdateEvent event = new PushImageUpdateEvent("id", "status", new ProgressDetail(null, null),
"progress", new PushImageUpdateEvent.ErrorDetail("test message"));
ErrorDetail errorDetail = event.getErrorDetail();
assertThat(errorDetail).isNotNull();
assertThat(errorDetail.getMessage()).isEqualTo("test message");
}
@Override
@@ -19,6 +19,7 @@ package org.springframework.boot.buildpack.platform.docker.configuration;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -140,7 +141,9 @@ class DockerConfigurationMetadataTests extends AbstractJsonTests {
private String pathToResource(String resource) throws URISyntaxException {
URL url = getClass().getResource(resource);
return Paths.get(url.toURI()).getParent().toAbsolutePath().toString();
Path parent = Paths.get(url.toURI()).getParent();
assertThat(parent).isNotNull();
return parent.toAbsolutePath().toString();
}
}
@@ -23,7 +23,9 @@ import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -78,6 +80,7 @@ class DockerRegistryConfigAuthenticationTests {
this.environment.put("DOCKER_CONFIG", directory.toString());
ImageReference imageReference = ImageReference.of("docker.io/ubuntu:latest");
String authHeader = getAuthHeader(imageReference);
assertThat(authHeader).isNotNull();
assertThat(decode(authHeader)).hasSize(4)
.containsEntry("serveraddress", "https://index.docker.io/v1/")
.containsEntry("username", "username")
@@ -100,6 +103,7 @@ class DockerRegistryConfigAuthenticationTests {
this.environment.put("DOCKER_CONFIG", directory.toString());
ImageReference imageReference = ImageReference.of("index.docker.io/ubuntu:latest");
String authHeader = getAuthHeader(imageReference);
assertThat(authHeader).isNotNull();
assertThat(decode(authHeader)).hasSize(4)
.containsEntry("serveraddress", "https://index.docker.io/v1/")
.containsEntry("username", "username")
@@ -121,6 +125,7 @@ class DockerRegistryConfigAuthenticationTests {
this.environment.put("DOCKER_CONFIG", directory.toString());
ImageReference imageReference = ImageReference.of("my-registry.example.com/ubuntu:latest");
String authHeader = getAuthHeader(imageReference);
assertThat(authHeader).isNotNull();
assertThat(decode(authHeader)).hasSize(4)
.containsEntry("serveraddress", "my-registry.example.com")
.containsEntry("username", "customUser")
@@ -142,6 +147,7 @@ class DockerRegistryConfigAuthenticationTests {
this.environment.put("DOCKER_CONFIG", directory.toString());
ImageReference imageReference = ImageReference.of("my-registry.example.com/ubuntu:latest");
String authHeader = getAuthHeader(imageReference);
assertThat(authHeader).isNotNull();
assertThat(decode(authHeader)).hasSize(4)
.containsEntry("serveraddress", "https://my-registry.example.com")
.containsEntry("username", "customUser")
@@ -158,6 +164,7 @@ class DockerRegistryConfigAuthenticationTests {
this.environment.put("DOCKER_CONFIG", directory.toString());
ImageReference imageReference = ImageReference.of("docker.io/ubuntu:latest");
String authHeader = getAuthHeader(imageReference, DockerRegistryAuthentication.EMPTY_USER);
assertThat(authHeader).isNotNull();
assertThat(decode(authHeader)).hasSize(4)
.containsEntry("serveraddress", "")
.containsEntry("username", "")
@@ -183,6 +190,7 @@ class DockerRegistryConfigAuthenticationTests {
ImageReference imageReference = ImageReference.of("docker.io/ubuntu:latest");
mockHelper("desktop", "https://index.docker.io/v1/", "credentials.json");
String authHeader = getAuthHeader(imageReference);
assertThat(authHeader).isNotNull();
assertThat(decode(authHeader)).hasSize(1).containsEntry("identitytoken", "secret");
}
@@ -212,6 +220,7 @@ class DockerRegistryConfigAuthenticationTests {
ImageReference imageReference = ImageReference.of("gcr.io/ubuntu:latest");
mockHelper("gcr", "gcr.io", "credentials.json");
String authHeader = getAuthHeader(imageReference);
assertThat(authHeader).isNotNull();
assertThat(decode(authHeader)).hasSize(4)
.containsEntry("serveraddress", "https://my-gcr.io")
.containsEntry("username", "username")
@@ -240,6 +249,7 @@ class DockerRegistryConfigAuthenticationTests {
ImageReference imageReference = ImageReference.of("gcr.io/ubuntu:latest");
mockHelper("gcr", "gcr.io", "credentials.json");
String authHeader = getAuthHeader(imageReference);
assertThat(authHeader).isNotNull();
assertThat(decode(authHeader)).hasSize(4)
.containsEntry("serveraddress", "gcr.io")
.containsEntry("username", "username")
@@ -269,6 +279,7 @@ class DockerRegistryConfigAuthenticationTests {
CredentialHelper helper = mockHelper("gcr");
given(helper.get("gcr.io")).willThrow(new IOException("Failed to obtain credentials for registry"));
String authHeader = getAuthHeader(imageReference);
assertThat(authHeader).isNotNull();
assertThat(decode(authHeader)).hasSize(4)
.containsEntry("serveraddress", "gcr.io")
.containsEntry("username", "username")
@@ -295,6 +306,7 @@ class DockerRegistryConfigAuthenticationTests {
CredentialHelper helper = mockHelper("gcr");
given(helper.get("gcr.io")).willThrow(new IOException("Failed to obtain credentials for registry"));
String authHeader = getAuthHeader(imageReference, DockerRegistryAuthentication.EMPTY_USER);
assertThat(authHeader).isNotNull();
assertThat(this.helperExceptions).hasSize(1);
assertThat(this.helperExceptions.keySet().iterator().next())
.contains("Error retrieving credentials for 'gcr.io' due to: Failed to obtain credentials for registry");
@@ -320,6 +332,7 @@ class DockerRegistryConfigAuthenticationTests {
ImageReference imageReference = ImageReference.of("gcr.io/ubuntu:latest");
CredentialHelper desktopHelper = mockHelper("desktop");
String authHeader = getAuthHeader(imageReference, DockerRegistryAuthentication.EMPTY_USER);
assertThat(authHeader).isNotNull();
// The Docker CLI appears to prioritize the credential helper over the
// credential store, even when the helper is empty.
assertThat(decode(authHeader)).hasSize(4)
@@ -340,6 +353,7 @@ class DockerRegistryConfigAuthenticationTests {
this.environment.put("DOCKER_CONFIG", directory.toString());
CredentialHelper desktopHelper = mockHelper("desktop");
String authHeader = getAuthHeader(null, DockerRegistryAuthentication.EMPTY_USER);
assertThat(authHeader).isNotNull();
assertThat(decode(authHeader)).hasSize(4)
.containsEntry("serveraddress", "")
.containsEntry("username", "")
@@ -371,6 +385,7 @@ class DockerRegistryConfigAuthenticationTests {
mockHelper("desktop", "my-registry.example.com", "credentials.json");
ImageReference imageReference = ImageReference.of("my-registry.example.com/ubuntu:latest");
String authHeader = getAuthHeader(imageReference);
assertThat(authHeader).isNotNull();
assertThat(decode(authHeader)).hasSize(4)
.containsEntry("serveraddress", "my-registry.example.com")
.containsEntry("username", "username")
@@ -378,18 +393,20 @@ class DockerRegistryConfigAuthenticationTests {
.containsEntry("email", "test@example.com");
}
private String getAuthHeader(ImageReference imageReference) {
private @Nullable String getAuthHeader(@Nullable ImageReference imageReference) {
return getAuthHeader(imageReference, null);
}
private String getAuthHeader(ImageReference imageReference, DockerRegistryAuthentication fallback) {
private @Nullable String getAuthHeader(@Nullable ImageReference imageReference,
@Nullable DockerRegistryAuthentication fallback) {
DockerRegistryConfigAuthentication authentication = getAuthentication(fallback);
return authentication.getAuthHeader(imageReference);
}
private DockerRegistryConfigAuthentication getAuthentication(DockerRegistryAuthentication fallback) {
private DockerRegistryConfigAuthentication getAuthentication(@Nullable DockerRegistryAuthentication fallback) {
Function<String, @Nullable CredentialHelper> credentialHelperFactory = this.credentialHelpers::get;
return new DockerRegistryConfigAuthentication(fallback, this.helperExceptions::put, this.environment::get,
this.credentialHelpers::get);
credentialHelperFactory);
}
private void mockHelper(String name, String serverUrl, String credentialsResourceName) throws Exception {
@@ -409,8 +426,7 @@ class DockerRegistryConfigAuthenticationTests {
}
}
private Map<String, String> decode(String authHeader) throws Exception {
assertThat(authHeader).isNotNull();
private Map<String, String> decode(String authHeader) {
return SharedJsonMapper.get().readValue(Base64.getDecoder().decode(authHeader), new TypeReference<>() {
});
}
@@ -27,6 +27,8 @@ import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import org.springframework.util.StreamUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DockerRegistryUserAuthentication}.
*
@@ -38,13 +40,17 @@ class DockerRegistryUserAuthenticationTests extends AbstractJsonTests {
void createMinimalAuthHeaderReturnsEncodedHeader() throws IOException, JSONException {
DockerRegistryUserAuthentication auth = new DockerRegistryUserAuthentication("user", "secret",
"https://docker.example.com", "docker@example.com");
JSONAssert.assertEquals(jsonContent("auth-user-full.json"), decoded(auth.getAuthHeader()), true);
String authHeader = auth.getAuthHeader();
assertThat(authHeader).isNotNull();
JSONAssert.assertEquals(jsonContent("auth-user-full.json"), decoded(authHeader), true);
}
@Test
void createFullAuthHeaderReturnsEncodedHeader() throws IOException, JSONException {
DockerRegistryUserAuthentication auth = new DockerRegistryUserAuthentication("user", "secret", null, null);
JSONAssert.assertEquals(jsonContent("auth-user-minimal.json"), decoded(auth.getAuthHeader()), false);
String authHeader = auth.getAuthHeader();
assertThat(authHeader).isNotNull();
JSONAssert.assertEquals(jsonContent("auth-user-minimal.json"), decoded(authHeader), false);
}
private String jsonContent(String s) throws IOException {
@@ -212,7 +212,9 @@ class ResolvedDockerHostTests {
private String pathToResource(String resource) throws URISyntaxException {
URL url = getClass().getResource(resource);
return Paths.get(url.toURI()).getParent().toAbsolutePath().toString();
Path parent = Paths.get(url.toURI()).getParent();
assertThat(parent).isNotNull();
return parent.toAbsolutePath().toString();
}
}
@@ -33,12 +33,14 @@ class DockerConnectionExceptionTests {
private static final String HOST = "docker://localhost/";
@Test
@SuppressWarnings("NullAway") // Test null check
void createWhenHostIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new DockerConnectionException(null, null))
.withMessage("'host' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void createWhenCauseIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new DockerConnectionException(HOST, null))
.withMessage("'cause' must not be null");
@@ -54,6 +54,7 @@ class DockerEngineExceptionTests {
private static final Message MESSAGE = new Message("response message");
@Test
@SuppressWarnings("NullAway") // Test null check
void createWhenHostIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new DockerEngineException(null, null, 404, null, NO_ERRORS, NO_MESSAGE))
@@ -61,6 +62,7 @@ class DockerEngineExceptionTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void createWhenUriIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new DockerEngineException(HOST, null, 404, null, NO_ERRORS, NO_MESSAGE))
@@ -67,15 +67,19 @@ class HttpClientTransportTests {
private static final String APPLICATION_X_TAR = "application/x-tar";
@Mock
@SuppressWarnings("NullAway.Init")
private HttpClient client;
@Mock
@SuppressWarnings("NullAway.Init")
private ClassicHttpResponse response;
@Mock
@SuppressWarnings("NullAway.Init")
private HttpEntity entity;
@Mock
@SuppressWarnings("NullAway.Init")
private InputStream content;
private HttpClientTransport http;
@@ -294,7 +298,9 @@ class HttpClientTransportTests {
assertThatExceptionOfType(DockerEngineException.class).isThrownBy(() -> this.http.get(this.uri))
.satisfies((ex) -> {
assertThat(ex.getErrors()).isNull();
assertThat(ex.getResponseMessage().getMessage()).contains("test message");
Message responseMessage = ex.getResponseMessage();
assertThat(responseMessage).isNotNull();
assertThat(responseMessage.getMessage()).contains("test message");
});
}
@@ -318,7 +324,9 @@ class HttpClientTransportTests {
assertThatExceptionOfType(DockerEngineException.class).isThrownBy(() -> this.http.get(this.uri))
.satisfies((ex) -> {
assertThat(ex.getErrors()).hasSize(2);
assertThat(ex.getResponseMessage().getMessage()).contains("test message");
Message responseMessage = ex.getResponseMessage();
assertThat(responseMessage).isNotNull();
assertThat(responseMessage.getMessage()).contains("test message");
});
}
@@ -68,6 +68,7 @@ class RemoteHttpClientTransportTests {
ResolvedDockerHost dockerHost = ResolvedDockerHost
.from(new DockerConnectionConfiguration.Host("tcp://192.168.1.2:2376"));
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(dockerHost);
assertThat(transport).isNotNull();
assertThat(transport.getHost()).satisfies(hostOf("http", "192.168.1.2", 2376));
}
@@ -78,6 +79,7 @@ class RemoteHttpClientTransportTests {
ResolvedDockerHost dockerHost = ResolvedDockerHost
.from(new DockerConnectionConfiguration.Host("tcp://192.168.1.2:2376", true, "/test-cert-path"));
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(dockerHost, sslContextFactory);
assertThat(transport).isNotNull();
assertThat(transport.getHost()).satisfies(hostOf("https", "192.168.1.2", 2376));
}
@@ -39,6 +39,7 @@ class BindingTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWithNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Binding.of(null))
.withMessageContaining("'value' must not be null");
@@ -51,12 +52,14 @@ class BindingTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void fromWithNullSourceThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Binding.from((String) null, "container-dest"))
.withMessageContaining("'source' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void fromWithNullDestinationThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Binding.from("host-src", null))
.withMessageContaining("'destination' must not be null");
@@ -69,6 +72,7 @@ class BindingTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void fromVolumeNameSourceWithNullSourceThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Binding.from((VolumeName) null, "container-dest"))
.withMessageContaining("'sourceVolume' must not be null");
@@ -37,12 +37,14 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
class ContainerConfigTests extends AbstractJsonTests {
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenImageReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ContainerConfig.of(null, (update) -> {
})).withMessage("'imageReference' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenUpdateIsNullThrowsException() {
ImageReference imageReference = ImageReference.of("ubuntu:bionic");
assertThatIllegalArgumentException().isThrownBy(() -> ContainerConfig.of(imageReference, null))
@@ -32,12 +32,14 @@ import static org.mockito.Mockito.mock;
class ContainerContentTests {
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenArchiveIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ContainerContent.of(null))
.withMessage("'archive' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenDestinationPathIsNullThrowsException() {
TarArchive archive = mock(TarArchive.class);
assertThatIllegalArgumentException().isThrownBy(() -> ContainerContent.of(archive, null))
@@ -36,6 +36,7 @@ class ContainerReferenceTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ContainerReference.of(null))
.withMessage("'value' must not be empty");
@@ -18,6 +18,7 @@ package org.springframework.boot.buildpack.platform.docker.type;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
@@ -36,7 +37,7 @@ class ImageConfigTests extends AbstractJsonTests {
@Test
void getEnvContainsParsedValues() {
ImageConfig imageConfig = getImageConfig();
Map<String, String> env = imageConfig.getEnv();
Map<String, @Nullable String> env = imageConfig.getEnv();
assertThat(env).contains(entry("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"),
entry("CNB_USER_ID", "2000"), entry("CNB_GROUP_ID", "2000"),
entry("CNB_STACK_ID", "org.cloudfoundry.stacks.cflinuxfs3"));
@@ -45,7 +46,7 @@ class ImageConfigTests extends AbstractJsonTests {
@Test
void whenConfigHasNoEnvThenImageConfigEnvIsEmpty() {
ImageConfig imageConfig = getMinimalImageConfig();
Map<String, String> env = imageConfig.getEnv();
Map<String, @Nullable String> env = imageConfig.getEnv();
assertThat(env).isEmpty();
}
@@ -116,6 +116,7 @@ class ImageNameTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenNameIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ImageName.of(null))
.withMessage("'value' must not be empty");
@@ -246,6 +246,7 @@ class ImageReferenceTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void randomWherePrefixIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ImageReference.random(null))
.withMessage("'prefix' must not be null");
@@ -20,6 +20,7 @@ import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
@@ -37,7 +38,7 @@ class ImageTests extends AbstractJsonTests {
@Test
void getConfigEnvContainsParsedValues() throws Exception {
Image image = getImage();
Map<String, String> env = image.getConfig().getEnv();
Map<String, @Nullable String> env = image.getConfig().getEnv();
assertThat(env).contains(entry("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"),
entry("CNB_USER_ID", "2000"), entry("CNB_GROUP_ID", "2000"),
entry("CNB_STACK_ID", "org.cloudfoundry.stacks.cflinuxfs3"));
@@ -50,6 +50,7 @@ class LayerIdTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenValueIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> LayerId.of((String) null))
.withMessage("'value' must not be empty");
@@ -78,6 +79,7 @@ class LayerIdTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofSha256DigestWhenNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> LayerId.ofSha256Digest((byte[]) null))
.withMessage("'digest' must not be null");
@@ -38,12 +38,14 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
class LayerTests {
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenLayoutIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Layer.of((IOConsumer<Layout>) null))
.withMessage("'layout' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void fromTarArchiveWhenTarArchiveIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Layer.fromTarArchive(null))
.withMessage("'tarArchive' must not be null");
@@ -29,6 +29,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
class RandomStringTests {
@Test
@SuppressWarnings("NullAway") // Test null check
void generateWhenPrefixIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> RandomString.generate(null, 10))
.withMessage("'prefix' must not be null");
@@ -29,6 +29,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
class VolumeNameTests {
@Test
@SuppressWarnings("NullAway") // Test null check
void randomWhenPrefixIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.random(null))
.withMessage("'prefix' must not be null");
@@ -55,24 +56,28 @@ class VolumeNameTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void basedOnWhenSourceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.basedOn(null, "prefix", "suffix", 6))
.withMessage("'source' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void basedOnWhenNameExtractorIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.basedOn("test", null, "prefix", "suffix", 6))
.withMessage("'nameExtractor' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void basedOnWhenPrefixIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.basedOn("test", null, "suffix", 6))
.withMessage("'prefix' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void basedOnWhenSuffixIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.basedOn("test", "prefix", null, 6))
.withMessage("'suffix' must not be null");
@@ -91,6 +96,7 @@ class VolumeNameTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenValueIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.of(null))
.withMessage("'value' must not be null");
@@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
class ContentTests {
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenSupplierIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Content.of(1, (IOSupplier<InputStream>) null))
.withMessage("'supplier' must not be null");
@@ -49,6 +50,7 @@ class ContentTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenStringIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Content.of((String) null))
.withMessage("'string' must not be null");
@@ -61,6 +63,7 @@ class ContentTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenBytesIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Content.of((byte[]) null))
.withMessage("'bytes' must not be null");
@@ -45,6 +45,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
class FilePermissionsTests {
@TempDir
@SuppressWarnings("NullAway.Init")
Path tempDir;
@Test
@@ -72,6 +73,7 @@ class FilePermissionsTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void umaskForPathWithNullPath() {
assertThatIllegalArgumentException().isThrownBy(() -> FilePermissions.umaskForPath(null));
}
@@ -89,6 +91,7 @@ class FilePermissionsTests {
}
@Test
@SuppressWarnings("NullAway") // Test null check
void posixPermissionsToUmaskWithNullPermissions() {
assertThatIllegalArgumentException().isThrownBy(() -> FilePermissions.posixPermissionsToUmask(null));
}
@@ -37,18 +37,21 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
class InspectedContentTests {
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenInputStreamThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> InspectedContent.of((InputStream) null))
.withMessage("'inputStream' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenContentIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> InspectedContent.of((Content) null))
.withMessage("'content' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void ofWhenConsumerIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> InspectedContent.of((IOConsumer<OutputStream>) null))
.withMessage("'writer' must not be null");
@@ -40,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class TarArchiveTests {
@TempDir
@SuppressWarnings("NullAway.Init")
File tempDir;
@Test
@@ -41,15 +41,18 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
class ZipFileTarArchiveTests {
@TempDir
@SuppressWarnings("NullAway.Init")
File tempDir;
@Test
@SuppressWarnings("NullAway") // Test null check
void createWhenZipIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new ZipFileTarArchive(null, Owner.ROOT))
.withMessage("'zip' must not be null");
}
@Test
@SuppressWarnings("NullAway") // Test null check
void createWhenOwnerIsNullThrowsException() throws Exception {
File file = new File(this.tempDir, "test.zip");
writeTestZip(file);
@@ -63,6 +63,7 @@ class MappedObjectTests extends AbstractJsonTests {
@Test
void valueAtWhenInterfaceReturnsProxy() {
Person person = this.mapped.valueAt("/person", Person.class);
assertThat(person).isNotNull();
assertThat(person.getName().getFirst()).isEqualTo("spring");
assertThat(person.getName().getLast()).isEqualTo("boot");
}
@@ -70,6 +71,7 @@ class MappedObjectTests extends AbstractJsonTests {
@Test
void valueAtWhenInterfaceAndMissingReturnsProxy() {
Person person = this.mapped.valueAt("/missing", Person.class);
assertThat(person).isNotNull();
assertThat(person.getName().getFirst()).isNull();
assertThat(person.getName().getLast()).isNull();
}
@@ -82,6 +84,7 @@ class MappedObjectTests extends AbstractJsonTests {
@Test
void valueAtWhenDefaultMethodReturnsValue() {
Person person = this.mapped.valueAt("/person", Person.class);
assertThat(person).isNotNull();
assertThat(person.getName().getFullName()).isEqualTo("dr spring boot");
}