CASSANDRASC-83: Require gossip to be enabled for ring and token ranges mapping endpoints

The ring and token ranges mapping endpoints leverage JMX to produce the payload. Ring and token ranges
mapping information is derived from gossip information available to the Cassandra instance. If gossip
has been disabled, the information might provide an inconsistent view of the cluster which is
undesirable. We should instead return a 503 (Service Unavailable) to the client whenever the client
tries to access these endpoints and gossip has been disabled.

In this commit, we ensure the endpoints return a 503 (Service Unavailable) when gossip is disabled.

Patch by Francisco Guerrero; Reviewed by Dinesh Joshi, Josh McKenzie, Yifan Cai for CASSANDRASC-83
diff --git a/CHANGES.txt b/CHANGES.txt
index 6c22d9c..36aa800 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
 1.0.0
 -----
+ * Require gossip to be enabled for ring and token ranges mapping endpoints (CASSANDRASC-83)
  * Improve TokenRangeReplicasResponse payload (CASSANDRASC-81)
  * HealthCheckPeriodicTask execute never completes the promise when instances are empty (CASSANDRASC-80)
  * Fix token-ranges endpoint to handle gossip-info responses without 'status' (CASSANDRASC-78)
diff --git a/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/GossipDependentStorageJmxOperations.java b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/GossipDependentStorageJmxOperations.java
new file mode 100644
index 0000000..c76927a
--- /dev/null
+++ b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/GossipDependentStorageJmxOperations.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.cassandra.sidecar.adapters.base;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.sidecar.adapters.base.exception.OperationUnavailableException;
+
+/**
+ * A wrapper class for {@link StorageJmxOperations} that ensures gossip is enabled during initialization.
+ */
+public class GossipDependentStorageJmxOperations implements StorageJmxOperations
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(GossipDependentStorageJmxOperations.class);
+
+    private final StorageJmxOperations delegate;
+
+    public GossipDependentStorageJmxOperations(StorageJmxOperations delegate)
+    {
+        this.delegate = Objects.requireNonNull(delegate, "delegate must be not-null");
+        ensureGossipIsEnabled();
+    }
+
+    @Override
+    public List<String> getLiveNodesWithPort()
+    {
+        return delegate.getLiveNodesWithPort();
+    }
+
+    @Override
+    public List<String> getUnreachableNodesWithPort()
+    {
+        return delegate.getUnreachableNodesWithPort();
+    }
+
+    @Override
+    public List<String> getJoiningNodesWithPort()
+    {
+        return delegate.getJoiningNodesWithPort();
+    }
+
+    @Override
+    public List<String> getLeavingNodesWithPort()
+    {
+        return delegate.getLeavingNodesWithPort();
+    }
+
+    @Override
+    public List<String> getMovingNodesWithPort()
+    {
+        return delegate.getMovingNodesWithPort();
+    }
+
+    @Override
+    public Map<String, String> getLoadMapWithPort()
+    {
+        return delegate.getLoadMapWithPort();
+    }
+
+    @Override
+    public Map<String, String> getTokenToEndpointWithPortMap()
+    {
+        return delegate.getTokenToEndpointWithPortMap();
+    }
+
+    @Override
+    public Map<String, Float> effectiveOwnershipWithPort(String keyspace) throws IllegalStateException
+    {
+        return delegate.effectiveOwnershipWithPort(keyspace);
+    }
+
+    @Override
+    public Map<String, Float> getOwnershipWithPort()
+    {
+        return delegate.getOwnershipWithPort();
+    }
+
+    @Override
+    public Map<String, String> getEndpointWithPortToHostId()
+    {
+        return delegate.getEndpointWithPortToHostId();
+    }
+
+    @Override
+    public void takeSnapshot(String tag, Map<String, String> options, String... entities) throws IOException
+    {
+        delegate.takeSnapshot(tag, options, entities);
+    }
+
+    @Override
+    public void clearSnapshot(String tag, String... keyspaceNames)
+    {
+        delegate.clearSnapshot(tag, keyspaceNames);
+    }
+
+    @Override
+    public Map<List<String>, List<String>> getRangeToEndpointWithPortMap(String keyspace)
+    {
+        return delegate.getRangeToEndpointWithPortMap(keyspace);
+    }
+
+    @Override
+    public Map<List<String>, List<String>> getPendingRangeToEndpointWithPortMap(String keyspace)
+    {
+        return delegate.getPendingRangeToEndpointWithPortMap(keyspace);
+    }
+
+    @Override
+    public boolean isGossipRunning()
+    {
+        return delegate.isGossipRunning();
+    }
+
+    /**
+     * Ensures that gossip is running on the Cassandra instance
+     *
+     * @throws OperationUnavailableException when gossip is not running
+     */
+    public void ensureGossipIsEnabled()
+    {
+        if (delegate.isGossipRunning())
+            return;
+
+        LOGGER.warn("Gossip is disabled and unavailable for the operation");
+        throw OperationUnavailableException.GOSSIP_DISABLED;
+    }
+}
diff --git a/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/RingProvider.java b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/RingProvider.java
index 5d44711..8fd3253 100644
--- a/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/RingProvider.java
+++ b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/RingProvider.java
@@ -126,7 +126,8 @@
 
     protected StorageJmxOperations initializeStorageOps()
     {
-        return jmxClient.proxy(StorageJmxOperations.class, STORAGE_SERVICE_OBJ_NAME);
+        return new GossipDependentStorageJmxOperations(jmxClient.proxy(StorageJmxOperations.class,
+                                                                       STORAGE_SERVICE_OBJ_NAME));
     }
 
     /**
diff --git a/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/StorageJmxOperations.java b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/StorageJmxOperations.java
index d39ba8f..c2cb77d 100644
--- a/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/StorageJmxOperations.java
+++ b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/StorageJmxOperations.java
@@ -134,4 +134,9 @@
      * a list of endpoints
      */
     Map<List<String>, List<String>> getPendingRangeToEndpointWithPortMap(String keyspace);
+
+    /**
+     * @return {@code true} if gossip is running, {@code false} otherwise
+     */
+    boolean isGossipRunning();
 }
diff --git a/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicaProvider.java b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicaProvider.java
index 71a70e8..f1f5ff1 100644
--- a/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicaProvider.java
+++ b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicaProvider.java
@@ -177,10 +177,10 @@
 
     protected StorageJmxOperations initializeStorageOps()
     {
-        return jmxClient.proxy(StorageJmxOperations.class, STORAGE_SERVICE_OBJ_NAME);
+        return new GossipDependentStorageJmxOperations(jmxClient.proxy(StorageJmxOperations.class,
+                                                                       STORAGE_SERVICE_OBJ_NAME));
     }
 
-
     protected String getRawGossipInfo()
     {
         return jmxClient.proxy(ClusterMembershipJmxOperations.class, FAILURE_DETECTOR_OBJ_NAME)
diff --git a/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/exception/OperationUnavailableException.java b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/exception/OperationUnavailableException.java
new file mode 100644
index 0000000..818b878
--- /dev/null
+++ b/adapters/base/src/main/java/org/apache/cassandra/sidecar/adapters/base/exception/OperationUnavailableException.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.cassandra.sidecar.adapters.base.exception;
+
+/**
+ * Indicates that the operation is unavailable because a requirement for the operation has not been met
+ */
+public class OperationUnavailableException extends RuntimeException
+{
+    public static final OperationUnavailableException GOSSIP_DISABLED
+    = new OperationUnavailableException("Gossip is required for the operation but it is disabled");
+
+    public OperationUnavailableException(String errorMessage)
+    {
+        super(errorMessage);
+    }
+}
diff --git a/adapters/base/src/test/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicaProviderTest.java b/adapters/base/src/test/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicaProviderTest.java
index 5c1253d..8c244bb 100644
--- a/adapters/base/src/test/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicaProviderTest.java
+++ b/adapters/base/src/test/java/org/apache/cassandra/sidecar/adapters/base/TokenRangeReplicaProviderTest.java
@@ -82,6 +82,7 @@
         dnsResolver = mock(DnsResolver.class);
         instance = new TokenRangeReplicaProvider(jmxClient, dnsResolver);
 
+        when(storageOperations.isGossipRunning()).thenReturn(true);
         when(jmxClient.proxy(StorageJmxOperations.class, "org.apache.cassandra.db:type=StorageService"))
         .thenReturn(storageOperations);
         when(jmxClient.proxy(EndpointSnitchJmxOperations.class, "org.apache.cassandra.db:type=EndpointSnitchInfo"))
diff --git a/src/main/java/org/apache/cassandra/sidecar/routes/AbstractHandler.java b/src/main/java/org/apache/cassandra/sidecar/routes/AbstractHandler.java
index 7cd7dc2..e8d1304 100644
--- a/src/main/java/org/apache/cassandra/sidecar/routes/AbstractHandler.java
+++ b/src/main/java/org/apache/cassandra/sidecar/routes/AbstractHandler.java
@@ -30,6 +30,7 @@
 import io.vertx.core.net.SocketAddress;
 import io.vertx.ext.web.RoutingContext;
 import io.vertx.ext.web.handler.HttpException;
+import org.apache.cassandra.sidecar.adapters.base.exception.OperationUnavailableException;
 import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
 import org.apache.cassandra.sidecar.common.data.QualifiedTableName;
 import org.apache.cassandra.sidecar.common.exceptions.JmxAuthenticationException;
@@ -194,6 +195,11 @@
             return wrapHttpException(HttpResponseStatus.SERVICE_UNAVAILABLE, cause);
         }
 
+        if (cause instanceof OperationUnavailableException)
+        {
+            return wrapHttpException(HttpResponseStatus.SERVICE_UNAVAILABLE, cause.getMessage(), cause);
+        }
+
         return wrapHttpException(HttpResponseStatus.INTERNAL_SERVER_ERROR, cause);
     }
 
diff --git a/src/test/integration/org/apache/cassandra/sidecar/routes/RingHandlerIntegrationTest.java b/src/test/integration/org/apache/cassandra/sidecar/routes/RingHandlerIntegrationTest.java
index e52ba30..0d3000f 100644
--- a/src/test/integration/org/apache/cassandra/sidecar/routes/RingHandlerIntegrationTest.java
+++ b/src/test/integration/org/apache/cassandra/sidecar/routes/RingHandlerIntegrationTest.java
@@ -36,6 +36,7 @@
 import org.apache.cassandra.sidecar.common.data.RingResponse;
 import org.apache.cassandra.sidecar.test.CassandraSidecarTestContext;
 import org.apache.cassandra.testing.CassandraIntegrationTest;
+import org.apache.cassandra.testing.CassandraTestContext;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -45,8 +46,7 @@
 @ExtendWith(VertxExtension.class)
 class RingHandlerIntegrationTest extends IntegrationTestBase
 {
-
-    @CassandraIntegrationTest
+    @CassandraIntegrationTest(gossip = true)
     void retrieveRingWithoutKeyspace(VertxTestContext context)
     throws Exception
     {
@@ -61,7 +61,7 @@
         });
     }
 
-    @CassandraIntegrationTest
+    @CassandraIntegrationTest(gossip = true)
     void retrieveRingWithUnknownKeyspace(VertxTestContext context) throws Exception
     {
         retrieveRingWithKeyspace(context, "unknown_ks", response -> {
@@ -75,7 +75,27 @@
         });
     }
 
-    @CassandraIntegrationTest
+    @CassandraIntegrationTest(gossip = true)
+    void ringFailsWhenGossipIsDisabled(CassandraTestContext context, VertxTestContext testContext) throws Exception
+    {
+        int disableGossip = context.getCluster().getFirstRunningInstance().nodetool("disablegossip");
+        assertThat(disableGossip).isEqualTo(0);
+        String testRoute = "/api/v1/cassandra/ring";
+        testWithClient(testContext, client -> {
+            client.get(server.actualPort(), "127.0.0.1", testRoute)
+                  .expect(ResponsePredicate.SC_SERVICE_UNAVAILABLE)
+                  .send(testContext.succeeding(response -> {
+                      JsonObject payload = response.bodyAsJsonObject();
+                      assertThat(payload.getString("status")).isEqualTo("Service Unavailable");
+                      assertThat(payload.getInteger("code")).isEqualTo(503);
+                      assertThat(payload.getString("message"))
+                      .isEqualTo("Gossip is required for the operation but it is disabled");
+                      testContext.completeNow();
+                  }));
+        });
+    }
+
+    @CassandraIntegrationTest(gossip = true)
     void retrieveRingWithExistingKeyspace(VertxTestContext context) throws Exception
     {
         createTestKeyspace();
diff --git a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicGossipDisabledTest.java b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicGossipDisabledTest.java
new file mode 100644
index 0000000..6331bd2
--- /dev/null
+++ b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicGossipDisabledTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.cassandra.sidecar.routes.tokenrange;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.vertx.core.json.JsonObject;
+import io.vertx.junit5.VertxExtension;
+import io.vertx.junit5.VertxTestContext;
+import org.apache.cassandra.testing.CassandraIntegrationTest;
+import org.apache.cassandra.testing.CassandraTestContext;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests that the token range mapping endpoint fails with service unavailable when gossip is disabled
+ */
+@ExtendWith(VertxExtension.class)
+public class BasicGossipDisabledTest extends BaseTokenRangeIntegrationTest
+{
+    @CassandraIntegrationTest
+    void tokenRangeEndpointFailsWhenGossipIsDisabled(CassandraTestContext context, VertxTestContext testContext)
+    throws Exception
+    {
+        int disableGossip = context.getCluster().getFirstRunningInstance().nodetool("disablegossip");
+        assertThat(disableGossip).isEqualTo(0);
+        retrieveMappingWithKeyspace(testContext, TEST_KEYSPACE, response -> {
+            assertThat(response.statusCode()).isEqualTo(HttpResponseStatus.SERVICE_UNAVAILABLE.code());
+            JsonObject payload = response.bodyAsJsonObject();
+            assertThat(payload.getString("status")).isEqualTo("Service Unavailable");
+            assertThat(payload.getInteger("code")).isEqualTo(503);
+            assertThat(payload.getString("message"))
+            .isEqualTo("Gossip is required for the operation but it is disabled");
+            testContext.completeNow();
+        });
+    }
+}
diff --git a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestMultiDCRf3.java b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicMultiDCRf3Test.java
similarity index 94%
rename from src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestMultiDCRf3.java
rename to src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicMultiDCRf3Test.java
index 8e70364..5389b83 100644
--- a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestMultiDCRf3.java
+++ b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicMultiDCRf3Test.java
@@ -39,9 +39,9 @@
  * therefore limit the instance size required to run the tests from CircleCI as the in-jvm-dtests tests are memory bound
  */
 @ExtendWith(VertxExtension.class)
-class BasicTestMultiDCRf3 extends BaseTokenRangeIntegrationTest
+class BasicMultiDCRf3Test extends BaseTokenRangeIntegrationTest
 {
-    @CassandraIntegrationTest(nodesPerDc = 5, numDcs = 2)
+    @CassandraIntegrationTest(nodesPerDc = 5, numDcs = 2, gossip = true)
     void retrieveMappingMultiDcRf3(VertxTestContext context) throws Exception
     {
         int replicationFactor = 3;
diff --git a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestMultiDCSingleReplicated.java b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicMultiDCSingleReplicatedTest.java
similarity index 93%
rename from src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestMultiDCSingleReplicated.java
rename to src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicMultiDCSingleReplicatedTest.java
index 782e760..075d707 100644
--- a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestMultiDCSingleReplicated.java
+++ b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicMultiDCSingleReplicatedTest.java
@@ -35,9 +35,9 @@
  * therefore limit the instance size required to run the tests from CircleCI as the in-jvm-dtests tests are memory bound
  */
 @ExtendWith(VertxExtension.class)
-class BasicTestMultiDCSingleReplicated extends BaseTokenRangeIntegrationTest
+class BasicMultiDCSingleReplicatedTest extends BaseTokenRangeIntegrationTest
 {
-    @CassandraIntegrationTest(nodesPerDc = 5, numDcs = 2)
+    @CassandraIntegrationTest(nodesPerDc = 5, numDcs = 2, gossip = true)
     void retrieveMappingSingleDCReplicatedRf3(VertxTestContext context)
     throws Exception
     {
diff --git a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestRf1.java b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicRf1Test.java
similarity index 94%
rename from src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestRf1.java
rename to src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicRf1Test.java
index b11bcd0..4a63ef1 100644
--- a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestRf1.java
+++ b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicRf1Test.java
@@ -37,9 +37,9 @@
  * therefore limit the instance size required to run the tests from CircleCI as the in-jvm-dtests tests are memory bound
  */
 @ExtendWith(VertxExtension.class)
-class BasicTestRf1 extends BaseTokenRangeIntegrationTest
+class BasicRf1Test extends BaseTokenRangeIntegrationTest
 {
-    @CassandraIntegrationTest(nodesPerDc = 3)
+    @CassandraIntegrationTest(nodesPerDc = 3, gossip = true)
     void retrieveMappingRf1(VertxTestContext context) throws Exception
     {
         createTestKeyspace();
diff --git a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestRf3.java b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicRf3Test.java
similarity index 94%
rename from src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestRf3.java
rename to src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicRf3Test.java
index b99e6fd..940be7e 100644
--- a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestRf3.java
+++ b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicRf3Test.java
@@ -35,9 +35,9 @@
  * therefore limit the instance size required to run the tests from CircleCI as the in-jvm-dtests tests are memory bound
  */
 @ExtendWith(VertxExtension.class)
-class BasicTestRf3 extends BaseTokenRangeIntegrationTest
+class BasicRf3Test extends BaseTokenRangeIntegrationTest
 {
-    @CassandraIntegrationTest(nodesPerDc = 5)
+    @CassandraIntegrationTest(nodesPerDc = 5, gossip = true)
     void retrieveMappingRf3(VertxTestContext context) throws Exception
     {
         int replicationFactor = 3;
diff --git a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestUnknownKeyspace.java b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicUnknownKeyspaceTest.java
similarity index 94%
rename from src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestUnknownKeyspace.java
rename to src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicUnknownKeyspaceTest.java
index 2b1a158..6da3fb3 100644
--- a/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicTestUnknownKeyspace.java
+++ b/src/test/integration/org/apache/cassandra/sidecar/routes/tokenrange/BasicUnknownKeyspaceTest.java
@@ -35,9 +35,9 @@
  * therefore limit the instance size required to run the tests from CircleCI as the in-jvm-dtests tests are memory bound
  */
 @ExtendWith(VertxExtension.class)
-class BasicTestUnknownKeyspace extends BaseTokenRangeIntegrationTest
+class BasicUnknownKeyspaceTest extends BaseTokenRangeIntegrationTest
 {
-    @CassandraIntegrationTest
+    @CassandraIntegrationTest(gossip = true)
     void retrieveMappingWithUnknownKeyspace(VertxTestContext context) throws Exception
     {
         retrieveMappingWithKeyspace(context, "unknown_ks", response -> {