Skip to content

Commit a3d4d21

Browse files
authored
Merge pull request #79 from ContainerSolutions/int-test-improvement
Improve integration tests
2 parents 8d04be4 + c4ab741 commit a3d4d21

File tree

5 files changed

+123
-115
lines changed

5 files changed

+123
-115
lines changed

operator-framework/src/test/java/com/github/containersolutions/operator/ConcurrencyIT.java

Lines changed: 52 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
import com.github.containersolutions.operator.sample.TestCustomResourceController;
55
import io.fabric8.kubernetes.api.model.ConfigMap;
66
import org.awaitility.Awaitility;
7-
import org.junit.jupiter.api.*;
7+
import org.junit.jupiter.api.BeforeAll;
8+
import org.junit.jupiter.api.BeforeEach;
9+
import org.junit.jupiter.api.Test;
10+
import org.junit.jupiter.api.TestInstance;
811
import org.slf4j.Logger;
912
import org.slf4j.LoggerFactory;
1013

@@ -34,58 +37,55 @@ public void cleanup() {
3437
integrationTest.cleanup();
3538
}
3639

37-
@AfterAll
38-
public void teardown() {
39-
integrationTest.teardown();
40-
}
41-
4240
@Test
43-
public void manyResourcesGetCreatedUpdatedAndDeleted() throws InterruptedException {
44-
log.info("Adding new resources.");
45-
for (int i = 0; i < NUMBER_OF_RESOURCES_CREATED; i++) {
46-
TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i));
47-
integrationTest.getCrOperations().inNamespace(TEST_NAMESPACE).create(tcr);
48-
}
49-
50-
Awaitility.await().atMost(1, TimeUnit.MINUTES)
51-
.untilAsserted(() -> {
52-
List<ConfigMap> items = integrationTest.getK8sClient().configMaps()
53-
.inNamespace(TEST_NAMESPACE)
54-
.withLabel("managedBy", TestCustomResourceController.class.getSimpleName())
55-
.list().getItems();
56-
assertThat(items).hasSize(NUMBER_OF_RESOURCES_CREATED);
57-
});
58-
59-
log.info("Updating resources.");
60-
// update some resources
61-
for (int i = 0; i < NUMBER_OF_RESOURCES_UPDATED; i++) {
62-
TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i));
63-
tcr.getSpec().setValue(i + UPDATED_SUFFIX);
64-
integrationTest.getCrOperations().inNamespace(TEST_NAMESPACE).createOrReplace(tcr);
65-
}
66-
// sleep to make some variability to the test, so some updates are not executed before delete
67-
Thread.sleep(300);
68-
69-
log.info("Deleting resources.");
70-
// deleting some resources
71-
for (int i = 0; i < NUMBER_OF_RESOURCES_DELETED; i++) {
72-
TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i));
73-
integrationTest.getCrOperations().inNamespace(TEST_NAMESPACE).delete(tcr);
74-
}
75-
76-
Awaitility.await().atMost(1, TimeUnit.MINUTES)
77-
.untilAsserted(() -> {
78-
List<ConfigMap> items = integrationTest.getK8sClient().configMaps()
79-
.inNamespace(TEST_NAMESPACE)
80-
.withLabel("managedBy", TestCustomResourceController.class.getSimpleName())
81-
.list().getItems();
82-
assertThat(items).hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED);
83-
84-
List<TestCustomResource> crs = integrationTest.getCrOperations()
85-
.inNamespace(TEST_NAMESPACE)
86-
.list().getItems();
87-
assertThat(crs).hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED);
88-
});
41+
public void manyResourcesGetCreatedUpdatedAndDeleted() throws Exception {
42+
integrationTest.teardownIfSuccess(() -> {
43+
log.info("Adding new resources.");
44+
for (int i = 0; i < NUMBER_OF_RESOURCES_CREATED; i++) {
45+
TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i));
46+
integrationTest.getCrOperations().inNamespace(TEST_NAMESPACE).create(tcr);
47+
}
48+
49+
Awaitility.await().atMost(1, TimeUnit.MINUTES)
50+
.untilAsserted(() -> {
51+
List<ConfigMap> items = integrationTest.getK8sClient().configMaps()
52+
.inNamespace(TEST_NAMESPACE)
53+
.withLabel("managedBy", TestCustomResourceController.class.getSimpleName())
54+
.list().getItems();
55+
assertThat(items).hasSize(NUMBER_OF_RESOURCES_CREATED);
56+
});
57+
58+
log.info("Updating resources.");
59+
// update some resources
60+
for (int i = 0; i < NUMBER_OF_RESOURCES_UPDATED; i++) {
61+
TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i));
62+
tcr.getSpec().setValue(i + UPDATED_SUFFIX);
63+
integrationTest.getCrOperations().inNamespace(TEST_NAMESPACE).createOrReplace(tcr);
64+
}
65+
// sleep to make some variability to the test, so some updates are not executed before delete
66+
Thread.sleep(300);
67+
68+
log.info("Deleting resources.");
69+
// deleting some resources
70+
for (int i = 0; i < NUMBER_OF_RESOURCES_DELETED; i++) {
71+
TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i));
72+
integrationTest.getCrOperations().inNamespace(TEST_NAMESPACE).delete(tcr);
73+
}
74+
75+
Awaitility.await().atMost(1, TimeUnit.MINUTES)
76+
.untilAsserted(() -> {
77+
List<ConfigMap> items = integrationTest.getK8sClient().configMaps()
78+
.inNamespace(TEST_NAMESPACE)
79+
.withLabel("managedBy", TestCustomResourceController.class.getSimpleName())
80+
.list().getItems();
81+
assertThat(items).hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED);
82+
83+
List<TestCustomResource> crs = integrationTest.getCrOperations()
84+
.inNamespace(TEST_NAMESPACE)
85+
.list().getItems();
86+
assertThat(crs).hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED);
87+
});
88+
});
8989
}
9090

9191

operator-framework/src/test/java/com/github/containersolutions/operator/ControllerExecutionIT.java

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
import com.github.containersolutions.operator.sample.TestCustomResourceSpec;
55
import io.fabric8.kubernetes.api.model.ConfigMap;
66
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
7-
import org.junit.jupiter.api.*;
7+
import org.junit.jupiter.api.BeforeAll;
8+
import org.junit.jupiter.api.BeforeEach;
9+
import org.junit.jupiter.api.Test;
10+
import org.junit.jupiter.api.TestInstance;
811
import org.slf4j.Logger;
912
import org.slf4j.LoggerFactory;
1013

@@ -30,38 +33,35 @@ public void cleanup() {
3033
integrationTestSupport.cleanup();
3134
}
3235

33-
@AfterAll
34-
public void teardown() {
35-
integrationTestSupport.teardown();
36-
}
37-
3836
@Test
39-
public void configMapGetsCreatedForTestCustomResource() {
40-
TestCustomResource resource = new TestCustomResource();
41-
resource.setMetadata(new ObjectMetaBuilder()
42-
.withName("test-custom-resource")
43-
.withNamespace(TEST_NAMESPACE)
44-
.build());
45-
resource.setKind("CustomService");
46-
resource.setSpec(new TestCustomResourceSpec());
47-
resource.getSpec().setConfigMapName("test-config-map");
48-
resource.getSpec().setKey("test-key");
49-
resource.getSpec().setValue("test-value");
50-
integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource);
37+
public void configMapGetsCreatedForTestCustomResource() throws Exception {
38+
integrationTestSupport.teardownIfSuccess(() -> {
39+
TestCustomResource resource = new TestCustomResource();
40+
resource.setMetadata(new ObjectMetaBuilder()
41+
.withName("test-custom-resource")
42+
.withNamespace(TEST_NAMESPACE)
43+
.build());
44+
resource.setKind("CustomService");
45+
resource.setSpec(new TestCustomResourceSpec());
46+
resource.getSpec().setConfigMapName("test-config-map");
47+
resource.getSpec().setKey("test-key");
48+
resource.getSpec().setValue("test-value");
49+
integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource);
5150

52-
await("configmap created").atMost(5, TimeUnit.SECONDS)
53-
.untilAsserted(() -> {
54-
ConfigMap configMap = integrationTestSupport.getK8sClient().configMaps().inNamespace(TEST_NAMESPACE)
55-
.withName("test-config-map").get();
56-
assertThat(configMap).isNotNull();
57-
assertThat(configMap.getData().get("test-key")).isEqualTo("test-value");
58-
});
59-
await("cr status updated").atMost(5, TimeUnit.SECONDS)
60-
.untilAsserted(() -> {
61-
TestCustomResource cr = integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).withName("test-custom-resource").get();
62-
assertThat(cr).isNotNull();
63-
assertThat(cr.getStatus()).isNotNull();
64-
assertThat(cr.getStatus().getConfigMapStatus()).isEqualTo("ConfigMap Ready");
65-
});
51+
await("configmap created").atMost(5, TimeUnit.SECONDS)
52+
.untilAsserted(() -> {
53+
ConfigMap configMap = integrationTestSupport.getK8sClient().configMaps().inNamespace(TEST_NAMESPACE)
54+
.withName("test-config-map").get();
55+
assertThat(configMap).isNotNull();
56+
assertThat(configMap.getData().get("test-key")).isEqualTo("test-value");
57+
});
58+
await("cr status updated").atMost(5, TimeUnit.SECONDS)
59+
.untilAsserted(() -> {
60+
TestCustomResource cr = integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).withName("test-custom-resource").get();
61+
assertThat(cr).isNotNull();
62+
assertThat(cr.getStatus()).isNotNull();
63+
assertThat(cr.getStatus().getConfigMapStatus()).isEqualTo("ConfigMap Ready");
64+
});
65+
});
6666
}
6767
}

operator-framework/src/test/java/com/github/containersolutions/operator/EventSchedulerTest.java

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -84,24 +84,6 @@ public void retriesEventsWithErrors() {
8484
.has(new Condition<>(e -> e.getException() == null, ""), atIndex(1));
8585
}
8686

87-
88-
@Test
89-
public void discardsEventIfThereWereANewerVersionProcessedBefore() throws InterruptedException {
90-
normalDispatcherExecution();
91-
CustomResource resource1 = sampleResource();
92-
CustomResource resource2 = sampleResource();
93-
resource2.getMetadata().setResourceVersion("2");
94-
95-
CompletableFuture.runAsync(() -> eventScheduler.eventReceived(Watcher.Action.MODIFIED, resource2));
96-
Thread.sleep(50);
97-
CompletableFuture.runAsync(() -> eventScheduler.eventReceived(Watcher.Action.MODIFIED, resource1));
98-
99-
waitTimeForExecution(2);
100-
101-
assertThat(eventProcessingList).hasSize(1).has(new Condition<>(e -> e.getCustomResource().getMetadata().getResourceVersion().equals("2"),
102-
"Just handles event that arrived first"), atIndex(0));
103-
}
104-
10587
@Test
10688
public void schedulesEventIfOlderVersionIsAlreadyUnderProcessing() {
10789
normalDispatcherExecution();

operator-framework/src/test/java/com/github/containersolutions/operator/IntegrationTestSupport.java

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.github.containersolutions.operator.sample.TestCustomResource;
55
import com.github.containersolutions.operator.sample.TestCustomResourceController;
66
import com.github.containersolutions.operator.sample.TestCustomResourceSpec;
7+
import io.fabric8.kubernetes.api.model.Namespace;
78
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
89
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
910
import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinition;
@@ -14,7 +15,6 @@
1415
import io.fabric8.kubernetes.client.dsl.MixedOperation;
1516
import io.fabric8.kubernetes.client.dsl.Resource;
1617
import io.fabric8.kubernetes.client.utils.Serialization;
17-
import io.fabric8.kubernetes.internal.KubernetesDeserializer;
1818
import org.slf4j.Logger;
1919
import org.slf4j.LoggerFactory;
2020

@@ -40,28 +40,28 @@ public class IntegrationTestSupport {
4040
public void initialize() {
4141
k8sClient = new DefaultKubernetesClient();
4242

43-
log.info("Running integration test in namespace " + TEST_NAMESPACE);
43+
log.info("Initializing integration test in namespace {}", TEST_NAMESPACE);
4444

4545
CustomResourceDefinition crd = loadYaml(CustomResourceDefinition.class, "test-crd.yaml");
4646
k8sClient.customResourceDefinitions().createOrReplace(crd);
4747

48+
ResourceController<TestCustomResource> controller = new TestCustomResourceController(k8sClient);
49+
Class doneableClass = getCustomResourceDoneableClass(controller);
50+
crOperations = k8sClient.customResources(crd, TestCustomResource.class, CustomResourceList.class, doneableClass);
51+
crOperations.inNamespace(TEST_NAMESPACE).delete(crOperations.list().getItems());
52+
4853
if (k8sClient.namespaces().withName(TEST_NAMESPACE).get() == null) {
4954
k8sClient.namespaces().create(new NamespaceBuilder()
5055
.withMetadata(new ObjectMetaBuilder().withName(TEST_NAMESPACE).build()).build());
5156
}
5257
operator = new Operator(k8sClient);
53-
operator.registerController(new TestCustomResourceController(k8sClient), TEST_NAMESPACE);
58+
operator.registerController(controller, TEST_NAMESPACE);
59+
log.info("Operator is running with TestCustomeResourceController");
5460
}
5561

5662
public void cleanup() {
57-
CustomResourceDefinition crd = loadYaml(CustomResourceDefinition.class, "test-crd.yaml");
58-
k8sClient.customResourceDefinitions().createOrReplace(crd);
59-
KubernetesDeserializer.registerCustomKind(crd.getApiVersion(), crd.getKind(), TestCustomResource.class);
63+
log.info("Cleaning up namespace {}", TEST_NAMESPACE);
6064

61-
ResourceController<TestCustomResource> controller = new TestCustomResourceController(k8sClient);
62-
Class doneableClass = getCustomResourceDoneableClass(controller);
63-
crOperations = k8sClient.customResources(crd, TestCustomResource.class, CustomResourceList.class, doneableClass);
64-
crOperations.inNamespace(TEST_NAMESPACE).delete(crOperations.list().getItems());
6565
//we depend on the actual operator from the startup to handle the finalizers and clean up
6666
//resources from previous test runs
6767

@@ -85,8 +85,29 @@ public void cleanup() {
8585
log.info("Cleaned up namespace " + TEST_NAMESPACE);
8686
}
8787

88-
public void teardown() {
89-
k8sClient.close();
88+
/**
89+
* Use this method to execute the cleanup of the integration test namespace only in case the test
90+
* was successful. This is useful to keep the Kubernetes resources around to debug a failed test run.
91+
* Unfortunately I couldn't make this work with standard JUnit methods as the @AfterAll method doesn't know
92+
* if the tests succeeded or not.
93+
*
94+
* @param test The code of the actual test.
95+
* @throws Exception if the test threw an exception.
96+
*/
97+
public void teardownIfSuccess(TestRun test) throws Exception {
98+
try {
99+
test.run();
100+
101+
log.info("Deleting namespace {} and stopping operator", TEST_NAMESPACE);
102+
Namespace namespace = k8sClient.namespaces().withName(TEST_NAMESPACE).get();
103+
if (namespace.getStatus().getPhase().equals("Active")) {
104+
k8sClient.namespaces().withName(TEST_NAMESPACE).delete();
105+
}
106+
await("namespace deleted").atMost(30, TimeUnit.SECONDS)
107+
.until(() -> k8sClient.namespaces().withName(TEST_NAMESPACE).get() == null);
108+
} finally {
109+
k8sClient.close();
110+
}
90111
}
91112

92113
private <T> T loadYaml(Class<T> clazz, String yaml) {
@@ -122,4 +143,8 @@ public MixedOperation<TestCustomResource, CustomResourceList, CustomResourceDone
122143
public Operator getOperator() {
123144
return operator;
124145
}
146+
147+
public interface TestRun {
148+
void run() throws Exception;
149+
}
125150
}

pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
<dependency>
4545
<groupId>io.fabric8</groupId>
4646
<artifactId>openshift-client</artifactId>
47-
<version>4.9.0</version>
47+
<version>4.9.1</version>
4848
</dependency>
4949
<dependency>
5050
<groupId>org.apache.commons</groupId>
@@ -92,6 +92,7 @@
9292
<configuration>
9393
<includes>
9494
<include>**/*IT.java</include>
95+
<include>**/*Test.java</include>
9596
</includes>
9697
</configuration>
9798
</plugin>

0 commit comments

Comments
 (0)