CASSANDRASC-90 Token-ranges endpoint fix to unwrap token-ranges by the partitioner's boundary for a single node clusters

Patch by Arjun Ashok; Reviewed by Francisco Guerrero, Yifan Cai for CASSANDRASC-90
diff --git a/CHANGES.txt b/CHANGES.txt
index d61a23f..ad51d93 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
 1.0.0
 -----
+ * Fix Sidecar TokenRangeReplicas endpoint to unwrap the token-range by partitioner's range for a single node clusters (CASSANDRASC-90)
  * Expose TTL option for the create snapshot endpoint (CASSANDRASC-85)
  * Allow DriverUtils to be pluggable (CASSANDRASC-88)
  * Add JMX health checks during the periodic health checks (CASSANDRASC-87)
diff --git a/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicas.java b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicas.java
index 43fe5d8..a46437c 100644
--- a/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicas.java
+++ b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicas.java
@@ -69,7 +69,7 @@
                                                                       Partitioner partitioner,
                                                                       Set<String> replicaSet)
     {
-        if (start.compareTo(end) > 0)
+        if (start.compareTo(end) >= 0)
         {
             return unwrapRange(start, end, partitioner, replicaSet);
         }
@@ -216,10 +216,11 @@
      */
     public static List<TokenRangeReplicas> normalize(List<TokenRangeReplicas> ranges)
     {
-
         if (ranges.stream().noneMatch(r -> r.partitioner.minToken.compareTo(r.start()) == 0))
         {
-            LOGGER.warn("{} based minToken does not exist in the token ranges", Partitioner.class.getName());
+            LOGGER.warn("{} based minToken does not exist in the token ranges", ranges.stream()
+                                                                                      .findFirst()
+                                                                                      .get().partitioner.name());
         }
 
         return deoverlap(ranges);
diff --git a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BaseTokenRangeIntegrationTest.java b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BaseTokenRangeIntegrationTest.java
index 250bdee..bc86a8b 100644
--- a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BaseTokenRangeIntegrationTest.java
+++ b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BaseTokenRangeIntegrationTest.java
@@ -37,6 +37,7 @@
 import io.vertx.core.buffer.Buffer;
 import io.vertx.ext.web.client.HttpResponse;
 import io.vertx.junit5.VertxTestContext;
+import org.apache.cassandra.dht.Murmur3Partitioner;
 import org.apache.cassandra.distributed.UpgradeableCluster;
 import org.apache.cassandra.distributed.api.IInstanceConfig;
 import org.apache.cassandra.distributed.api.TokenSupplier;
@@ -275,6 +276,8 @@
         assertThat(mappingResponse).isNotNull();
         assertThat(mappingResponse.readReplicas()).isNotNull();
         assertThat(mappingResponse.writeReplicas()).isNotNull();
+        validateRanges(mappingResponse.writeReplicas());
+        validateRanges(mappingResponse.readReplicas());
         TokenRangeReplicasResponse.ReplicaInfo readReplica = mappingResponse.readReplicas().get(0);
         assertThat(readReplica.replicasByDatacenter()).isNotNull().hasSize(dcReplication.size());
         TokenRangeReplicasResponse.ReplicaInfo writeReplica = mappingResponse.writeReplicas().get(0);
@@ -291,4 +294,17 @@
             .isGreaterThanOrEqualTo(replicationFactor);
         }
     }
+
+    private void validateRanges(List<TokenRangeReplicasResponse.ReplicaInfo> replicaRanges)
+    {
+        // Ranges should not be empty
+        replicaRanges.forEach(r -> assertThat(r.start()).isNotEqualTo(r.end()));
+        // Ranges should include partitioner start and end
+        assertThat(replicaRanges.stream()
+                                .map(TokenRangeReplicasResponse.ReplicaInfo::start)
+                                .anyMatch(s -> s.equals(Murmur3Partitioner.MINIMUM.toString()))).isTrue();
+        assertThat(replicaRanges.stream()
+                                .map(TokenRangeReplicasResponse.ReplicaInfo::end)
+                                .anyMatch(s -> s.equals(Long.toString(Murmur3Partitioner.MAXIMUM)))).isTrue();
+    }
 }
diff --git a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicRf1Test.java b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicRf1Test.java
index 4a63ef1..bd507dd 100644
--- a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicRf1Test.java
+++ b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicRf1Test.java
@@ -50,4 +50,17 @@
             context.completeNow();
         });
     }
+
+    @CassandraIntegrationTest()
+    void retrieveMappingSingleNodeRf1(VertxTestContext context) throws Exception
+    {
+        createTestKeyspace();
+        retrieveMappingWithKeyspace(context, TEST_KEYSPACE, response -> {
+            assertThat(response.statusCode()).isEqualTo(HttpResponseStatus.OK.code());
+            TokenRangeReplicasResponse mappingResponse = response.bodyAsJson(TokenRangeReplicasResponse.class);
+            assertMappingResponseOK(mappingResponse, 1, Collections.singleton("datacenter1"));
+            context.completeNow();
+        });
+    }
+
 }