Add K6 test for writing without relationships 76/139176/2
authorFiete Ostkamp <Fiete.Ostkamp@telekom.de>
Wed, 9 Oct 2024 06:24:38 +0000 (08:24 +0200)
committerFiete Ostkamp <Fiete.Ostkamp@telekom.de>
Thu, 10 Oct 2024 06:57:53 +0000 (08:57 +0200)
- create test that writes pservers via the LegacyMoxyConsumer (not bulk)

Issue-ID: AAI-4013
Change-Id: I0fe4116ebc01ed4f8a5c0807113a08a2424d24ba
Signed-off-by: Fiete Ostkamp <Fiete.Ostkamp@telekom.de>
aai-resources/src/test/java/org/onap/aai/it/performance/K6ReadTest.java [new file with mode: 0644]
aai-resources/src/test/java/org/onap/aai/it/performance/K6WriteTest.java [moved from aai-resources/src/test/java/org/onap/aai/it/performance/K6PerformanceTest.java with 74% similarity]
aai-resources/src/test/resources/k6/readWithoutRelations.js [moved from aai-resources/src/test/resources/k6/test.js with 100% similarity]
aai-resources/src/test/resources/k6/writeWithoutRelations.js [new file with mode: 0644]

diff --git a/aai-resources/src/test/java/org/onap/aai/it/performance/K6ReadTest.java b/aai-resources/src/test/java/org/onap/aai/it/performance/K6ReadTest.java
new file mode 100644 (file)
index 0000000..0dee841
--- /dev/null
@@ -0,0 +1,183 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2024 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.aai.it.performance;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.janusgraph.core.JanusGraph;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.onap.aai.db.props.AAIProperties;
+import org.onap.aai.dbmap.AAIGraph;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.testcontainers.containers.output.WaitingConsumer;
+import org.testcontainers.junit.jupiter.Testcontainers;
+import org.testcontainers.k6.K6Container;
+import org.testcontainers.utility.MountableFile;
+
+import lombok.SneakyThrows;
+
+@Testcontainers
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+@EnableAutoConfiguration(exclude={CassandraDataAutoConfiguration.class, CassandraAutoConfiguration.class}) // there is no running cassandra instance for the test
+public class K6ReadTest {
+
+  private static final Logger logger = LoggerFactory.getLogger(K6ReadTest.class);
+  private static final long nPservers = 10;
+
+  @LocalServerPort
+  private int port;
+
+  @BeforeEach
+  public void setup() {
+    if (!AAIGraph.isInit()) {
+      AAIGraph.getInstance();
+
+      long startTime = System.currentTimeMillis();
+      logger.info("Creating pserver nodes");
+      loadPerformanceTestData();
+      long endTime = System.currentTimeMillis();
+      logger.info("Created pserver nodes in {} seconds", (endTime - startTime) / 1000);
+    }
+  }
+
+  @AfterAll
+  public static void cleanup() {
+    JanusGraph graph = AAIGraph.getInstance().getGraph();
+    graph.traversal().V().has("aai-node-type", "pserver").drop().iterate();
+    graph.tx().commit();
+  }
+
+  @Test
+  public void readWithoutRelationsTest() throws Exception {
+    int testDuration = 5;
+
+    K6Container container = new K6Container("grafana/k6:0.49.0")
+        .withNetworkMode("host")
+        .withAccessToHost(true)
+        .withTestScript(MountableFile.forClasspathResource("k6/readWithoutRelations.js"))
+        .withScriptVar("API_PORT", String.valueOf(port))
+        .withScriptVar("API_VERSION", "v29")
+        .withScriptVar("DURATION_SECONDS", String.valueOf(testDuration))
+        .withScriptVar("N_PSERVERS", String.valueOf(nPservers))
+        .withCmdOptions("--quiet", "--no-usage-report");
+    container.start();
+
+    WaitingConsumer consumer = new WaitingConsumer();
+    container.followOutput(consumer);
+
+    // Wait for test script results to be collected
+    try {
+      consumer.waitUntil(
+          frame -> {
+            return frame.getUtf8String().contains("iteration_duration");
+          },
+          testDuration + 10,
+          TimeUnit.SECONDS);
+    } catch (Exception e) {
+      logger.error(container.getLogs());
+    }
+
+    logger.debug(container.getLogs());
+    assertThat(container.getLogs(), containsString("✓ status was 200"));
+    assertThat(container.getLogs(), containsString("✓ returned correct number of results"));
+    assertThat(container.getLogs(), containsString("✓ http_req_duration"));
+    assertThat(container.getLogs(), containsString("✓ http_req_failed"));
+    container.stop();
+
+  }
+
+  @Test
+  @Disabled
+  public void writeWithoutRelations() throws Exception {
+    int testDuration = 5;
+
+    K6Container container = new K6Container("grafana/k6:0.49.0")
+        .withNetworkMode("host")
+        .withAccessToHost(true)
+        .withTestScript(MountableFile.forClasspathResource("k6/writeWithoutRelations.js"))
+        .withScriptVar("API_PORT", String.valueOf(port))
+        .withScriptVar("API_VERSION", "v29")
+        .withScriptVar("DURATION_SECONDS", String.valueOf(testDuration))
+        .withScriptVar("N_PSERVERS", String.valueOf(nPservers))
+        .withCmdOptions("--quiet", "--no-usage-report");
+    container.start();
+
+    WaitingConsumer consumer = new WaitingConsumer();
+    container.followOutput(consumer);
+
+    // Wait for test script results to be collected
+    try {
+      consumer.waitUntil(
+          frame -> {
+            return frame.getUtf8String().contains("iteration_duration");
+          },
+          testDuration + 10,
+          TimeUnit.SECONDS);
+    } catch (Exception e) {
+      // log the container stdout in case of failure in the test script
+      logger.error(container.getLogs());
+    }
+
+    String report = container.getLogs().substring(container.getLogs().indexOf("✓ status was 201"));
+    logger.info(report);
+    assertThat(report, containsString("✓ status was 201"));
+    assertThat(report, containsString("✓ http_req_duration"));
+    assertThat(report, containsString("✓ http_req_failed"));
+    container.stop();
+  }
+
+  @SneakyThrows
+  public static void loadPerformanceTestData() {
+    JanusGraph graph = AAIGraph.getInstance().getGraph();
+    GraphTraversalSource g = graph.traversal();
+    long n = nPservers;
+    for (long i = 0; i < n; i++) {
+      createPServer(g, i);
+    }
+    graph.tx().commit();
+  }
+
+  private static void createPServer(GraphTraversalSource g, long i) {
+    String hostname = "hostname" + i;
+    String uri = "/cloud-infrastructure/pservers/pserver/" + hostname;
+    g.addV()
+        .property("aai-node-type", "pserver")
+        .property("hostname", hostname)
+        .property("resource-version", UUID.randomUUID().toString())
+        .property(AAIProperties.AAI_URI, uri)
+        .next();
+  }
+}
@@ -30,6 +30,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSo
 import org.janusgraph.core.JanusGraph;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.onap.aai.ResourcesApp;
 import org.onap.aai.db.props.AAIProperties;
@@ -52,9 +53,9 @@ import lombok.SneakyThrows;
 @Testcontainers
 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
 @EnableAutoConfiguration(exclude={CassandraDataAutoConfiguration.class, CassandraAutoConfiguration.class}) // there is no running cassandra instance for the test
-public class K6PerformanceTest {
+public class K6WriteTest {
 
-  private static final Logger logger = LoggerFactory.getLogger(ResourcesApp.class.getName());
+  private static final Logger logger = LoggerFactory.getLogger(K6ReadTest.class);
   private static final long nPservers = 10;
 
   @LocalServerPort
@@ -81,38 +82,43 @@ public class K6PerformanceTest {
   }
 
   @Test
-  public void k6StandardTest() throws Exception {
+  @Disabled
+  public void writeWithoutRelations() throws Exception {
     int testDuration = 5;
 
-    try (
-        K6Container container = new K6Container("grafana/k6:0.49.0")
-            .withNetworkMode("host")
-            .withAccessToHost(true)
-            .withTestScript(MountableFile.forClasspathResource("k6/test.js"))
-            .withScriptVar("API_PORT", String.valueOf(port))
-            .withScriptVar("API_VERSION", "v29")
-            .withScriptVar("DURATION_SECONDS", String.valueOf(testDuration))
-            .withScriptVar("N_PSERVERS", String.valueOf(nPservers))
-            .withCmdOptions("--quiet", "--no-usage-report");) {
-      container.start();
-
-      WaitingConsumer consumer = new WaitingConsumer();
-      container.followOutput(consumer);
-
-      // Wait for test script results to be collected
+    K6Container container = new K6Container("grafana/k6:0.49.0")
+        .withNetworkMode("host")
+        .withAccessToHost(true)
+        .withTestScript(MountableFile.forClasspathResource("k6/writeWithoutRelations.js"))
+        .withScriptVar("API_PORT", String.valueOf(port))
+        .withScriptVar("API_VERSION", "v29")
+        .withScriptVar("DURATION_SECONDS", String.valueOf(testDuration))
+        .withScriptVar("N_PSERVERS", String.valueOf(nPservers))
+        .withCmdOptions("--quiet", "--no-usage-report");
+    container.start();
+
+    WaitingConsumer consumer = new WaitingConsumer();
+    container.followOutput(consumer);
+
+    // Wait for test script results to be collected
+    try {
       consumer.waitUntil(
           frame -> {
             return frame.getUtf8String().contains("iteration_duration");
           },
-          testDuration + 30,
+          testDuration + 10,
           TimeUnit.SECONDS);
-
-      logger.debug(container.getLogs());
-      assertThat(container.getLogs(), containsString("✓ status was 200"));
-      assertThat(container.getLogs(), containsString("✓ returned correct number of results"));
-      assertThat(container.getLogs(), containsString("✓ http_req_duration"));
-      assertThat(container.getLogs(), containsString("✓ http_req_failed"));
+    } catch (Exception e) {
+      // log the container stdout in case of failure in the test script
+      logger.error(container.getLogs());
     }
+
+    String report = container.getLogs().substring(container.getLogs().indexOf("✓ status was 201"));
+    logger.info(report);
+    assertThat(report, containsString("✓ status was 201"));
+    assertThat(report, containsString("✓ http_req_duration"));
+    assertThat(report, containsString("✓ http_req_failed"));
+    container.stop();
   }
 
   @SneakyThrows
diff --git a/aai-resources/src/test/resources/k6/writeWithoutRelations.js b/aai-resources/src/test/resources/k6/writeWithoutRelations.js
new file mode 100644 (file)
index 0000000..bb4b839
--- /dev/null
@@ -0,0 +1,111 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2024 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=========================================================
+ */
+import http from "k6/http";
+import { check } from "k6";
+import { randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.3.0/index.js';
+
+export const options = {
+  vus: 3,
+  duration: `${__ENV.DURATION_SECONDS}s`,
+  thresholds: {
+    http_req_failed: ["rate<0.01"], // http errors should be less than 1%
+    http_req_duration: [
+      "p(99)<3000",
+      "p(90)<2000",
+      "avg<70",
+      "med<70",
+      "min<1000",
+    ],
+  },
+  insecureSkipTLSVerify: true,
+};
+
+function generatePServer(someInt) {
+  return JSON.stringify({
+      'hostname': someInt,
+      'ptnii-equip-name': `example-ptnii-equip-name-val-${someInt}`,
+      'number-of-cpus': someInt,
+      'disk-in-gigabytes': someInt,
+      'ram-in-megabytes': someInt,
+      'equip-type': `example-equip-type-val-${someInt}`,
+      'equip-vendor': `example-equip-vendor-val-${someInt}`,
+      'equip-model': `example-equip-model-val-${someInt}`,
+      'fqdn': `example-fqdn-val-${someInt}`,
+      'pserver-selflink': `example-pserver-selflink-val-${someInt}`,
+      'ipv4-oam-address': `example-ipv4-oam-address-val-${someInt}`,
+      'serial-number': `example-serial-number-val-${someInt}`,
+      'ipaddress-v4-loopback-0': `example-ipaddress-v4-loopback0-val-${someInt}`,
+      'ipaddress-v6-loopback-0': `example-ipaddress-v6-loopback0-val-${someInt}`,
+      'ipaddress-v4-aim': `example-ipaddress-v4-aim-val-${someInt}`,
+      'ipaddress-v6-aim': `example-ipaddress-v6-aim-val-${someInt}`,
+      'ipaddress-v6-oam': `example-ipaddress-v6-oam-val-${someInt}`,
+      'inv-status': `example-inv-status-val-${someInt}`,
+      'pserver-id': `example-pserver-id-val-${someInt}`,
+      'internet-topology': `example-internet-topology-val-${someInt}`,
+      'in-maint': true,
+      'pserver-name2': `example-pserver-name2-val-${someInt}`,
+      'purpose': `example-purpose-val-${someInt}`,
+      'prov-status': `example-prov-status-val-${someInt}`,
+      'management-option': `example-management-option-val-${someInt}`,
+      'host-profile': `example-host-profile-val-${someInt}`
+  });
+}
+
+const baseUrl = `http://localhost:${__ENV.API_PORT}/aai/${__ENV.API_VERSION}`;
+const path = `/cloud-infrastructure/pservers/pserver`;
+const url = baseUrl + path;
+const encodedCredentials = 'QUFJOkFBSQ==';
+const httpOpts = {
+  headers: {
+    Accept: "application/json",
+    Authorization: `Basic ${encodedCredentials}`,
+    "X-FromAppId": "k6",
+    "X-TransactionId": "someTransaction",
+  },
+};
+
+export function setup() {
+  // Perform a warmup with 100 requests
+  for (let i = 0; i < 100; i++) {
+      const someInt = randomIntBetween(10000, 1000000);
+      const payload = generatePServer(someInt);
+      const pserverUrl = url + `/${someInt}`
+      const res = http.put(pserverUrl, payload, httpOpts);
+
+      if (res.status != 201) {
+        console.error(res);
+      }
+  }
+}
+
+export default function () {
+  const someInt = randomIntBetween(10000, 1000000);
+  const pserverUrl = url + `/${someInt}`
+  const payload = generatePServer();
+  const res = http.put(pserverUrl, payload, httpOpts);
+
+  if (res.status != 201) {
+    console.error(res);
+  }
+
+  check(res, {
+    "status was 201": (r) => r.status == 201,
+  });
+}