diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/test/DockerComposeTestExtension.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/test/DockerComposeTestExtension.java index c0ee2a7cf0..d2470524b0 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/test/DockerComposeTestExtension.java +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/test/DockerComposeTestExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.boot.docker.compose.service.connection.test; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.LinkedHashMap; @@ -39,6 +40,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +import org.springframework.util.FileSystemUtils; import static org.assertj.core.api.Assertions.fail; @@ -46,22 +48,24 @@ import static org.assertj.core.api.Assertions.fail; * {@link Extension} for {@link DockerComposeTest @DockerComposeTest}. * * @author Andy Wilkinson + * @author Moritz Halbritter */ class DockerComposeTestExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback, ParameterResolver { private static final Namespace NAMESPACE = Namespace.create(DockerComposeTestExtension.class); - private static final String STORE_KEY_COMPOSE_FILE = "compose-file"; + private static final String STORE_KEY_WORKSPACE = "workspace"; private static final String STORE_KEY_APPLICATION_CONTEXT = "application-context"; @Override public void beforeTestExecution(ExtensionContext context) throws Exception { - Path transformedComposeFile = prepareComposeFile(context); Store store = context.getStore(NAMESPACE); - store.put(STORE_KEY_COMPOSE_FILE, transformedComposeFile); + Path workspace = Files.createTempDirectory("DockerComposeTestExtension-"); + store.put(STORE_KEY_WORKSPACE, workspace); try { - SpringApplication application = prepareApplication(transformedComposeFile); + Path composeFile = prepareComposeFile(workspace, context); + SpringApplication application = prepareApplication(composeFile); store.put(STORE_KEY_APPLICATION_CONTEXT, application.run()); } catch (Exception ex) { @@ -70,33 +74,33 @@ class DockerComposeTestExtension implements BeforeTestExecutionCallback, AfterTe } } - private Path prepareComposeFile(ExtensionContext context) { + private Path prepareComposeFile(Path workspace, ExtensionContext context) { DockerComposeTest dockerComposeTest = context.getRequiredTestMethod().getAnnotation(DockerComposeTest.class); TestImage image = dockerComposeTest.image(); Resource composeResource = new ClassPathResource(dockerComposeTest.composeFile(), context.getRequiredTestClass()); - return transformedComposeFile(composeResource, image); + return transformedComposeFile(workspace, composeResource, image); } - private Path transformedComposeFile(Resource composeFileResource, TestImage image) { + private Path transformedComposeFile(Path workspace, Resource composeFileResource, TestImage image) { try { - Path composeFile = composeFileResource.getFile().toPath(); - Path transformedComposeFile = Files.createTempFile("", "-" + composeFile.getFileName().toString()); - String transformedContent = Files.readString(composeFile).replace("{imageName}", image.toString()); - Files.writeString(transformedComposeFile, transformedContent); - return transformedComposeFile; + String template = composeFileResource.getContentAsString(StandardCharsets.UTF_8); + String content = template.replace("{imageName}", image.toString()); + Path composeFile = workspace.resolve("compose.yaml"); + Files.writeString(composeFile, content); + return composeFile; } catch (IOException ex) { - fail("Error transforming Docker compose file '" + composeFileResource + "': " + ex.getMessage()); + fail("Error transforming Docker compose file '" + composeFileResource + "': " + ex.getMessage(), ex); + return null; } - return null; } - private SpringApplication prepareApplication(Path transformedComposeFile) { + private SpringApplication prepareApplication(Path composeFile) { SpringApplication application = new SpringApplication(Config.class); Map properties = new LinkedHashMap<>(); properties.put("spring.docker.compose.skip.in-tests", "false"); - properties.put("spring.docker.compose.file", transformedComposeFile); + properties.put("spring.docker.compose.file", composeFile); properties.put("spring.docker.compose.stop.command", "down"); application.setDefaultProperties(properties); return application; @@ -110,7 +114,7 @@ class DockerComposeTestExtension implements BeforeTestExecutionCallback, AfterTe private void cleanUp(ExtensionContext context) throws Exception { Store store = context.getStore(NAMESPACE); runShutdownHandlers(); - deleteComposeFile(store); + deleteWorkspace(store); } private void runShutdownHandlers() { @@ -118,10 +122,10 @@ class DockerComposeTestExtension implements BeforeTestExecutionCallback, AfterTe ((Runnable) shutdownHandlers).run(); } - private void deleteComposeFile(Store store) throws IOException { - Path composeFile = store.get(STORE_KEY_COMPOSE_FILE, Path.class); - if (composeFile != null) { - Files.delete(composeFile); + private void deleteWorkspace(Store store) throws IOException { + Path workspace = (Path) store.get(STORE_KEY_WORKSPACE); + if (workspace != null) { + FileSystemUtils.deleteRecursively(workspace); } }