Add integration tests 54/143354/3
authorFiete Ostkamp <fiete.ostkamp@telekom.de>
Mon, 23 Feb 2026 08:02:05 +0000 (09:02 +0100)
committerFiete Ostkamp <fiete.ostkamp@telekom.de>
Mon, 23 Feb 2026 08:25:54 +0000 (09:25 +0100)
- test end to end http communication towards AAI and Multicloud

Issue-ID: SO-4240
Change-Id: Ic2aedad93f4899d8874903b9ec1d9c6961053091
Signed-off-by: Fiete Ostkamp <fiete.ostkamp@telekom.de>
15 files changed:
so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/client/MulticloudClientIntegrationTest.java [new file with mode: 0644]
so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/rest/CnfAdapterRestIntegrationTest.java [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/allInstancesResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/createInstanceResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/emptyResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/getHealthCheckResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/getInstanceResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/getInstanceStatusResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/instanceStatusResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/notFoundResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/queryInstanceResourcesResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/startHealthCheckResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/subscriptionExistsResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/subscriptionResponse.json [new file with mode: 0644]
so-cnf-adapter-application/src/test/resources/__files/multicloud/upgradeInstanceResponse.json [new file with mode: 0644]

diff --git a/so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/client/MulticloudClientIntegrationTest.java b/so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/client/MulticloudClientIntegrationTest.java
new file mode 100644 (file)
index 0000000..794446d
--- /dev/null
@@ -0,0 +1,258 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP - SO
+ * ================================================================================
+ * Copyright (C) 2026 Deutsche Telekom. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.cnf.client;
+
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.onap.so.adapters.cnf.model.InstanceResponse;
+import org.onap.so.adapters.cnf.model.MulticloudInstanceRequest;
+import org.onap.so.adapters.cnf.model.healthcheck.K8sRbInstanceHealthCheck;
+import org.onap.so.adapters.cnf.model.healthcheck.K8sRbInstanceHealthCheckSimple;
+import org.onap.so.adapters.cnf.model.statuscheck.K8sRbInstanceStatus;
+import org.onap.so.adapters.cnf.model.synchronization.SubscriptionRequest;
+import org.onap.so.adapters.cnf.model.synchronization.SubscriptionResponse;
+import org.onap.so.client.exception.BadResponseException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.util.SocketUtils;
+
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+
+@SpringBootTest
+@RunWith(SpringRunner.class)
+public class MulticloudClientIntegrationTest {
+
+    private static int wireMockPort = SocketUtils.findAvailableTcpPort();
+
+    @ClassRule
+    public static WireMockRule wireMockRule = new WireMockRule(wireMockConfig().port(wireMockPort));
+
+    @Autowired
+    private MulticloudClient multicloudClient;
+
+    @Before
+    public void setUp() {
+        WireMock.reset();
+    }
+
+    @DynamicPropertySource
+    static void configureProperties(DynamicPropertyRegistry registry) {
+        registry.add("multicloud.endpoint", () -> "http://localhost:" + wireMockPort);
+        registry.add("spring.security.usercredentials[0].username", () -> "test");
+        registry.add("spring.security.usercredentials[0].password", () -> "test");
+        registry.add("spring.security.usercredentials[0].role", () -> "ACTUATOR");
+    }
+
+    @Test
+    public void thatAllInstancesCanBeRetrieved() throws BadResponseException {
+        WireMock.stubFor(WireMock.get(WireMock.urlEqualTo("/v1/instance"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/allInstancesResponse.json")));
+
+        List<InstanceResponse> instances = multicloudClient.getAllInstances();
+
+        assertEquals(2, instances.size());
+        assertEquals("inst-1", instances.get(0).getId());
+        assertEquals("inst-2", instances.get(1).getId());
+        WireMock.verify(WireMock.getRequestedFor(WireMock.urlEqualTo("/v1/instance"))
+                .withHeader("Accept", WireMock.equalTo("application/json")));
+    }
+
+    @Test
+    public void thatEmptyListReturnedWhenNoDocumentsFound() throws BadResponseException {
+        WireMock.stubFor(WireMock.get(WireMock.urlEqualTo("/v1/instance"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(500)
+                        .withBody("no documents for given ID")));
+
+        List<InstanceResponse> instances = multicloudClient.getAllInstances();
+
+        assertTrue(instances.isEmpty());
+    }
+
+    @Test
+    public void thatInstanceStatusCanBeRetrieved() throws BadResponseException {
+        WireMock.stubFor(WireMock.get(WireMock.urlEqualTo("/v1/instance/test-inst/status"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/instanceStatusResponse.json")));
+
+        K8sRbInstanceStatus status = multicloudClient.getInstanceStatus("test-inst");
+
+        assertEquals(3, status.getResourceCount());
+        assertTrue(status.isReady());
+        WireMock.verify(WireMock.getRequestedFor(WireMock.urlEqualTo("/v1/instance/test-inst/status")));
+    }
+
+    @Test
+    public void thatSubscriptionCanBeRegistered() throws BadResponseException {
+        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/v1/instance/test-inst/status/subscription"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/subscriptionResponse.json")));
+
+        SubscriptionRequest request = new SubscriptionRequest();
+        request.setName("test-sub");
+        request.setCallbackUrl("http://callback");
+        request.setMinNotifyInterval(10);
+
+        SubscriptionResponse response = multicloudClient.registerSubscription("test-inst", request);
+
+        assertEquals("test-sub", response.getName());
+        WireMock.verify(WireMock.postRequestedFor(
+                WireMock.urlEqualTo("/v1/instance/test-inst/status/subscription"))
+                .withHeader("Content-Type", WireMock.equalTo("application/json"))
+                .withRequestBody(WireMock.matchingJsonPath("$.name", WireMock.equalTo("test-sub")))
+                .withRequestBody(WireMock.matchingJsonPath("$['callback-url']",
+                        WireMock.equalTo("http://callback"))));
+    }
+
+    @Test
+    public void thatSubscriptionExistenceCanBeChecked() throws BadResponseException {
+        WireMock.stubFor(WireMock.get(
+                WireMock.urlEqualTo("/v1/instance/test-inst/status/subscription/test-sub"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/subscriptionExistsResponse.json")));
+
+        assertTrue(multicloudClient.hasSubscription("test-inst", "test-sub"));
+        WireMock.verify(WireMock.getRequestedFor(
+                WireMock.urlEqualTo("/v1/instance/test-inst/status/subscription/test-sub")));
+    }
+
+    @Test
+    public void thatHasSubscriptionReturnsFalseWhenNotFound() throws BadResponseException {
+        WireMock.stubFor(WireMock.get(
+                WireMock.urlEqualTo("/v1/instance/test-inst/status/subscription/missing"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(500)
+                        .withBody("no documents for given ID")));
+
+        assertFalse(multicloudClient.hasSubscription("test-inst", "missing"));
+    }
+
+    @Test
+    public void thatSubscriptionCanBeDeleted() throws BadResponseException {
+        WireMock.stubFor(WireMock.delete(
+                WireMock.urlEqualTo("/v1/instance/test-inst/status/subscription/test-sub"))
+                .willReturn(WireMock.aResponse().withStatus(200)));
+
+        multicloudClient.deleteSubscription("test-inst", "test-sub");
+
+        WireMock.verify(WireMock.deleteRequestedFor(
+                WireMock.urlEqualTo("/v1/instance/test-inst/status/subscription/test-sub")));
+    }
+
+    @Test
+    public void thatInstanceCanBeUpgraded() throws BadResponseException {
+        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/v1/instance/test-inst/upgrade"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/upgradeInstanceResponse.json")));
+
+        MulticloudInstanceRequest upgradeRequest = new MulticloudInstanceRequest();
+        upgradeRequest.setRbName("test-rb");
+        upgradeRequest.setRbVersion("v1");
+        upgradeRequest.setProfileName("default");
+        upgradeRequest.setCloudRegion("region-1");
+
+        String result = multicloudClient.upgradeInstance("test-inst", upgradeRequest);
+
+        assertNotNull(result);
+        assertTrue(result.contains("test-inst"));
+        WireMock.verify(WireMock.postRequestedFor(
+                WireMock.urlEqualTo("/v1/instance/test-inst/upgrade"))
+                .withRequestBody(WireMock.matchingJsonPath("$['rb-name']",
+                        WireMock.equalTo("test-rb")))
+                .withRequestBody(WireMock.matchingJsonPath("$['rb-version']",
+                        WireMock.equalTo("v1")))
+                .withRequestBody(WireMock.matchingJsonPath("$['profile-name']",
+                        WireMock.equalTo("default"))));
+    }
+
+    @Test
+    public void thatHealthCheckCanBeStarted() throws BadResponseException {
+        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/v1/instance/test-inst/healthcheck"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/startHealthCheckResponse.json")));
+
+        K8sRbInstanceHealthCheckSimple result =
+                multicloudClient.startInstanceHealthCheck("test-inst");
+
+        assertEquals("hc-1", result.getId());
+        assertEquals("RUNNING", result.getStatus());
+        WireMock.verify(WireMock.postRequestedFor(
+                WireMock.urlEqualTo("/v1/instance/test-inst/healthcheck")));
+    }
+
+    @Test
+    public void thatHealthCheckCanBeRetrieved() throws BadResponseException {
+        WireMock.stubFor(WireMock.get(
+                WireMock.urlEqualTo("/v1/instance/test-inst/healthcheck/hc-1"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/getHealthCheckResponse.json")));
+
+        K8sRbInstanceHealthCheck result =
+                multicloudClient.getInstanceHealthCheck("test-inst", "hc-1");
+
+        assertEquals("test-inst", result.getInstanceId());
+        assertEquals("hc-1", result.getHealthcheckId());
+        assertEquals("STOPPED", result.getStatus());
+    }
+
+    @Test
+    public void thatHealthCheckCanBeDeleted() throws BadResponseException {
+        WireMock.stubFor(WireMock.delete(
+                WireMock.urlEqualTo("/v1/instance/test-inst/healthcheck/hc-1"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withBody("")));
+
+        multicloudClient.deleteInstanceHealthCheck("test-inst", "hc-1");
+
+        WireMock.verify(WireMock.deleteRequestedFor(
+                WireMock.urlEqualTo("/v1/instance/test-inst/healthcheck/hc-1")));
+    }
+}
diff --git a/so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/rest/CnfAdapterRestIntegrationTest.java b/so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/rest/CnfAdapterRestIntegrationTest.java
new file mode 100644 (file)
index 0000000..84a6bfe
--- /dev/null
@@ -0,0 +1,276 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP - SO
+ * ================================================================================
+ * Copyright (C) 2026 Deutsche Telekom. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.cnf.rest;
+
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.onap.so.adapters.cnf.model.BpmnInstanceRequest;
+import org.onap.so.adapters.cnf.model.CheckInstanceRequest;
+import org.onap.so.adapters.cnf.model.InstanceRequest;
+import org.onap.so.adapters.cnf.model.upgrade.InstanceUpgradeRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.util.SocketUtils;
+
+import com.github.tomakehurst.wiremock.client.BasicCredentials;
+import com.github.tomakehurst.wiremock.client.VerificationException;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+
+/**
+ * Integration tests for {@link CnfAdapterRest} that start from HTTP requests
+ * to the controller endpoints and verify that the correct outbound requests
+ * are made to the Multicloud K8s Plugin (stubbed by WireMock).
+ */
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@RunWith(SpringRunner.class)
+public class CnfAdapterRestIntegrationTest {
+
+    private static int wireMockPort = SocketUtils.findAvailableTcpPort();
+
+    @ClassRule
+    public static WireMockRule wireMockRule = new WireMockRule(wireMockConfig().port(wireMockPort));
+
+    @Autowired
+    private TestRestTemplate restTemplate;
+
+    @Before
+    public void setUp() {
+        WireMock.reset();
+    }
+
+    @DynamicPropertySource
+    static void configureProperties(DynamicPropertyRegistry registry) {
+        registry.add("multicloud.endpoint", () -> "http://localhost:" + wireMockPort);
+        registry.add("spring.security.usercredentials[0].username", () -> "test");
+        registry.add("spring.security.usercredentials[0].password", () -> "test");
+        registry.add("spring.security.usercredentials[0].role", () -> "ACTUATOR");
+    }
+
+    @Test
+    public void thatInstanceCanBeCreated() {
+        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/v1/instance"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/createInstanceResponse.json")));
+
+        BpmnInstanceRequest request = new BpmnInstanceRequest();
+        request.setModelInvariantId("rb-name-1");
+        request.setModelCustomizationId("rb-version-1");
+        request.setK8sRBProfileName("profile-1");
+        request.setCloudRegionId("region-1");
+        request.setK8sRBInstanceReleaseName("test-release");
+
+        ResponseEntity<String> response = restTemplate.postForEntity(
+                "/api/cnf-adapter/v1/instance", request, String.class);
+
+        assertEquals(200, response.getStatusCodeValue());
+        assertNotNull(response.getBody());
+        assertTrue(response.getBody().contains("new-inst"));
+        WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/v1/instance"))
+                .withRequestBody(WireMock.matchingJsonPath("$['rb-name']",
+                        WireMock.equalTo("rb-name-1")))
+                .withRequestBody(WireMock.matchingJsonPath("$['rb-version']",
+                        WireMock.equalTo("rb-version-1")))
+                .withRequestBody(WireMock.matchingJsonPath("$['profile-name']",
+                        WireMock.equalTo("profile-1")))
+                .withRequestBody(WireMock.matchingJsonPath("$['cloud-region']",
+                        WireMock.equalTo("region-1"))));
+    }
+
+    @Test
+    public void thatInstanceCanBeRetrievedById() {
+        WireMock.stubFor(WireMock.get(WireMock.urlEqualTo("/v1/instance/inst-1"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/getInstanceResponse.json")));
+
+        ResponseEntity<String> response = restTemplate.getForEntity(
+                "/api/cnf-adapter/v1/instance/{instID}", String.class, "inst-1");
+
+        assertEquals(200, response.getStatusCodeValue());
+        assertTrue(response.getBody().contains("inst-1"));
+        WireMock.verify(WireMock.getRequestedFor(WireMock.urlEqualTo("/v1/instance/inst-1")));
+    }
+
+    @Test
+    public void thatInstanceCanBeDeleted() {
+        WireMock.stubFor(WireMock.delete(WireMock.urlEqualTo("/v1/instance/inst-1"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/emptyResponse.json")));
+
+        ResponseEntity<String> response = restTemplate.exchange(
+                "/api/cnf-adapter/v1/instance/{instID}", HttpMethod.DELETE,
+                null, String.class, "inst-1");
+
+        assertEquals(200, response.getStatusCodeValue());
+        WireMock.verify(WireMock.deleteRequestedFor(
+                WireMock.urlEqualTo("/v1/instance/inst-1")));
+    }
+
+    @Test
+    public void thatInstanceStatusCanBeRetrieved() {
+        WireMock.stubFor(WireMock.get(WireMock.urlEqualTo("/v1/instance/inst-1/status"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/getInstanceStatusResponse.json")));
+
+        ResponseEntity<String> response = restTemplate.getForEntity(
+                "/api/cnf-adapter/v1/instance/{instID}/status", String.class, "inst-1");
+
+        assertEquals(200, response.getStatusCodeValue());
+        assertTrue(response.getBody().contains("resourceCount"));
+        WireMock.verify(WireMock.getRequestedFor(
+                WireMock.urlEqualTo("/v1/instance/inst-1/status")));
+    }
+
+    @Test
+    public void thatInstanceResourcesCanBeQueried() {
+        WireMock.stubFor(WireMock.get(WireMock.urlPathEqualTo("/v1/instance/inst-1/query"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/queryInstanceResourcesResponse.json")));
+
+        ResponseEntity<String> response = restTemplate.getForEntity(
+                "/api/cnf-adapter/v1/instance/{instanceId}/query?Kind={kind}&ApiVersion={apiVersion}",
+                String.class, "inst-1", "Pod", "v1");
+
+        assertEquals(200, response.getStatusCodeValue());
+        assertTrue(response.getBody().contains("test-pod"));
+        WireMock.verify(WireMock.getRequestedFor(
+                WireMock.urlPathEqualTo("/v1/instance/inst-1/query"))
+                .withQueryParam("Kind", WireMock.equalTo("Pod"))
+                .withQueryParam("ApiVersion", WireMock.equalTo("v1")));
+    }
+
+    @Test
+    public void thatInstanceCanBeUpgraded() {
+        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/v1/instance/test-inst/upgrade"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/upgradeInstanceResponse.json")));
+
+        InstanceUpgradeRequest upgradeRequest = new InstanceUpgradeRequest();
+        upgradeRequest.setModelInvariantId("rb-name-1");
+        upgradeRequest.setModelCustomizationId("rb-version-1");
+        upgradeRequest.setK8sRBProfileName("default");
+        upgradeRequest.setCloudRegionId("region-1");
+
+        ResponseEntity<String> response = restTemplate.postForEntity(
+                "/api/cnf-adapter/v1/instance/{instanceID}/upgrade",
+                upgradeRequest, String.class, "test-inst");
+
+        assertEquals(200, response.getStatusCodeValue());
+        assertNotNull(response.getBody());
+        WireMock.verify(WireMock.postRequestedFor(
+                WireMock.urlEqualTo("/v1/instance/test-inst/upgrade"))
+                .withRequestBody(WireMock.matchingJsonPath("$['rb-name']",
+                        WireMock.equalTo("rb-name-1")))
+                .withRequestBody(WireMock.matchingJsonPath("$['profile-name']",
+                        WireMock.equalTo("default"))));
+    }
+
+    @Test
+    public void thatStatusCheckCallsMulticloudAndSendsCallback() throws InterruptedException {
+        WireMock.stubFor(WireMock.get(WireMock.urlEqualTo("/v1/instance/test-inst/status"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(200)
+                        .withHeader("Content-Type", "application/json")
+                        .withBodyFile("multicloud/instanceStatusResponse.json")));
+
+        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/callback"))
+                .willReturn(WireMock.aResponse().withStatus(200)));
+
+        CheckInstanceRequest statusCheckRequest = new CheckInstanceRequest();
+        InstanceRequest instanceRequest = new InstanceRequest();
+        instanceRequest.setInstanceId("test-inst");
+        statusCheckRequest.setInstances(Collections.singletonList(instanceRequest));
+        statusCheckRequest.setCallbackUrl("http://localhost:" + wireMockPort + "/callback");
+
+        ResponseEntity<String> response = restTemplate.postForEntity(
+                "/api/cnf-adapter/v1/statuscheck", statusCheckRequest, String.class);
+
+        assertEquals(202, response.getStatusCodeValue());
+
+        awaitWireMockCallback("/callback");
+
+        WireMock.verify(WireMock.getRequestedFor(
+                WireMock.urlEqualTo("/v1/instance/test-inst/status")));
+        WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/callback"))
+                .withHeader("Content-Type", WireMock.containing("text/plain"))
+                .withBasicAuth(new BasicCredentials("test", "test")));
+    }
+
+    @Test
+    public void thatCreateInstanceReturns500OnMulticloud404() {
+        WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/v1/instance"))
+                .willReturn(WireMock.aResponse()
+                        .withStatus(404)
+                        .withBodyFile("multicloud/notFoundResponse.json")));
+
+        BpmnInstanceRequest request = new BpmnInstanceRequest();
+        request.setModelInvariantId("rb-name-1");
+        request.setModelCustomizationId("rb-version-1");
+        request.setK8sRBProfileName("profile-1");
+        request.setCloudRegionId("region-1");
+
+        ResponseEntity<String> response = restTemplate.postForEntity(
+                "/api/cnf-adapter/v1/instance", request, String.class);
+
+        assertEquals(500, response.getStatusCodeValue());
+    }
+
+    /**
+     * Poll WireMock until the expected callback POST arrives (max 10 seconds).
+     */
+    private void awaitWireMockCallback(String url) throws InterruptedException {
+        for (int i = 0; i < 50; i++) {
+            try {
+                WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo(url)));
+                return;
+            } catch (VerificationException e) {
+                Thread.sleep(200);
+            }
+        }
+    }
+}
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/allInstancesResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/allInstancesResponse.json
new file mode 100644 (file)
index 0000000..385efc0
--- /dev/null
@@ -0,0 +1,12 @@
+[
+  {
+    "id": "inst-1",
+    "namespace": "default",
+    "release-name": "rel-1"
+  },
+  {
+    "id": "inst-2",
+    "namespace": "test",
+    "release-name": "rel-2"
+  }
+]
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/createInstanceResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/createInstanceResponse.json
new file mode 100644 (file)
index 0000000..e97475d
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "id": "new-inst",
+  "namespace": "default",
+  "release-name": "test-release"
+}
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/emptyResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/emptyResponse.json
new file mode 100644 (file)
index 0000000..0967ef4
--- /dev/null
@@ -0,0 +1 @@
+{}
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/getHealthCheckResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/getHealthCheckResponse.json
new file mode 100644 (file)
index 0000000..21e3192
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "instance-id": "test-inst",
+  "healthcheck-id": "hc-1",
+  "status": "STOPPED"
+}
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/getInstanceResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/getInstanceResponse.json
new file mode 100644 (file)
index 0000000..7dd7ee3
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "id": "inst-1",
+  "namespace": "default"
+}
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/getInstanceStatusResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/getInstanceStatusResponse.json
new file mode 100644 (file)
index 0000000..a7c5836
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "resourceCount": 5,
+  "ready": false
+}
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/instanceStatusResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/instanceStatusResponse.json
new file mode 100644 (file)
index 0000000..8024029
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "resourceCount": 3,
+  "ready": true
+}
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/notFoundResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/notFoundResponse.json
new file mode 100644 (file)
index 0000000..5244ecd
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "error": "not found"
+}
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/queryInstanceResourcesResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/queryInstanceResourcesResponse.json
new file mode 100644 (file)
index 0000000..ebb52e5
--- /dev/null
@@ -0,0 +1,10 @@
+[
+  {
+    "Name": "test-pod",
+    "GVK": {
+      "Group": "",
+      "Version": "v1",
+      "Kind": "Pod"
+    }
+  }
+]
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/startHealthCheckResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/startHealthCheckResponse.json
new file mode 100644 (file)
index 0000000..b9917e7
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "healthcheck-id": "hc-1",
+  "status": "RUNNING"
+}
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/subscriptionExistsResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/subscriptionExistsResponse.json
new file mode 100644 (file)
index 0000000..847150f
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "name": "test-sub"
+}
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/subscriptionResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/subscriptionResponse.json
new file mode 100644 (file)
index 0000000..7dd3484
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "name": "test-sub",
+  "callback-url": "http://callback"
+}
diff --git a/so-cnf-adapter-application/src/test/resources/__files/multicloud/upgradeInstanceResponse.json b/so-cnf-adapter-application/src/test/resources/__files/multicloud/upgradeInstanceResponse.json
new file mode 100644 (file)
index 0000000..b7e4461
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "id": "test-inst"
+}