test: RED - Expectations for `HaproxyDataPlaneApiShardingPublischerStrategy`
authorKai Moritz <kai@juplo.de>
Mon, 18 Mar 2024 07:31:39 +0000 (08:31 +0100)
committerKai Moritz <kai@juplo.de>
Fri, 22 Mar 2024 16:39:20 +0000 (17:39 +0100)
pom.xml
src/main/java/de/juplo/kafka/chat/backend/implementation/haproxy/HaproxyDataPlaneApiShardingPublisherStrategy.java [new file with mode: 0644]
src/test/java/de/juplo/kafka/chat/backend/implementation/haproxy/HaproxyDataPlaneApiShardingPublisherStrategyTest.java [new file with mode: 0644]
src/test/resources/de/juplo/kafka/chat/backend/implementation/haproxy/maps__get.json [new file with mode: 0644]
src/test/resources/de/juplo/kafka/chat/backend/implementation/haproxy/maps_entries__4__put.json [new file with mode: 0644]
src/test/resources/de/juplo/kafka/chat/backend/implementation/haproxy/maps_entries__4__put__error.json [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
index 13a0e7c..50310cf 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,7 @@
   <properties>
     <java.version>17</java.version>
     <assertj-reactor.version>1.0.8</assertj-reactor.version>
+    <inject-resources-junit-jupiter.version>0.3.3</inject-resources-junit-jupiter.version>
   </properties>
   <dependencies>
     <dependency>
       <artifactId>spring-kafka-test</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.squareup.okhttp3</groupId>
+      <artifactId>mockwebserver</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.hosuaby</groupId>
+      <artifactId>inject-resources-junit-jupiter</artifactId>
+      <version>${inject-resources-junit-jupiter.version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/src/main/java/de/juplo/kafka/chat/backend/implementation/haproxy/HaproxyDataPlaneApiShardingPublisherStrategy.java b/src/main/java/de/juplo/kafka/chat/backend/implementation/haproxy/HaproxyDataPlaneApiShardingPublisherStrategy.java
new file mode 100644 (file)
index 0000000..949104a
--- /dev/null
@@ -0,0 +1,31 @@
+package de.juplo.kafka.chat.backend.implementation.haproxy;
+
+import de.juplo.kafka.chat.backend.domain.ShardingPublisherStrategy;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+
+@Slf4j
+public class HaproxyDataPlaneApiShardingPublisherStrategy implements ShardingPublisherStrategy
+{
+  @Getter
+  private final int mapId;
+
+
+  public HaproxyDataPlaneApiShardingPublisherStrategy(
+      WebClient webClient,
+      String mapPath,
+      String instanceId)
+  {
+    this.mapId = 0;
+  }
+
+
+  @Override
+  public Mono<String> publishOwnership(int shard)
+  {
+    return Mono.empty();
+  }
+}
diff --git a/src/test/java/de/juplo/kafka/chat/backend/implementation/haproxy/HaproxyDataPlaneApiShardingPublisherStrategyTest.java b/src/test/java/de/juplo/kafka/chat/backend/implementation/haproxy/HaproxyDataPlaneApiShardingPublisherStrategyTest.java
new file mode 100644 (file)
index 0000000..87f707e
--- /dev/null
@@ -0,0 +1,147 @@
+package de.juplo.kafka.chat.backend.implementation.haproxy;
+
+import com.adelean.inject.resources.junit.jupiter.GivenTextResource;
+import com.adelean.inject.resources.junit.jupiter.TestWithResources;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+@TestWithResources
+public class HaproxyDataPlaneApiShardingPublisherStrategyTest
+{
+  final static String MAP_PATH = "/usr/local/etc/haproxy/sharding.map";
+  final static String INSTANCE_ID = "backend_3";
+  final static int SHARD = 4;
+
+
+  MockWebServer mockHaproxy;
+  WebClient webClient;
+
+  @GivenTextResource("de/juplo/kafka/chat/backend/implementation/haproxy/maps__get.json")
+  String maps__get;
+  @GivenTextResource("de/juplo/kafka/chat/backend/implementation/haproxy/maps_entries__4__put.json")
+  String maps_entries__4__put;
+  @GivenTextResource("de/juplo/kafka/chat/backend/implementation/haproxy/maps_entries__4__put__error.json")
+  String maps_entries__4__put__error;
+
+
+  @BeforeEach
+  void setUp() throws IOException
+  {
+    mockHaproxy = new MockWebServer();
+    mockHaproxy.start();
+    webClient = WebClient
+        .builder()
+        .baseUrl(String.format("http://localhost:%s/v2", mockHaproxy.getPort()))
+        .build();
+  }
+
+  @AfterEach
+  void tearDown() throws IOException
+  {
+    mockHaproxy.shutdown();
+  }
+
+
+  @DisplayName("Requests the available maps from HAProxy via the expected path on instanciation")
+  @Test
+  void testRequestsMapsFromHaproxyViaTheExpectedPathOnInstanciation() throws InterruptedException
+  {
+    // Given
+    mockHaproxy.enqueue(new MockResponse()
+        .setStatus("HTTP/1.1 200 OK")
+        .setBody(maps__get)
+        .addHeader("Content-Type", "application/json"));
+
+    // When
+    HaproxyDataPlaneApiShardingPublisherStrategy shardingPublisherStrategy =
+        new HaproxyDataPlaneApiShardingPublisherStrategy(webClient, MAP_PATH, INSTANCE_ID);
+
+    // Then
+    RecordedRequest recordedRequest = mockHaproxy.takeRequest(1l, TimeUnit.SECONDS);
+    assertThat(recordedRequest.getPath())
+        .isEqualTo("/v2/services/haproxy/runtime/maps?include_unmanaged=true");
+  }
+
+  @DisplayName("Detects the expected map-ID on instanciation")
+  @Test
+  void testDetectsExpectedIdForMapOnInstanciation()
+  {
+    // Given
+    mockHaproxy.enqueue(new MockResponse()
+        .setStatus("HTTP/1.1 200 OK")
+        .setBody(maps__get)
+        .addHeader("Content-Type", "application/json"));
+
+    // When
+    HaproxyDataPlaneApiShardingPublisherStrategy shardingPublisherStrategy =
+        new HaproxyDataPlaneApiShardingPublisherStrategy(webClient, MAP_PATH, INSTANCE_ID);
+
+    // Then
+    assertThat(shardingPublisherStrategy.getMapId())
+        .isEqualTo(4);
+  }
+
+  @DisplayName("The expected result is yielded on successful publishing")
+  @Test
+  void testExpectedResultOnSuccessfulPublishing()
+  {
+    // Given
+    mockHaproxy.enqueue(new MockResponse()
+        .setStatus("HTTP/1.1 200 OK")
+        .setBody(maps__get)
+        .addHeader("Content-Type", "application/json"));
+    mockHaproxy.enqueue(new MockResponse()
+        .setStatus("HTTP/1.1 200 OK")
+        .setBody(maps_entries__4__put)
+        .addHeader("Content-Type", "application/json"));
+
+    // When
+    HaproxyDataPlaneApiShardingPublisherStrategy shardingPublisherStrategy =
+        new HaproxyDataPlaneApiShardingPublisherStrategy(webClient, MAP_PATH, INSTANCE_ID);
+    Mono<String> result = shardingPublisherStrategy.publishOwnership(SHARD);
+
+    // Then
+    assertThat(result.block(Duration.ofSeconds(1)))
+        .isEqualTo(INSTANCE_ID);
+  }
+
+  @DisplayName("The expected error is raised on failed publishing")
+  @Test
+  void testExpectedResultOnFailedPublishing()
+  {
+    // Given
+    mockHaproxy.enqueue(new MockResponse()
+        .setStatus("HTTP/1.1 200 OK")
+        .setBody(maps__get)
+        .addHeader("Content-Type", "application/json"));
+    mockHaproxy.enqueue(new MockResponse()
+        .setStatus("HTTP/1.1 400 Bad Request")
+        .setBody(maps_entries__4__put__error)
+        .addHeader("Content-Type", "application/json"));
+
+    // When
+    HaproxyDataPlaneApiShardingPublisherStrategy shardingPublisherStrategy =
+        new HaproxyDataPlaneApiShardingPublisherStrategy(webClient, MAP_PATH, INSTANCE_ID);
+    Mono<String> result = shardingPublisherStrategy
+        .publishOwnership(SHARD)
+        .onErrorResume(throwable -> Mono.just(throwable.getMessage()));
+
+    // Then
+    assertThat(result.block(Duration.ofSeconds(1)))
+        .isEqualTo("Evil Error -- BOOM!");
+  }
+}
diff --git a/src/test/resources/de/juplo/kafka/chat/backend/implementation/haproxy/maps__get.json b/src/test/resources/de/juplo/kafka/chat/backend/implementation/haproxy/maps__get.json
new file mode 100644 (file)
index 0000000..a02728b
--- /dev/null
@@ -0,0 +1,7 @@
+[
+    {
+        "description": "pattern loaded from file '/usr/local/etc/haproxy/sharding.map' used by map at file '/usr/local/etc/haproxy/haproxy.cfg' line 27. curr_ver=0 next_ver=0 entry_cnt=10",
+        "file": "/usr/local/etc/haproxy/sharding.map",
+        "id": "4"
+    }
+]
diff --git a/src/test/resources/de/juplo/kafka/chat/backend/implementation/haproxy/maps_entries__4__put.json b/src/test/resources/de/juplo/kafka/chat/backend/implementation/haproxy/maps_entries__4__put.json
new file mode 100644 (file)
index 0000000..3409ea3
--- /dev/null
@@ -0,0 +1,4 @@
+{
+    "key": "4",
+    "value": "backend_3"
+}
diff --git a/src/test/resources/de/juplo/kafka/chat/backend/implementation/haproxy/maps_entries__4__put__error.json b/src/test/resources/de/juplo/kafka/chat/backend/implementation/haproxy/maps_entries__4__put__error.json
new file mode 100644 (file)
index 0000000..11a0e7f
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "code": 666,
+    "message": "Evil Error -- BOOM!",
+    "any-key": "Some additional information"
+}