diff --git a/pom.xml b/pom.xml
index a0fc29f7dd..40c616ff80 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,6 +13,8 @@
2.16.2
+
+ 3.9.5
3.4.2
false
diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/cqldriver/CqlSessionCacheTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/cqldriver/CqlSessionCacheTest.java
new file mode 100644
index 0000000000..e8e25b96d7
--- /dev/null
+++ b/src/test/java/io/stargate/sgv2/jsonapi/service/cqldriver/CqlSessionCacheTest.java
@@ -0,0 +1,316 @@
+package io.stargate.sgv2.jsonapi.service.cqldriver;
+
+import static io.stargate.sgv2.jsonapi.service.cqldriver.TenantAwareCqlSessionBuilderTest.TENANT_ID_PROPERTY_KEY;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.assertj.core.api.AssertionsForClassTypes.catchThrowable;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.internal.core.context.DefaultDriverContext;
+import io.micrometer.core.instrument.FunctionCounter;
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import io.quarkus.security.UnauthorizedException;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import io.stargate.sgv2.jsonapi.api.request.DataApiRequestInfo;
+import io.stargate.sgv2.jsonapi.config.OperationsConfig;
+import jakarta.inject.Inject;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@QuarkusTest
+@TestProfile(CqlSessionCacheTest.TestProfile.class)
+public class CqlSessionCacheTest {
+ public static class TestProfile implements QuarkusTestProfile {
+ // Alas, we do need actual DB backend so cannot do:
+ // public boolean disableGlobalTestResources() { return true; }
+
+ @Override
+ public Map getConfigOverrides() {
+ return Map.of("stargate.jsonapi.operations.database-config.fixed-token", "test-token");
+ }
+ }
+
+ private static final String TENANT_ID_FOR_TEST = "test_tenant";
+
+ @Inject OperationsConfig operationsConfig;
+
+ private MeterRegistry meterRegistry;
+
+ /**
+ * List of sessions created in the tests. This is used to close the sessions after each test. This
+ * is needed because, though the sessions evicted from the cache are closed, the sessions left
+ * active on the cache are not closed, so we have to close them explicitly.
+ */
+ private List sessionsCreatedInTests;
+
+ @BeforeEach
+ public void tearUpEachTest() {
+ meterRegistry = new SimpleMeterRegistry();
+ sessionsCreatedInTests = new ArrayList<>();
+ }
+
+ @AfterEach
+ public void tearDownEachTest() {
+ sessionsCreatedInTests.forEach(CqlSession::close);
+ }
+
+ @Test
+ public void testOSSCxCQLSessionCacheDefaultTenant() {
+ DataApiRequestInfo dataApiRequestInfo = mock(DataApiRequestInfo.class);
+ when(dataApiRequestInfo.getCassandraToken())
+ .thenReturn(operationsConfig.databaseConfig().fixedToken());
+ CQLSessionCache cqlSessionCacheForTest = new CQLSessionCache(operationsConfig, meterRegistry);
+ CqlSession cqlSession = cqlSessionCacheForTest.getSession(dataApiRequestInfo);
+ sessionsCreatedInTests.add(cqlSession);
+ assertThat(
+ ((DefaultDriverContext) cqlSession.getContext())
+ .getStartupOptions()
+ .get(TENANT_ID_PROPERTY_KEY))
+ .isEqualTo("default_tenant");
+ Gauge cacheSizeMetric =
+ meterRegistry.find("cache.size").tag("cache", "cql_sessions_cache").gauge();
+ FunctionCounter cachePutMetric =
+ meterRegistry.find("cache.puts").tag("cache", "cql_sessions_cache").functionCounter();
+ assertThat(cacheSizeMetric).isNotNull();
+ assertThat(cacheSizeMetric.value()).isEqualTo(1);
+ assertThat(cachePutMetric).isNotNull();
+ assertThat(cachePutMetric.count()).isEqualTo(1);
+ }
+
+ @Test
+ public void testOSSCxCQLSessionCacheWithFixedToken()
+ throws NoSuchFieldException, IllegalAccessException {
+ // set request info
+ DataApiRequestInfo dataApiRequestInfo = mock(DataApiRequestInfo.class);
+ when(dataApiRequestInfo.getTenantId()).thenReturn(Optional.of(TENANT_ID_FOR_TEST));
+ when(dataApiRequestInfo.getCassandraToken())
+ .thenReturn(operationsConfig.databaseConfig().fixedToken());
+ CQLSessionCache cqlSessionCacheForTest = new CQLSessionCache(operationsConfig, meterRegistry);
+ // set operation config
+ Field operationsConfigField =
+ cqlSessionCacheForTest.getClass().getDeclaredField("operationsConfig");
+ operationsConfigField.setAccessible(true);
+ operationsConfigField.set(cqlSessionCacheForTest, operationsConfig);
+ CqlSession cqlSession = cqlSessionCacheForTest.getSession(dataApiRequestInfo);
+ sessionsCreatedInTests.add(cqlSession);
+ assertThat(
+ ((DefaultDriverContext) cqlSession.getContext())
+ .getStartupOptions()
+ .get(TENANT_ID_PROPERTY_KEY))
+ .isEqualTo(TENANT_ID_FOR_TEST);
+ // metrics test
+ Gauge cacheSizeMetric =
+ meterRegistry.find("cache.size").tag("cache", "cql_sessions_cache").gauge();
+ FunctionCounter cachePutMetric =
+ meterRegistry.find("cache.puts").tag("cache", "cql_sessions_cache").functionCounter();
+ assertThat(cacheSizeMetric).isNotNull();
+ assertThat(cacheSizeMetric.value()).isEqualTo(1);
+ assertThat(cachePutMetric).isNotNull();
+ assertThat(cachePutMetric.count()).isEqualTo(1);
+ }
+
+ @Test
+ public void testOSSCxCQLSessionCacheWithInvalidFixedToken()
+ throws NoSuchFieldException, IllegalAccessException {
+ // set request info
+ DataApiRequestInfo dataApiRequestInfo = mock(DataApiRequestInfo.class);
+ when(dataApiRequestInfo.getTenantId()).thenReturn(Optional.of(TENANT_ID_FOR_TEST));
+ when(dataApiRequestInfo.getCassandraToken()).thenReturn(Optional.of("invalid_token"));
+ CQLSessionCache cqlSessionCacheForTest = new CQLSessionCache(operationsConfig, meterRegistry);
+ // set operation config
+ Field operationsConfigField =
+ cqlSessionCacheForTest.getClass().getDeclaredField("operationsConfig");
+ operationsConfigField.setAccessible(true);
+ operationsConfigField.set(cqlSessionCacheForTest, operationsConfig);
+ // Throwable
+ Throwable t = catchThrowable(() -> cqlSessionCacheForTest.getSession(dataApiRequestInfo));
+ assertThat(t)
+ .isNotNull()
+ .isInstanceOf(UnauthorizedException.class)
+ .hasMessage("UNAUTHENTICATED: Invalid token");
+ // metrics test
+ Gauge cacheSizeMetric =
+ meterRegistry.find("cache.size").tag("cache", "cql_sessions_cache").gauge();
+ FunctionCounter cachePutMetric =
+ meterRegistry.find("cache.puts").tag("cache", "cql_sessions_cache").functionCounter();
+ assertThat(cacheSizeMetric).isNotNull();
+ assertThat(cacheSizeMetric.value()).isEqualTo(0);
+ assertThat(cachePutMetric).isNotNull();
+ assertThat(cachePutMetric.count()).isEqualTo(0);
+ }
+
+ @Test
+ public void testOSSCxCQLSessionCacheMultiTenant()
+ throws NoSuchFieldException, IllegalAccessException {
+ CQLSessionCache cqlSessionCacheForTest = new CQLSessionCache(operationsConfig, meterRegistry);
+ // set operation config
+ Field operationsConfigField =
+ cqlSessionCacheForTest.getClass().getDeclaredField("operationsConfig");
+ operationsConfigField.setAccessible(true);
+ operationsConfigField.set(cqlSessionCacheForTest, operationsConfig);
+ List tenantIds = new ArrayList<>();
+ tenantIds.add("tenant1");
+ tenantIds.add("tenant2");
+ tenantIds.add("tenant3");
+ tenantIds.add("tenant4");
+ tenantIds.add("tenant5");
+ for (String tenantId : tenantIds) {
+ // set request info
+ DataApiRequestInfo dataApiRequestInfo = mock(DataApiRequestInfo.class);
+ when(dataApiRequestInfo.getTenantId()).thenReturn(Optional.of(tenantId));
+ when(dataApiRequestInfo.getCassandraToken())
+ .thenReturn(operationsConfig.databaseConfig().fixedToken());
+ CqlSession cqlSession = cqlSessionCacheForTest.getSession(dataApiRequestInfo);
+ sessionsCreatedInTests.add(cqlSession);
+ assertThat(
+ ((DefaultDriverContext) cqlSession.getContext())
+ .getStartupOptions()
+ .get(TENANT_ID_PROPERTY_KEY))
+ .isEqualTo(tenantId);
+ }
+ // metrics test
+ Gauge cacheSizeMetric =
+ meterRegistry.find("cache.size").tag("cache", "cql_sessions_cache").gauge();
+ assertThat(cacheSizeMetric).isNotNull();
+ assertThat(cacheSizeMetric.value()).isEqualTo(tenantIds.size());
+ FunctionCounter cachePutMetric =
+ meterRegistry.find("cache.puts").tag("cache", "cql_sessions_cache").functionCounter();
+ assertThat(cachePutMetric).isNotNull();
+ assertThat(cachePutMetric.count()).isEqualTo(tenantIds.size());
+ FunctionCounter cacheLoadMetric =
+ meterRegistry
+ .find("cache.load")
+ .tag("cache", "cql_sessions_cache")
+ .tag("result", "success")
+ .functionCounter();
+ assertThat(cacheLoadMetric).isNotNull();
+ assertThat(cacheLoadMetric.count()).isEqualTo(tenantIds.size());
+ }
+
+ @Test
+ public void testOSSCxCQLSessionCacheSizeEviction()
+ throws NoSuchFieldException, IllegalAccessException {
+ CQLSessionCache cqlSessionCacheForTest = new CQLSessionCache(operationsConfig, meterRegistry);
+ // set operation config
+ Field operationsConfigField =
+ cqlSessionCacheForTest.getClass().getDeclaredField("operationsConfig");
+ operationsConfigField.setAccessible(true);
+ operationsConfigField.set(cqlSessionCacheForTest, operationsConfig);
+ int sessionsToCreate = operationsConfig.databaseConfig().sessionCacheMaxSize() + 5;
+ for (int i = 0; i < sessionsToCreate; i++) {
+ String tenantId = "tenant" + i;
+ DataApiRequestInfo dataApiRequestInfo = mock(DataApiRequestInfo.class);
+ when(dataApiRequestInfo.getTenantId()).thenReturn(Optional.of(tenantId));
+ when(dataApiRequestInfo.getCassandraToken())
+ .thenReturn(operationsConfig.databaseConfig().fixedToken());
+ CqlSession cqlSession = cqlSessionCacheForTest.getSession(dataApiRequestInfo);
+ sessionsCreatedInTests.add(cqlSession);
+ assertThat(
+ ((DefaultDriverContext) cqlSession.getContext())
+ .getStartupOptions()
+ .get(TENANT_ID_PROPERTY_KEY))
+ .isEqualTo(tenantId);
+ }
+ // test if cache size is maintained at `sessionCacheMaxSizeConfigured`
+ long cacheSize = cqlSessionCacheForTest.cacheSize();
+ assertThat(cacheSize).isEqualTo(operationsConfig.databaseConfig().sessionCacheMaxSize());
+ // metrics test
+ Gauge cacheSizeMetric =
+ meterRegistry.find("cache.size").tag("cache", "cql_sessions_cache").gauge();
+ assertThat(cacheSizeMetric).isNotNull();
+ assertThat(cacheSizeMetric.value())
+ .isEqualTo(operationsConfig.databaseConfig().sessionCacheMaxSize());
+ FunctionCounter cachePutMetric =
+ meterRegistry.find("cache.puts").tag("cache", "cql_sessions_cache").functionCounter();
+ assertThat(cachePutMetric).isNotNull();
+ assertThat(cachePutMetric.count()).isEqualTo(sessionsToCreate);
+ FunctionCounter cacheLoadMetric =
+ meterRegistry
+ .find("cache.load")
+ .tag("cache", "cql_sessions_cache")
+ .tag("result", "success")
+ .functionCounter();
+ assertThat(cacheLoadMetric).isNotNull();
+ assertThat(cacheLoadMetric.count()).isEqualTo(sessionsToCreate);
+ }
+ //
+ // @Test
+ // public void testInvalidSession()
+ // throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ // CQLSessionCache cqlSessionCacheForTest = new CQLSessionCache(operationsConfig,
+ // meterRegistry);
+ // CqlSession cqlSession = mock(CqlSession.class);
+ // Metadata metadata = mock(Metadata.class);
+ // when(cqlSession.getMetadata()).thenReturn(metadata);
+ // when(metadata.getKeyspaces()).thenReturn(Map.of());
+ // Node node = mock(Node.class);
+ // com.datastax.oss.driver.api.core.servererrors.UnauthorizedException unauthorizedException =
+ // new com.datastax.oss.driver.api.core.servererrors.UnauthorizedException(
+ // node, "Unauthorized");
+ // when(cqlSession.execute("SELECT * FROM system_virtual_schema.tables"))
+ // .thenThrow(unauthorizedException);
+ // Method isValidMethod =
+ // cqlSessionCacheForTest
+ // .getClass()
+ // .getDeclaredMethod("isAstraSessionValid", CqlSession.class, String.class);
+ // isValidMethod.setAccessible(true);
+ // boolean isValid =
+ // (boolean) isValidMethod.invoke(cqlSessionCacheForTest, cqlSession, TENANT_ID_FOR_TEST);
+ // assertThat(isValid).isFalse();
+ // }
+ //
+ // @Test
+ // public void testAValidSessionWithKeyspaces()
+ // throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ // CQLSessionCache cqlSessionCacheForTest = new CQLSessionCache(operationsConfig,
+ // meterRegistry);
+ // CqlSession cqlSession = mock(CqlSession.class);
+ // Metadata metadata = mock(Metadata.class);
+ // when(cqlSession.getMetadata()).thenReturn(metadata);
+ // when(metadata.getKeyspaces())
+ // .thenReturn(Map.of(CqlIdentifier.fromCql("ks1"), mock(KeyspaceMetadata.class)));
+ // Method isValidMethod =
+ // cqlSessionCacheForTest
+ // .getClass()
+ // .getDeclaredMethod("isAstraSessionValid", CqlSession.class, String.class);
+ // isValidMethod.setAccessible(true);
+ // boolean isValid =
+ // (boolean) isValidMethod.invoke(cqlSessionCacheForTest, cqlSession, TENANT_ID_FOR_TEST);
+ // assertThat(isValid).isTrue();
+ // }
+ //
+ // @Test
+ // public void testAValidSessionNoKeyspaces()
+ // throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ // CQLSessionCache cqlSessionCacheForTest = new CQLSessionCache(operationsConfig,
+ // meterRegistry);
+ // CqlSession cqlSession = mock(CqlSession.class);
+ // Metadata metadata = mock(Metadata.class);
+ // when(cqlSession.getMetadata()).thenReturn(metadata);
+ // when(metadata.getKeyspaces()).thenReturn(Map.of());
+ // Node node = mock(Node.class);
+ // com.datastax.oss.driver.api.core.servererrors.UnauthorizedException unauthorizedException =
+ // new com.datastax.oss.driver.api.core.servererrors.UnauthorizedException(
+ // node, "Unauthorized");
+ // when(cqlSession.execute("SELECT * FROM system_virtual_schema.tables")).thenReturn(null);
+ // Method isValidMethod =
+ // cqlSessionCacheForTest
+ // .getClass()
+ // .getDeclaredMethod("isAstraSessionValid", CqlSession.class, String.class);
+ // isValidMethod.setAccessible(true);
+ // boolean isValid =
+ // (boolean) isValidMethod.invoke(cqlSessionCacheForTest, cqlSession, TENANT_ID_FOR_TEST);
+ // assertThat(isValid).isTrue();
+ // }
+}
diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/cqldriver/CqlSessionCacheTimingTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/cqldriver/CqlSessionCacheTimingTest.java
new file mode 100644
index 0000000000..3d74a284f4
--- /dev/null
+++ b/src/test/java/io/stargate/sgv2/jsonapi/service/cqldriver/CqlSessionCacheTimingTest.java
@@ -0,0 +1,105 @@
+package io.stargate.sgv2.jsonapi.service.cqldriver;
+
+import static io.stargate.sgv2.jsonapi.service.cqldriver.TenantAwareCqlSessionBuilderTest.TENANT_ID_PROPERTY_KEY;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.internal.core.context.DefaultDriverContext;
+import io.micrometer.core.instrument.FunctionCounter;
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import io.stargate.sgv2.jsonapi.api.request.DataApiRequestInfo;
+import io.stargate.sgv2.jsonapi.config.OperationsConfig;
+import jakarta.inject.Inject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@QuarkusTest
+@TestProfile(CqlSessionCacheTimingTest.TestProfile.class)
+public class CqlSessionCacheTimingTest {
+ public static class TestProfile implements QuarkusTestProfile {
+ // Alas, we do need actual DB backend so cannot do:
+ // public boolean disableGlobalTestResources() { return true; }
+
+ @Override
+ public Map getConfigOverrides() {
+ return Map.of(
+ "stargate.jsonapi.operations.database-config.fixed-token",
+ "test-token",
+ "stargate.jsonapi.operations.database-config.session-cache-ttl-seconds",
+ "10");
+ }
+ }
+
+ @Inject OperationsConfig operationsConfig;
+
+ private MeterRegistry meterRegistry;
+
+ /**
+ * List of sessions created in the tests. This is used to close the sessions after each test. This
+ * is needed because, though the sessions evicted from the cache are closed, the sessions left
+ * active on the cache are not closed, so we have to close them explicitly.
+ */
+ private List sessionsCreatedInTests;
+
+ @BeforeEach
+ public void setupEachTest() {
+ meterRegistry = new SimpleMeterRegistry();
+ sessionsCreatedInTests = new ArrayList<>();
+ }
+
+ @AfterEach
+ public void tearDownEachTest() {
+ sessionsCreatedInTests.forEach(CqlSession::close);
+ }
+
+ @Test
+ public void testOSSCxCQLSessionCacheTimedEviction() throws InterruptedException {
+ CQLSessionCache cqlSessionCacheForTest = new CQLSessionCache(operationsConfig, meterRegistry);
+ int sessionsToCreate = operationsConfig.databaseConfig().sessionCacheMaxSize();
+ for (int i = 0; i < sessionsToCreate; i++) {
+ String tenantId = "tenant_timing_test_" + i;
+ DataApiRequestInfo dataApiRequestInfo = mock(DataApiRequestInfo.class);
+ when(dataApiRequestInfo.getTenantId()).thenReturn(Optional.of(tenantId));
+ when(dataApiRequestInfo.getCassandraToken())
+ .thenReturn(operationsConfig.databaseConfig().fixedToken());
+ CqlSession cqlSession = cqlSessionCacheForTest.getSession(dataApiRequestInfo);
+ sessionsCreatedInTests.add(cqlSession);
+ assertThat(
+ ((DefaultDriverContext) cqlSession.getContext())
+ .getStartupOptions()
+ .get(TENANT_ID_PROPERTY_KEY))
+ .isEqualTo(tenantId);
+ }
+ Thread.sleep(10000);
+ assertThat(cqlSessionCacheForTest.cacheSize()).isEqualTo(0);
+ // metrics test
+ Gauge cacheSizeMetric =
+ meterRegistry.find("cache.size").tag("cache", "cql_sessions_cache").gauge();
+ assertThat(cacheSizeMetric).isNotNull();
+ assertThat(cacheSizeMetric.value()).isEqualTo(0);
+ FunctionCounter cachePutMetric =
+ meterRegistry.find("cache.puts").tag("cache", "cql_sessions_cache").functionCounter();
+ assertThat(cachePutMetric).isNotNull();
+ assertThat(cachePutMetric.count()).isEqualTo(sessionsToCreate);
+ FunctionCounter cacheLoadMetric =
+ meterRegistry
+ .find("cache.load")
+ .tag("cache", "cql_sessions_cache")
+ .tag("result", "success")
+ .functionCounter();
+ assertThat(cacheLoadMetric).isNotNull();
+ assertThat(cacheLoadMetric.count()).isEqualTo(sessionsToCreate);
+ }
+}
diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/cqldriver/InvalidCredentialsTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/cqldriver/InvalidCredentialsTest.java
new file mode 100644
index 0000000000..3a2f785ac8
--- /dev/null
+++ b/src/test/java/io/stargate/sgv2/jsonapi/service/cqldriver/InvalidCredentialsTest.java
@@ -0,0 +1,116 @@
+package io.stargate.sgv2.jsonapi.service.cqldriver;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.assertj.core.api.AssertionsForClassTypes.catchThrowable;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.datastax.oss.driver.api.core.AllNodesFailedException;
+import com.datastax.oss.driver.api.core.CqlSession;
+import io.micrometer.core.instrument.FunctionCounter;
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import io.stargate.sgv2.jsonapi.api.model.command.CommandResult;
+import io.stargate.sgv2.jsonapi.api.request.DataApiRequestInfo;
+import io.stargate.sgv2.jsonapi.config.OperationsConfig;
+import io.stargate.sgv2.jsonapi.exception.mappers.ThrowableToErrorMapper;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@QuarkusTest
+@TestProfile(InvalidCredentialsTest.TestProfile.class)
+public class InvalidCredentialsTest {
+ public static class TestProfile implements QuarkusTestProfile {
+ // Alas, we do need actual DB backend so cannot do:
+ // public boolean disableGlobalTestResources() { return true; }
+
+ @Override
+ public Map getConfigOverrides() {
+ return Map.of(
+ "stargate.jsonapi.operations.database-config.fixed-token",
+ "test-token",
+ "stargate.jsonapi.operations.database-config.password",
+ "invalid-password");
+ }
+ }
+
+ private static final String TENANT_ID_FOR_TEST = "test_tenant";
+
+ @Inject OperationsConfig operationsConfig;
+
+ private MeterRegistry meterRegistry;
+
+ /**
+ * List of sessions created in the tests. This is used to close the sessions after each test. This
+ * is needed because, though the sessions evicted from the cache are closed, the sessions left
+ * active on the cache are not closed, so we have to close them explicitly.
+ */
+ private List sessionsCreatedInTests;
+
+ @BeforeEach
+ public void tearUpEachTest() {
+ meterRegistry = new SimpleMeterRegistry();
+ sessionsCreatedInTests = new ArrayList<>();
+ }
+
+ @AfterEach
+ public void tearDownEachTest() {
+ sessionsCreatedInTests.forEach(CqlSession::close);
+ }
+
+ @Test
+ public void testOSSCxCQLSessionCacheWithInvalidCredentials()
+ throws NoSuchFieldException, IllegalAccessException {
+ // set request info
+ DataApiRequestInfo dataApiRequestInfo = mock(DataApiRequestInfo.class);
+ when(dataApiRequestInfo.getTenantId()).thenReturn(Optional.of(TENANT_ID_FOR_TEST));
+ when(dataApiRequestInfo.getCassandraToken())
+ .thenReturn(operationsConfig.databaseConfig().fixedToken());
+ CQLSessionCache cqlSessionCacheForTest = new CQLSessionCache(operationsConfig, meterRegistry);
+ // Throwable
+ Throwable t = catchThrowable(() -> cqlSessionCacheForTest.getSession(dataApiRequestInfo));
+ assertThat(t).isInstanceOf(AllNodesFailedException.class);
+ CommandResult.Error error =
+ ThrowableToErrorMapper.getMapperWithMessageFunction().apply(t, t.getMessage());
+ assertThat(error).isNotNull();
+ assertThat(error.message()).contains("UNAUTHENTICATED: Invalid token");
+ assertThat(error.status()).isEqualTo(Response.Status.UNAUTHORIZED);
+ assertThat(cqlSessionCacheForTest.cacheSize()).isEqualTo(0);
+ // metrics test
+ Gauge cacheSizeMetric =
+ meterRegistry.find("cache.size").tag("cache", "cql_sessions_cache").gauge();
+ assertThat(cacheSizeMetric).isNotNull();
+ assertThat(cacheSizeMetric.value()).isEqualTo(0);
+ FunctionCounter cachePutMetric =
+ meterRegistry.find("cache.puts").tag("cache", "cql_sessions_cache").functionCounter();
+ assertThat(cachePutMetric).isNotNull();
+ assertThat(cachePutMetric.count()).isEqualTo(1);
+ FunctionCounter cacheLoadSuccessMetric =
+ meterRegistry
+ .find("cache.load")
+ .tag("cache", "cql_sessions_cache")
+ .tag("result", "success")
+ .functionCounter();
+ assertThat(cacheLoadSuccessMetric).isNotNull();
+ assertThat(cacheLoadSuccessMetric.count()).isEqualTo(0);
+ FunctionCounter cacheLoadFailureMetric =
+ meterRegistry
+ .find("cache.load")
+ .tag("cache", "cql_sessions_cache")
+ .tag("result", "failure")
+ .functionCounter();
+ assertThat(cacheLoadFailureMetric).isNotNull();
+ assertThat(cacheLoadFailureMetric.count()).isEqualTo(1);
+ }
+}
diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/embedding/operation/EmbeddingProviderErrorMessageTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/embedding/operation/EmbeddingProviderErrorMessageTest.java
index 77ce4c8755..cd0d5fd59f 100644
--- a/src/test/java/io/stargate/sgv2/jsonapi/service/embedding/operation/EmbeddingProviderErrorMessageTest.java
+++ b/src/test/java/io/stargate/sgv2/jsonapi/service/embedding/operation/EmbeddingProviderErrorMessageTest.java
@@ -175,7 +175,7 @@ public void testIncorrectContentTypeXML() {
.withSubscriber(UniAssertSubscriber.create())
.awaitFailure()
.getFailure();
- assertThat(exception.getCause())
+ assertThat(exception)
.isInstanceOf(JsonApiException.class)
.hasFieldOrPropertyWithValue(
"errorCode", ErrorCode.EMBEDDING_PROVIDER_UNEXPECTED_RESPONSE)
@@ -203,7 +203,7 @@ public void testIncorrectContentTypePlainText() {
.withSubscriber(UniAssertSubscriber.create())
.awaitFailure()
.getFailure();
- assertThat(exception.getCause())
+ assertThat(exception)
.isInstanceOf(JsonApiException.class)
.hasFieldOrPropertyWithValue(
"errorCode", ErrorCode.EMBEDDING_PROVIDER_UNEXPECTED_RESPONSE)
@@ -231,7 +231,7 @@ public void testNoJsonResponse() {
.withSubscriber(UniAssertSubscriber.create())
.awaitFailure()
.getFailure();
- assertThat(exception.getCause())
+ assertThat(exception)
.isInstanceOf(JsonApiException.class)
.hasFieldOrPropertyWithValue(
"errorCode", ErrorCode.EMBEDDING_PROVIDER_UNEXPECTED_RESPONSE)