diff --git a/integration-test/src/test/java/org/cloudfoundry/CleanupCloudFoundryAfterClass.java b/integration-test/src/test/java/org/cloudfoundry/CleanupCloudFoundryAfterClass.java
new file mode 100644
index 0000000000..88d8fd6303
--- /dev/null
+++ b/integration-test/src/test/java/org/cloudfoundry/CleanupCloudFoundryAfterClass.java
@@ -0,0 +1,27 @@
+package org.cloudfoundry;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.springframework.context.annotation.Bean;
+import org.springframework.test.annotation.DirtiesContext;
+
+/**
+ * Meta-annotation to show that a test class will add too much to the CF instance, and that a full universe
+ * cleanup should occur. This is important because otherwise the integration tests create too many apps and
+ * blow up the memory quota. We do not want to recreate the full environment for EVERY test class because
+ * the process takes 30~60s, so in total it could add more than an hour to the integration tests.
+ *
+ * Technically, this is achieved by recreating a Spring ApplicationContext with {@link DirtiesContext}. The
+ * {@link CloudFoundryCleaner} bean will be destroyed, which triggers {@link CloudFoundryCleaner#clean()}.
+ * After that, every {@link Bean} in {@link IntegrationTestConfiguration} is recreated, including users,
+ * clients, organizations, etc: everything required to run a test.
+ *
+ * We use a meta-annotation instead of a raw {@link DirtiesContext} to make it clear what it does, rather
+ * than having to understand complicated lifecycle issues.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@DirtiesContext
+public @interface CleanupCloudFoundryAfterClass {}
diff --git a/integration-test/src/test/java/org/cloudfoundry/CloudFoundryCleaner.java b/integration-test/src/test/java/org/cloudfoundry/CloudFoundryCleaner.java
index c4fb056ad2..d515a86b09 100644
--- a/integration-test/src/test/java/org/cloudfoundry/CloudFoundryCleaner.java
+++ b/integration-test/src/test/java/org/cloudfoundry/CloudFoundryCleaner.java
@@ -116,12 +116,16 @@
import org.cloudfoundry.util.ResourceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuples;
import reactor.util.retry.Retry;
-final class CloudFoundryCleaner {
+final class CloudFoundryCleaner implements InitializingBean, DisposableBean {
+
+ private static boolean cleanSlateEnvironment = false;
private static final Logger LOGGER = LoggerFactory.getLogger("cloudfoundry-client.test");
@@ -162,6 +166,25 @@ final class CloudFoundryCleaner {
this.uaaClient = uaaClient;
}
+ /**
+ * Once at the beginning of the whole test suite, clean up the environment. It should only ever happen
+ * once, hence the static init variable.
+ */
+ @Override
+ public void afterPropertiesSet() {
+ if (!cleanSlateEnvironment) {
+ LOGGER.info(
+ "Performing clean slate cleanup. Should happen once per integration test run.");
+ this.clean();
+ cleanSlateEnvironment = true;
+ }
+ }
+
+ @Override
+ public void destroy() {
+ this.clean();
+ }
+
void clean() {
Flux.empty()
.thenMany(
diff --git a/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java b/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java
index 12571face1..c0b3f44b30 100644
--- a/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java
+++ b/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java
@@ -37,7 +37,6 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
-import java.util.Random;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.info.GetInfoRequest;
import org.cloudfoundry.client.v2.organizationquotadefinitions.CreateOrganizationQuotaDefinitionRequest;
@@ -243,14 +242,13 @@ String clientSecret(NameFactory nameFactory) {
return nameFactory.getClientSecret();
}
- @Bean(initMethod = "clean", destroyMethod = "clean")
+ @Bean
CloudFoundryCleaner cloudFoundryCleaner(
@Qualifier("admin") CloudFoundryClient cloudFoundryClient,
NameFactory nameFactory,
@Qualifier("admin") NetworkingClient networkingClient,
Version serverVersion,
@Qualifier("admin") UaaClient uaaClient) {
-
return new CloudFoundryCleaner(
cloudFoundryClient, nameFactory, networkingClient, serverVersion, uaaClient);
}
@@ -339,8 +337,8 @@ ReactorLogCacheClient logCacheClient(
}
@Bean
- RandomNameFactory nameFactory(Random random) {
- return new RandomNameFactory(random);
+ RandomNameFactory nameFactory() {
+ return new RandomNameFactory(new SecureRandom());
}
@Bean(initMethod = "block")
@@ -447,11 +445,6 @@ String planName(NameFactory nameFactory) {
return nameFactory.getPlanName();
}
- @Bean
- SecureRandom random() {
- return new SecureRandom();
- }
-
@Bean
RoutingClient routingClient(ConnectionContext connectionContext, TokenProvider tokenProvider) {
return ReactorRoutingClient.builder()
diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v2/ApplicationsTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v2/ApplicationsTest.java
index 6cfbab97d8..9774568bd0 100644
--- a/integration-test/src/test/java/org/cloudfoundry/client/v2/ApplicationsTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/client/v2/ApplicationsTest.java
@@ -36,6 +36,7 @@
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.cloudfoundry.AbstractIntegrationTest;
+import org.cloudfoundry.CleanupCloudFoundryAfterClass;
import org.cloudfoundry.CloudFoundryVersion;
import org.cloudfoundry.IfCloudFoundryVersion;
import org.cloudfoundry.client.CloudFoundryClient;
@@ -96,6 +97,7 @@
import reactor.test.StepVerifier;
import reactor.util.function.Tuple2;
+@CleanupCloudFoundryAfterClass
public final class ApplicationsTest extends AbstractIntegrationTest {
@Autowired private CloudFoundryClient cloudFoundryClient;
diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v2/ServiceBrokersTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v2/ServiceBrokersTest.java
index 75486720fc..214577e4a9 100644
--- a/integration-test/src/test/java/org/cloudfoundry/client/v2/ServiceBrokersTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/client/v2/ServiceBrokersTest.java
@@ -25,6 +25,7 @@
import java.time.Duration;
import org.cloudfoundry.AbstractIntegrationTest;
import org.cloudfoundry.ApplicationUtils;
+import org.cloudfoundry.CleanupCloudFoundryAfterClass;
import org.cloudfoundry.ServiceBrokerUtils;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.servicebrokers.CreateServiceBrokerRequest;
@@ -43,6 +44,7 @@
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
+@CleanupCloudFoundryAfterClass
public final class ServiceBrokersTest extends AbstractIntegrationTest {
@Autowired private CloudFoundryClient cloudFoundryClient;
diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v3/AdminTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v3/AdminTest.java
index 6775ef1df5..d185f15dea 100644
--- a/integration-test/src/test/java/org/cloudfoundry/client/v3/AdminTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/client/v3/AdminTest.java
@@ -18,6 +18,7 @@
import java.time.Duration;
import org.cloudfoundry.AbstractIntegrationTest;
+import org.cloudfoundry.ApplicationUtils;
import org.cloudfoundry.CloudFoundryVersion;
import org.cloudfoundry.IfCloudFoundryVersion;
import org.cloudfoundry.client.CloudFoundryClient;
@@ -25,12 +26,18 @@
import org.cloudfoundry.util.JobUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
+import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
public final class AdminTest extends AbstractIntegrationTest {
@Autowired private CloudFoundryClient cloudFoundryClient;
+ // The buildpacks cache needs to be non-empty for the DELETE call to succeed.
+ // We pull the "testLogCacheApp" bean, which ensures the bean will be initialized,
+ // the app will be pushed, and it will add some data to the cache.
+ @Autowired private Mono testLogCacheApp;
+
@IfCloudFoundryVersion(greaterThanOrEqualTo = CloudFoundryVersion.PCF_2_10)
@Test
public void clearBuildpackCache() {
diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v3/ApplicationsTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v3/ApplicationsTest.java
index 3354b24ed7..ef3ea073c8 100644
--- a/integration-test/src/test/java/org/cloudfoundry/client/v3/ApplicationsTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/client/v3/ApplicationsTest.java
@@ -28,6 +28,7 @@
import java.util.Collections;
import java.util.Map;
import org.cloudfoundry.AbstractIntegrationTest;
+import org.cloudfoundry.CleanupCloudFoundryAfterClass;
import org.cloudfoundry.CloudFoundryVersion;
import org.cloudfoundry.IfCloudFoundryVersion;
import org.cloudfoundry.client.CloudFoundryClient;
@@ -106,6 +107,7 @@
import reactor.test.StepVerifier;
import reactor.util.function.Tuples;
+@CleanupCloudFoundryAfterClass
public final class ApplicationsTest extends AbstractIntegrationTest {
@Autowired private CloudFoundryClient cloudFoundryClient;
diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v3/DeploymentsTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v3/DeploymentsTest.java
index 61ec02076f..d823ad7b1c 100644
--- a/integration-test/src/test/java/org/cloudfoundry/client/v3/DeploymentsTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/client/v3/DeploymentsTest.java
@@ -22,6 +22,7 @@
import java.nio.file.Path;
import java.time.Duration;
import org.cloudfoundry.AbstractIntegrationTest;
+import org.cloudfoundry.CleanupCloudFoundryAfterClass;
import org.cloudfoundry.CloudFoundryVersion;
import org.cloudfoundry.IfCloudFoundryVersion;
import org.cloudfoundry.client.CloudFoundryClient;
@@ -51,6 +52,7 @@
import reactor.test.StepVerifier;
@IfCloudFoundryVersion(greaterThanOrEqualTo = CloudFoundryVersion.PCF_2_4)
+@CleanupCloudFoundryAfterClass
public final class DeploymentsTest extends AbstractIntegrationTest {
@Autowired private CloudFoundryClient cloudFoundryClient;
diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v3/ProcessesTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v3/ProcessesTest.java
index b509114793..e4a4184e85 100644
--- a/integration-test/src/test/java/org/cloudfoundry/client/v3/ProcessesTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/client/v3/ProcessesTest.java
@@ -22,6 +22,7 @@
import java.nio.file.Path;
import java.time.Duration;
import org.cloudfoundry.AbstractIntegrationTest;
+import org.cloudfoundry.CleanupCloudFoundryAfterClass;
import org.cloudfoundry.CloudFoundryVersion;
import org.cloudfoundry.IfCloudFoundryVersion;
import org.cloudfoundry.client.CloudFoundryClient;
@@ -42,6 +43,7 @@
import reactor.test.StepVerifier;
@IfCloudFoundryVersion(greaterThanOrEqualTo = CloudFoundryVersion.PCF_2_0)
+@CleanupCloudFoundryAfterClass
public final class ProcessesTest extends AbstractIntegrationTest {
@Autowired private CloudFoundryClient cloudFoundryClient;
diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v3/ServiceBrokersTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v3/ServiceBrokersTest.java
index f7c063c445..656a0b0efe 100644
--- a/integration-test/src/test/java/org/cloudfoundry/client/v3/ServiceBrokersTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/client/v3/ServiceBrokersTest.java
@@ -25,6 +25,7 @@
import java.time.Duration;
import org.cloudfoundry.AbstractIntegrationTest;
import org.cloudfoundry.ApplicationUtils;
+import org.cloudfoundry.CleanupCloudFoundryAfterClass;
import org.cloudfoundry.CloudFoundryVersion;
import org.cloudfoundry.IfCloudFoundryVersion;
import org.cloudfoundry.ServiceBrokerUtils;
@@ -49,6 +50,7 @@
import reactor.test.StepVerifier;
@IfCloudFoundryVersion(greaterThanOrEqualTo = CloudFoundryVersion.PCF_2_10)
+@CleanupCloudFoundryAfterClass
public final class ServiceBrokersTest extends AbstractIntegrationTest {
@Autowired private CloudFoundryClient cloudFoundryClient;
diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v3/TasksTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v3/TasksTest.java
index a4c962d0f1..0e3fdada8c 100644
--- a/integration-test/src/test/java/org/cloudfoundry/client/v3/TasksTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/client/v3/TasksTest.java
@@ -22,6 +22,7 @@
import java.io.IOException;
import java.time.Duration;
import org.cloudfoundry.AbstractIntegrationTest;
+import org.cloudfoundry.CleanupCloudFoundryAfterClass;
import org.cloudfoundry.CloudFoundryVersion;
import org.cloudfoundry.IfCloudFoundryVersion;
import org.cloudfoundry.client.CloudFoundryClient;
@@ -50,6 +51,7 @@
import reactor.test.StepVerifier;
@IfCloudFoundryVersion(greaterThanOrEqualTo = CloudFoundryVersion.PCF_1_12)
+@CleanupCloudFoundryAfterClass
public final class TasksTest extends AbstractIntegrationTest {
@Autowired private CloudFoundryClient cloudFoundryClient;
diff --git a/integration-test/src/test/java/org/cloudfoundry/logcache/v1/LogCacheTest.java b/integration-test/src/test/java/org/cloudfoundry/logcache/v1/LogCacheTest.java
index 656f84111e..414af210bd 100644
--- a/integration-test/src/test/java/org/cloudfoundry/logcache/v1/LogCacheTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/logcache/v1/LogCacheTest.java
@@ -21,6 +21,7 @@
import static org.cloudfoundry.util.DelayUtils.exponentialBackOff;
import java.math.BigInteger;
+import java.security.SecureRandom;
import java.time.Duration;
import java.util.Random;
import java.util.function.Predicate;
@@ -28,29 +29,27 @@
import org.cloudfoundry.ApplicationUtils;
import org.cloudfoundry.CloudFoundryVersion;
import org.cloudfoundry.IfCloudFoundryVersion;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@IfCloudFoundryVersion(greaterThanOrEqualTo = CloudFoundryVersion.PCF_2_9)
-public class LogCacheTest extends AbstractIntegrationTest implements InitializingBean {
+public class LogCacheTest extends AbstractIntegrationTest {
@Autowired LogCacheClient logCacheClient;
- @Autowired Random random;
-
- @Autowired private Mono testLogCacheApp;
-
private ApplicationUtils.ApplicationMetadata testLogCacheAppMetadata;
@Autowired private TestLogCacheEndpoints testLogCacheEndpoints;
- @Override
- public void afterPropertiesSet() {
- this.testLogCacheAppMetadata = this.testLogCacheApp.block();
+ private final Random random = new SecureRandom();
+
+ @BeforeEach
+ void setUp(@Autowired Mono testLogCacheApp) {
+ this.testLogCacheAppMetadata = testLogCacheApp.block();
}
@Test
diff --git a/integration-test/src/test/java/org/cloudfoundry/operations/ApplicationsTest.java b/integration-test/src/test/java/org/cloudfoundry/operations/ApplicationsTest.java
index 45addf4fb2..2ffd49b285 100644
--- a/integration-test/src/test/java/org/cloudfoundry/operations/ApplicationsTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/operations/ApplicationsTest.java
@@ -26,6 +26,7 @@
import java.util.List;
import java.util.Map;
import org.cloudfoundry.AbstractIntegrationTest;
+import org.cloudfoundry.CleanupCloudFoundryAfterClass;
import org.cloudfoundry.CloudFoundryVersion;
import org.cloudfoundry.IfCloudFoundryVersion;
import org.cloudfoundry.logcache.v1.Envelope;
@@ -87,11 +88,13 @@
import org.cloudfoundry.util.FluentMap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.ClassPathResource;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
+@CleanupCloudFoundryAfterClass
public final class ApplicationsTest extends AbstractIntegrationTest {
private static final String DEFAULT_ROUTER_GROUP = "default-tcp";
@@ -106,6 +109,12 @@ public final class ApplicationsTest extends AbstractIntegrationTest {
@Autowired private LogCacheClient logCacheClient;
+ // To create a service in #pushBindService, the Service Broker must be installed first.
+ // We ensure it is by loading the serviceBrokerId @Lazy bean.
+ @Qualifier("serviceBrokerId")
+ @Autowired
+ private Mono serviceBrokerId;
+
@Test
public void copySource() throws IOException {
String sourceName = this.nameFactory.getApplicationName();
diff --git a/integration-test/src/test/java/org/cloudfoundry/operations/RoutesTest.java b/integration-test/src/test/java/org/cloudfoundry/operations/RoutesTest.java
index 5b86191b22..19db1a53b1 100644
--- a/integration-test/src/test/java/org/cloudfoundry/operations/RoutesTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/operations/RoutesTest.java
@@ -27,6 +27,7 @@
import java.util.Optional;
import java.util.function.Predicate;
import org.cloudfoundry.AbstractIntegrationTest;
+import org.cloudfoundry.CleanupCloudFoundryAfterClass;
import org.cloudfoundry.operations.applications.ApplicationHealthCheck;
import org.cloudfoundry.operations.applications.PushApplicationRequest;
import org.cloudfoundry.operations.domains.CreateDomainRequest;
@@ -49,6 +50,7 @@
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
+@CleanupCloudFoundryAfterClass
public final class RoutesTest extends AbstractIntegrationTest {
private static final String DEFAULT_ROUTER_GROUP = "default-tcp";
diff --git a/integration-test/src/test/java/org/cloudfoundry/operations/ServiceAdminTest.java b/integration-test/src/test/java/org/cloudfoundry/operations/ServiceAdminTest.java
index 890c4c09b7..7a8f1ec378 100644
--- a/integration-test/src/test/java/org/cloudfoundry/operations/ServiceAdminTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/operations/ServiceAdminTest.java
@@ -21,6 +21,7 @@
import java.time.Duration;
import org.cloudfoundry.AbstractIntegrationTest;
+import org.cloudfoundry.CleanupCloudFoundryAfterClass;
import org.cloudfoundry.ServiceBrokerUtils;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.spaces.CreateSpaceRequest;
@@ -33,10 +34,12 @@
import org.cloudfoundry.util.ResourceUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
+@CleanupCloudFoundryAfterClass
public final class ServiceAdminTest extends AbstractIntegrationTest {
@Autowired private CloudFoundryClient cloudFoundryClient;
@@ -53,6 +56,12 @@ public final class ServiceAdminTest extends AbstractIntegrationTest {
@Autowired private String serviceName;
+ // To create a service in #pushBindService, the Service Broker must be installed first.
+ // We ensure it is by loading the serviceBrokerId @Lazy bean.
+ @Qualifier("serviceBrokerId")
+ @Autowired
+ private Mono serviceBrokerId;
+
@Test
public void disableServiceAccess() {
String planName = this.nameFactory.getPlanName();
diff --git a/integration-test/src/test/java/org/cloudfoundry/operations/ServicesTest.java b/integration-test/src/test/java/org/cloudfoundry/operations/ServicesTest.java
index 1d3355a4ba..520027128b 100644
--- a/integration-test/src/test/java/org/cloudfoundry/operations/ServicesTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/operations/ServicesTest.java
@@ -23,6 +23,7 @@
import java.nio.file.Path;
import java.time.Duration;
import org.cloudfoundry.AbstractIntegrationTest;
+import org.cloudfoundry.CleanupCloudFoundryAfterClass;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.servicebindings.ListServiceBindingsRequest;
import org.cloudfoundry.client.v2.servicebindings.ServiceBindingResource;
@@ -64,6 +65,7 @@
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
+@CleanupCloudFoundryAfterClass
public final class ServicesTest extends AbstractIntegrationTest {
@Autowired private CloudFoundryClient cloudFoundryClient;