From 7ef8e695d83651048ad41e5b4abcf2900b0f9b2d Mon Sep 17 00:00:00 2001 From: janehe Date: Wed, 10 Dec 2025 14:55:29 -0800 Subject: [PATCH 01/27] can create db --- .../core/cql/SimpleStatementAstraIT.java | 372 ++++++++++++++++++ .../src/test/resources/logback-test.xml | 12 +- test-infra/pom.xml | 6 + .../api/testinfra/astra/AstraBridge.java | 293 ++++++++++++++ .../driver/api/testinfra/astra/AstraRule.java | 82 ++++ .../api/testinfra/astra/BaseAstraRule.java | 119 ++++++ .../api/testinfra/astra/CustomAstraRule.java | 122 ++++++ .../testinfra/requirement/BackendType.java | 5 +- .../api/testinfra/session/SessionUtils.java | 27 +- 9 files changed, 1029 insertions(+), 9 deletions(-) create mode 100644 integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementAstraIT.java create mode 100644 test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java create mode 100644 test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraRule.java create mode 100644 test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java create mode 100644 test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementAstraIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementAstraIT.java new file mode 100644 index 00000000000..09ba8394893 --- /dev/null +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementAstraIT.java @@ -0,0 +1,372 @@ +/* + * 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 com.datastax.oss.driver.core.cql; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.cql.AsyncResultSet; +import com.datastax.oss.driver.api.core.cql.ResultSet; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; +import com.datastax.oss.driver.api.testinfra.astra.AstraRule; +import com.datastax.oss.driver.api.testinfra.session.SessionRule; +import com.datastax.oss.driver.api.testinfra.session.SessionUtils; +import com.datastax.oss.driver.categories.ParallelizableTests; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeUnit; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.RuleChain; +import org.junit.rules.TestName; +import org.junit.rules.TestRule; + +@Category(ParallelizableTests.class) +public class SimpleStatementAstraIT { + + private static final AstraRule ASTRA_RULE = AstraRule.getInstance(); + + private static final SessionRule SESSION_RULE = + SessionRule.builder(ASTRA_RULE) + .withConfigLoader( + SessionUtils.configLoaderBuilder() + .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 20) + .build()) + .build(); + + @ClassRule + public static final TestRule CHAIN = RuleChain.outerRule(ASTRA_RULE).around(SESSION_RULE); + + @Rule public TestName name = new TestName(); + + private static final String KEY = "test"; + + @BeforeClass + public static void setupSchema() { + // table where every column forms the primary key. + SESSION_RULE + .session() + .execute( + SimpleStatement.builder( + "CREATE TABLE IF NOT EXISTS test (k text, v int, PRIMARY KEY(k, v))") + .setExecutionProfile(SESSION_RULE.slowProfile()) + .build()); + for (int i = 0; i < 100; i++) { + SESSION_RULE + .session() + .execute( + SimpleStatement.builder("INSERT INTO test (k, v) VALUES (?, ?)") + .addPositionalValues(KEY, i) + .build()); + } + + // table with simple primary key, single cell. + SESSION_RULE + .session() + .execute( + SimpleStatement.builder("CREATE TABLE IF NOT EXISTS test2 (k text primary key, v int)") + .setExecutionProfile(SESSION_RULE.slowProfile()) + .build()); + } + + @Test + public void should_use_paging_state_when_copied() { + Statement st = + SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); + ResultSet result = SESSION_RULE.session().execute(st); + + // given a query created from a copy of a previous query with paging state from previous queries + // response. + st = st.copy(result.getExecutionInfo().getPagingState()); + + // when executing that query. + result = SESSION_RULE.session().execute(st); + + // then the response should start on the page boundary. + assertThat(result.iterator().next().getInt("v")).isEqualTo(20); + } + + @Test + public void should_use_paging_state_when_provided_to_new_statement() { + Statement st = + SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); + ResultSet result = SESSION_RULE.session().execute(st); + + // given a query created from a copy of a previous query with paging state from previous queries + // response. + st = + SimpleStatement.builder(String.format("SELECT v FROM test where k='%s'", KEY)) + .setPagingState(result.getExecutionInfo().getPagingState()) + .build(); + + // when executing that query. + result = SESSION_RULE.session().execute(st); + + // then the response should start on the page boundary. + assertThat(result.iterator().next().getInt("v")).isEqualTo(20); + } + + @Test + @Ignore + public void should_fail_if_using_paging_state_from_different_query() { + Statement st = + SimpleStatement.builder("SELECT v FROM test WHERE k=:k").addNamedValue("k", KEY).build(); + ResultSet result = SESSION_RULE.session().execute(st); + + // TODO Expect PagingStateException + + // given a new different query and providing the paging state from the previous query + // then an exception should be thrown indicating incompatible paging state + SimpleStatement.builder("SELECT v FROM test") + .setPagingState(result.getExecutionInfo().getPagingState()) + .build(); + } + + @Test + public void should_use_timestamp_when_set() { + // given inserting data with a timestamp 40 days in the past. + long timestamp = System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(40, TimeUnit.DAYS); + SimpleStatement insert = + SimpleStatement.builder("INSERT INTO test2 (k, v) values (?, ?)") + .addPositionalValues(name.getMethodName(), 0) + .setQueryTimestamp(timestamp) + .build(); + + SESSION_RULE.session().execute(insert); + + // when retrieving writetime of cell from that insert. + SimpleStatement select = + SimpleStatement.builder("SELECT writetime(v) as wv from test2 where k = ?") + .addPositionalValue(name.getMethodName()) + .build(); + + ResultSet result = SESSION_RULE.session().execute(select); + List rows = result.all(); + assertThat(rows).hasSize(1); + + // then the writetime should equal the timestamp provided. + Row row = rows.iterator().next(); + assertThat(row.getLong("wv")).isEqualTo(timestamp); + } + + @Test + @Ignore + public void should_use_tracing_when_set() { + // TODO currently there's no way to validate tracing was set since trace id is not set + // also write test to verify it is not set. + SESSION_RULE + .session() + .execute(SimpleStatement.builder("select * from test").setTracing().build()); + } + + @Test + public void should_use_positional_values() { + // given a statement with positional values + SimpleStatement insert = + SimpleStatement.builder("INSERT into test2 (k, v) values (?, ?)") + .addPositionalValue(name.getMethodName()) + .addPositionalValue(4) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then we should be able to retrieve the data as inserted. + SimpleStatement select = + SimpleStatement.builder("select k,v from test2 where k=?") + .addPositionalValue(name.getMethodName()) + .build(); + + ResultSet result = SESSION_RULE.session().execute(select); + List rows = result.all(); + assertThat(rows).hasSize(1); + + Row row = rows.iterator().next(); + assertThat(row.getString("k")).isEqualTo(name.getMethodName()); + assertThat(row.getInt("v")).isEqualTo(4); + } + + @Test + public void should_allow_nulls_in_positional_values() { + // given a statement with positional values + SimpleStatement insert = + SimpleStatement.builder("INSERT into test2 (k, v) values (?, ?)") + .addPositionalValue(name.getMethodName()) + .addPositionalValue(null) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then we should be able to retrieve the data as inserted. + SimpleStatement select = + SimpleStatement.builder("select k,v from test2 where k=?") + .addPositionalValue(name.getMethodName()) + .build(); + + ResultSet result = SESSION_RULE.session().execute(select); + List rows = result.all(); + assertThat(rows).hasSize(1); + + Row row = rows.iterator().next(); + assertThat(row.getString("k")).isEqualTo(name.getMethodName()); + assertThat(row.getObject("v")).isNull(); + } + + @Test(expected = InvalidQueryException.class) + public void should_fail_when_too_many_positional_values_provided() { + // given a statement with more bound values than anticipated (3 given vs. 2 expected) + SimpleStatement insert = + SimpleStatement.builder("INSERT into test (k, v) values (?, ?)") + .addPositionalValues(KEY, 0, 7) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then the server will throw an InvalidQueryException which is thrown up to the client. + } + + @Test(expected = InvalidQueryException.class) + public void should_fail_when_not_enough_positional_values_provided() { + // given a statement with not enough bound values (1 given vs. 2 expected) + SimpleStatement insert = + SimpleStatement.builder("SELECT * from test where k = ? and v = ?") + .addPositionalValue(KEY) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then the server will throw an InvalidQueryException which is thrown up to the client. + } + + @Test + public void should_use_named_values() { + // given a statement with named values + SimpleStatement insert = + SimpleStatement.builder("INSERT into test2 (k, v) values (:k, :v)") + .addNamedValue("k", name.getMethodName()) + .addNamedValue("v", 7) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then we should be able to retrieve the data as inserted. + SimpleStatement select = + SimpleStatement.builder("select k,v from test2 where k=:k") + .addNamedValue("k", name.getMethodName()) + .build(); + + ResultSet result = SESSION_RULE.session().execute(select); + List rows = result.all(); + assertThat(rows).hasSize(1); + + Row row = rows.iterator().next(); + assertThat(row.getString("k")).isEqualTo(name.getMethodName()); + assertThat(row.getInt("v")).isEqualTo(7); + } + + @Test + public void should_allow_nulls_in_named_values() { + // given a statement with named values + SimpleStatement insert = + SimpleStatement.builder("INSERT into test2 (k, v) values (:k, :v)") + .addNamedValue("k", name.getMethodName()) + .addNamedValue("v", null) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then we should be able to retrieve the data as inserted. + SimpleStatement select = + SimpleStatement.builder("select k,v from test2 where k=:k") + .addNamedValue("k", name.getMethodName()) + .build(); + + ResultSet result = SESSION_RULE.session().execute(select); + List rows = result.all(); + assertThat(rows).hasSize(1); + + Row row = rows.iterator().next(); + assertThat(row.getString("k")).isEqualTo(name.getMethodName()); + assertThat(row.getObject("v")).isNull(); + } + + @Test(expected = InvalidQueryException.class) + public void should_fail_when_named_value_missing() { + // given a statement with a missing named value (:k) + SimpleStatement insert = + SimpleStatement.builder("SELECT * from test where k = :k and v = :v") + .addNamedValue("v", 0) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then the server will throw an InvalidQueryException which is thrown up to the client. + } + + @Test(expected = IllegalArgumentException.class) + public void should_fail_when_mixing_named_and_positional_values() { + SimpleStatement.builder("SELECT * from test where k = :k and v = :v") + .addNamedValue("k", KEY) + .addPositionalValue(0) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void should_fail_when_mixing_positional_and_named_values() { + SimpleStatement.builder("SELECT * from test where k = :k and v = :v") + .addPositionalValue(0) + .addNamedValue("k", KEY) + .build(); + } + + @Test + public void should_use_positional_value_with_case_sensitive_id() { + SimpleStatement statement = + SimpleStatement.builder("SELECT count(*) FROM test2 WHERE k=:\"theKey\"") + .addNamedValue(CqlIdentifier.fromCql("\"theKey\""), 0) + .build(); + Row row = SESSION_RULE.session().execute(statement).one(); + assertThat(row.getLong(0)).isEqualTo(0); + } + + @Test + public void should_use_page_size() { + Statement st = SimpleStatement.builder("SELECT v FROM test").setPageSize(10).build(); + CompletionStage future = SESSION_RULE.session().executeAsync(st); + AsyncResultSet result = CompletableFutures.getUninterruptibly(future); + + // Should have only fetched 10 (page size) rows. + assertThat(result.remaining()).isEqualTo(10); + } +} diff --git a/integration-tests/src/test/resources/logback-test.xml b/integration-tests/src/test/resources/logback-test.xml index a2179e4357b..1dc34c20c6c 100644 --- a/integration-tests/src/test/resources/logback-test.xml +++ b/integration-tests/src/test/resources/logback-test.xml @@ -24,14 +24,14 @@ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - + - - - - - + + + + + diff --git a/test-infra/pom.xml b/test-infra/pom.xml index 5bf2d07f652..ebe64306b8c 100644 --- a/test-infra/pom.xml +++ b/test-infra/pom.xml @@ -70,6 +70,12 @@ commons-exec true + + + com.fasterxml.jackson.core + jackson-databind + true + org.awaitility awaitility diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java new file mode 100644 index 00000000000..0ff99c29226 --- /dev/null +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -0,0 +1,293 @@ +/* + * 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 com.datastax.oss.driver.api.testinfra.astra; + +import com.datastax.oss.driver.api.core.Version; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AstraBridge implements AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(AstraBridge.class); + + public static final BackendType DISTRIBUTION = BackendType.ASTRA; + + // Astra CLI configuration + private static final String ASTRA_TOKEN = System.getProperty("astra.token"); + private static final String ASTRA_CLIENT_ID = System.getProperty("astra.client.id", "token"); + private static final String ASTRA_CLIENT_SECRET = System.getProperty("astra.client.secret"); + private static final String ASTRA_CLOUD_PROVIDER = + System.getProperty("astra.cloud.provider", "gcp"); + private static final String ASTRA_REGION = System.getProperty("astra.region", "us-east1"); + + // Database configuration + private static final String DATABASE_NAME_PREFIX = "java_driver_test_db_"; + private static final String DEFAULT_KEYSPACE = "java_driver_test"; + + private final String databaseName; + private final String keyspace; + + @SuppressWarnings("UnusedVariable") + private final String cloudProvider; + + private final String region; + private final String clientId; + private final String clientSecret; + private final AtomicBoolean created = new AtomicBoolean(); + private final AtomicBoolean started = new AtomicBoolean(); + private final Path configDirectory; + + private String databaseId; + private File secureConnectBundle; + + private AstraBridge( + Path configDirectory, + String databaseName, + String keyspace, + String cloudProvider, + String region, + String clientId, + String clientSecret) { + this.configDirectory = configDirectory; + this.databaseName = databaseName; + this.keyspace = keyspace; + this.cloudProvider = cloudProvider; + this.region = region; + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public static Builder builder() { + return new Builder(); + } + + public static boolean isDistributionOf(BackendType type) { + return DISTRIBUTION == type; + } + + public static Version getDistributionVersion() { + // Astra uses Cassandra 4.0 + return Version.parse("4.0.0"); + } + + public static Version getCassandraVersion() { + // Astra uses Cassandra 4.0 + return Version.parse("4.0.0"); + } + + public static class Builder { + private String databaseName = DATABASE_NAME_PREFIX + System.currentTimeMillis(); + private String keyspace = DEFAULT_KEYSPACE; + private String cloudProvider = ASTRA_CLOUD_PROVIDER; + private String region = ASTRA_REGION; + private String clientId = ASTRA_CLIENT_ID; + private String clientSecret = ASTRA_CLIENT_SECRET; + + public Builder withDatabaseName(String databaseName) { + this.databaseName = databaseName; + return this; + } + + public Builder withKeyspace(String keyspace) { + this.keyspace = keyspace; + return this; + } + + public Builder withCloudProvider(String cloudProvider) { + this.cloudProvider = cloudProvider; + return this; + } + + public Builder withRegion(String region) { + this.region = region; + return this; + } + + public Builder withClientId(String clientId) { + this.clientId = clientId; + return this; + } + + public Builder withClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + return this; + } + + public AstraBridge build() { + try { + Path configDir = Files.createTempDirectory("astra-test-"); + return new AstraBridge( + configDir, databaseName, keyspace, cloudProvider, region, clientId, clientSecret); + } catch (IOException e) { + throw new RuntimeException("Failed to create config directory", e); + } + } + } + + public synchronized void create() { + if (created.compareAndSet(false, true)) { + try { + LOG.error("Creating Astra database: {}", databaseName); + + // Setup Astra CLI with token + runAstraCommand("setup", "--token", ASTRA_TOKEN); + + // Create database using Astra CLI + // astra db create --no-async --non-vector --if-not-exists -k -r + // + List createArgs = new ArrayList<>(); + createArgs.add("db"); + createArgs.add("create"); + createArgs.add("--no-async"); + createArgs.add("--non-vector"); + createArgs.add("--if-not-exists"); + createArgs.add("-k"); + createArgs.add(keyspace); + createArgs.add("-r"); + createArgs.add(region); + createArgs.add(databaseName); + + String output = runAstraCommand(createArgs.toArray(new String[0])); + LOG.error("Database creation output: {}", output); + + // Get database ID using: astra db get --key id + databaseId = runAstraCommand("db", "get", databaseName, "--key", "id").trim(); + LOG.error("Astra database created with ID: {}", databaseId); + + // Download secure connect bundle + downloadSecureConnectBundle(); + + } catch (IOException | InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Failed to create Astra database", e); + } + } + } + + private void downloadSecureConnectBundle() throws IOException, InterruptedException { + // Create SCB directory + Path scbDir = configDirectory.resolve("scb"); + Files.createDirectories(scbDir); + + // Download SCB using: astra db download-scb -f + File scbFile = scbDir.resolve("scb_" + databaseId + "_" + region + ".zip").toFile(); + runAstraCommand("db", "download-scb", databaseName, "-f", scbFile.getAbsolutePath()); + + if (!scbFile.exists()) { + throw new IOException("Secure connect bundle was not downloaded: " + scbFile); + } + + this.secureConnectBundle = scbFile; + LOG.info("Secure connect bundle downloaded to: {}", scbFile.getAbsolutePath()); + } + + private String runAstraCommand(String... args) throws IOException, InterruptedException { + List command = new ArrayList<>(); + command.add("astra"); + for (String arg : args) { + command.add(arg); + } + + LOG.error("Running Astra CLI command: {}", String.join(" ", command)); + + ProcessBuilder pb = new ProcessBuilder(command); + pb.redirectErrorStream(true); + Process process = pb.start(); + + StringBuilder output = new StringBuilder(); + try (BufferedReader reader = + new BufferedReader( + new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + LOG.info("Astra CLI: {}", line); + } + } + + int exitCode = process.waitFor(); + if (exitCode != 0) { + throw new IOException( + "Astra CLI command failed with exit code " + + exitCode + + ": " + + String.join(" ", command) + + "\nOutput: " + + output); + } + + return output.toString(); + } + + public synchronized void start() { + if (started.compareAndSet(false, true)) { + create(); + } + } + + public synchronized void stop() { + // Astra databases are not automatically terminated + // They can be manually terminated via: astra db delete + LOG.error("Astra database {} (ID: {}) is still running", databaseName, databaseId); + LOG.error("To terminate manually, run: astra db delete {}", databaseName); + } + + @Override + public void close() { + stop(); + } + + public String getDatabaseName() { + return databaseName; + } + + public String getDatabaseId() { + return databaseId; + } + + public String getKeyspace() { + return keyspace; + } + + public File getSecureConnectBundle() { + return secureConnectBundle; + } + + public String getClientId() { + return clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public BackendType getDistribution() { + return DISTRIBUTION; + } +} diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraRule.java new file mode 100644 index 00000000000..6b300f7dec2 --- /dev/null +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraRule.java @@ -0,0 +1,82 @@ +/* + * 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 com.datastax.oss.driver.api.testinfra.astra; + +import com.datastax.oss.driver.categories.ParallelizableTests; +import org.junit.AssumptionViolatedException; +import org.junit.experimental.categories.Category; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * A rule that creates a globally shared Astra database that is only terminated after the JVM exits. + * + *

Note that this rule should be considered mutually exclusive with {@link CustomAstraRule}. + * Creating instances of these rules can create resource issues. + */ +public class AstraRule extends BaseAstraRule { + + private static final AstraRule INSTANCE = new AstraRule(); + + private volatile boolean started = false; + + private AstraRule() { + super(AstraBridge.builder().build()); + } + + @Override + protected synchronized void before() { + if (!started) { + // synchronize before so blocks on other before() call waiting to finish. + super.before(); + started = true; + } + } + + @Override + protected void after() { + // override after so we don't remove when done. + } + + @Override + public Statement apply(Statement base, Description description) { + + Category categoryAnnotation = description.getTestClass().getAnnotation(Category.class); + if (categoryAnnotation == null + || categoryAnnotation.value().length != 1 + || categoryAnnotation.value()[0] != ParallelizableTests.class) { + return new Statement() { + @Override + public void evaluate() { + throw new AssumptionViolatedException( + String.format( + "Tests using %s must be annotated with `@Category(%s.class)`. Description: %s", + AstraRule.class.getSimpleName(), + ParallelizableTests.class.getSimpleName(), + description)); + } + }; + } + + return super.apply(base, description); + } + + public static AstraRule getInstance() { + return INSTANCE; + } +} diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java new file mode 100644 index 00000000000..52c8ffdf414 --- /dev/null +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java @@ -0,0 +1,119 @@ +/* + * 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 com.datastax.oss.driver.api.testinfra.astra; + +import com.datastax.oss.driver.api.core.DefaultProtocolVersion; +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.Version; +import com.datastax.oss.driver.api.core.metadata.EndPoint; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirementRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; +import java.io.File; +import java.util.Collections; +import java.util.Set; +import org.junit.AssumptionViolatedException; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +public abstract class BaseAstraRule extends CassandraResourceRule { + + protected final AstraBridge astraBridge; + + BaseAstraRule(AstraBridge astraBridge) { + this.astraBridge = astraBridge; + Runtime.getRuntime() + .addShutdownHook( + new Thread( + () -> { + try { + astraBridge.close(); + } catch (Exception e) { + // silently remove as may have already been removed. + } + })); + } + + @Override + protected void before() { + astraBridge.create(); + astraBridge.start(); + } + + @Override + protected void after() { + astraBridge.close(); + } + + @Override + public Statement apply(Statement base, Description description) { + if (BackendRequirementRule.meetsDescriptionRequirements(description)) { + return super.apply(base, description); + } else { + // requirements not met, throw reasoning assumption to skip test + return new Statement() { + @Override + public void evaluate() { + throw new AssumptionViolatedException( + BackendRequirementRule.buildReasonString(description)); + } + }; + } + } + + public BackendType getDistribution() { + return AstraBridge.DISTRIBUTION; + } + + public boolean isDistributionOf(BackendType type) { + return AstraBridge.isDistributionOf(type); + } + + public Version getDistributionVersion() { + return AstraBridge.getDistributionVersion(); + } + + public Version getCassandraVersion() { + return AstraBridge.getCassandraVersion(); + } + + @Override + public ProtocolVersion getHighestProtocolVersion() { + // Astra supports protocol version V4 + return DefaultProtocolVersion.V4; + } + + @Override + public Set getContactPoints() { + // Astra uses Secure Connect Bundle instead of contact points + return Collections.emptySet(); + } + + /** + * Returns the Secure Connect Bundle file for connecting to Astra. + * + * @return the Secure Connect Bundle file + */ + public File getSecureConnectBundle() { + return astraBridge.getSecureConnectBundle(); + } + + public AstraBridge getAstraBridge() { + return astraBridge; + } +} diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java new file mode 100644 index 00000000000..0008f64607c --- /dev/null +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java @@ -0,0 +1,122 @@ +/* + * 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 com.datastax.oss.driver.api.testinfra.astra; + +import java.util.concurrent.atomic.AtomicReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A rule that creates an Astra database that can be used in a test. This should be used if you plan + * on creating databases with unique configurations, such as different cloud providers, regions, or + * keyspaces. If you do not plan on doing this at all in your tests, consider using {@link + * AstraRule} which creates a global Astra database that may be shared among tests. + * + *

Note that this rule should be considered mutually exclusive with {@link AstraRule}. Creating + * instances of these rules can create resource issues. + */ +public class CustomAstraRule extends BaseAstraRule { + + private static final Logger LOG = LoggerFactory.getLogger(CustomAstraRule.class); + private static final AtomicReference CURRENT = new AtomicReference<>(); + + CustomAstraRule(AstraBridge astraBridge) { + super(astraBridge); + } + + @Override + protected void before() { + if (CURRENT.get() == null && CURRENT.compareAndSet(null, this)) { + try { + super.before(); + } catch (Exception e) { + // ExternalResource will not call after() when before() throws an exception + // Let's try and clean up and release the lock we have in CURRENT + LOG.warn( + "Error in CustomAstraRule before() method, attempting to clean up leftover state", e); + try { + after(); + } catch (Exception e1) { + LOG.warn("Error cleaning up CustomAstraRule before() failure", e1); + e.addSuppressed(e1); + } + throw e; + } + } else if (CURRENT.get() != this) { + throw new IllegalStateException( + "Attempting to use an Astra rule while another is in use. This is disallowed"); + } + } + + @Override + protected void after() { + try { + super.after(); + } finally { + CURRENT.compareAndSet(this, null); + } + } + + @Override + public AstraBridge getAstraBridge() { + return astraBridge; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final AstraBridge.Builder bridgeBuilder = AstraBridge.builder(); + + public Builder withDatabaseName(String databaseName) { + bridgeBuilder.withDatabaseName(databaseName); + return this; + } + + public Builder withKeyspace(String keyspace) { + bridgeBuilder.withKeyspace(keyspace); + return this; + } + + public Builder withCloudProvider(String cloudProvider) { + bridgeBuilder.withCloudProvider(cloudProvider); + return this; + } + + public Builder withRegion(String region) { + bridgeBuilder.withRegion(region); + return this; + } + + public Builder withClientId(String clientId) { + bridgeBuilder.withClientId(clientId); + return this; + } + + public Builder withClientSecret(String clientSecret) { + bridgeBuilder.withClientSecret(clientSecret); + return this; + } + + public CustomAstraRule build() { + return new CustomAstraRule(bridgeBuilder.build()); + } + } +} diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/BackendType.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/BackendType.java index e0058ca324a..a1ff09ff3bd 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/BackendType.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/BackendType.java @@ -20,7 +20,8 @@ public enum BackendType { CASSANDRA("Apache Cassandra"), DSE("DSE"), - HCD("HCD"); + HCD("HCD"), + ASTRA("Astra DB"); final String friendlyName; @@ -33,7 +34,7 @@ public String getFriendlyName() { } public String[] getCcmOptions() { - if (this == CASSANDRA) { + if (this == CASSANDRA || this == ASTRA) { return new String[0]; } return new String[] {"--" + name().toLowerCase()}; diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java index 7536c0ffdc0..9925f73c1d7 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java @@ -31,7 +31,9 @@ import com.datastax.oss.driver.api.core.session.Session; import com.datastax.oss.driver.api.core.session.SessionBuilder; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.astra.BaseAstraRule; import com.datastax.oss.driver.internal.core.loadbalancing.helper.NodeFilterToDistanceEvaluatorAdapter; +import java.io.File; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; @@ -147,8 +149,31 @@ private static SessionBuilder builder( SchemaChangeListener schemaChangeListener, Predicate nodeFilter) { SessionBuilder builder = baseBuilder(); + + // Check if this is an Astra resource - use Secure Connect Bundle instead of contact points + if (cassandraResource instanceof BaseAstraRule) { + BaseAstraRule astraRule = (BaseAstraRule) cassandraResource; + File secureConnectBundle = astraRule.getSecureConnectBundle(); + if (secureConnectBundle != null) { + builder.withCloudSecureConnectBundle(secureConnectBundle.toPath()); + + // Add authentication credentials for Astra + String clientId = astraRule.getAstraBridge().getClientId(); + String clientSecret = astraRule.getAstraBridge().getClientSecret(); + if (clientId != null && clientSecret != null) { + builder.withAuthCredentials(clientId, clientSecret); + } + } else { + throw new IllegalStateException( + "Astra Secure Connect Bundle is not available. " + + "Make sure the AstraRule has been initialized."); + } + } else { + // For non-Astra resources, use contact points + builder.addContactEndPoints(cassandraResource.getContactPoints()); + } + builder - .addContactEndPoints(cassandraResource.getContactPoints()) .withKeyspace(keyspace) .withNodeStateListener(nodeStateListener) .withSchemaChangeListener(schemaChangeListener); From ee9ef83dc8aa86a15f47f3f5aaddf501e813039b Mon Sep 17 00:00:00 2001 From: janehe Date: Thu, 11 Dec 2025 17:08:29 -0800 Subject: [PATCH 02/27] simple statement astra IT seems to work. --- .../api/testinfra/astra/AstraBridge.java | 52 ++++++++++++++++--- .../api/testinfra/session/SessionRule.java | 32 ++++++++++-- .../api/testinfra/session/SessionUtils.java | 10 ++-- 3 files changed, 77 insertions(+), 17 deletions(-) diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 0ff99c29226..b280e44f1e6 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -29,6 +29,8 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -153,7 +155,7 @@ public AstraBridge build() { public synchronized void create() { if (created.compareAndSet(false, true)) { try { - LOG.error("Creating Astra database: {}", databaseName); + LOG.info("Creating Astra database: {}", databaseName); // Setup Astra CLI with token runAstraCommand("setup", "--token", ASTRA_TOKEN); @@ -174,11 +176,15 @@ public synchronized void create() { createArgs.add(databaseName); String output = runAstraCommand(createArgs.toArray(new String[0])); - LOG.error("Database creation output: {}", output); + LOG.info("Database creation output: {}", output); // Get database ID using: astra db get --key id - databaseId = runAstraCommand("db", "get", databaseName, "--key", "id").trim(); - LOG.error("Astra database created with ID: {}", databaseId); + String dbIdOutput = runAstraCommand("db", "get", databaseName, "--key", "id"); + LOG.info("Database ID output: {}", dbIdOutput); + + // Extract the UUID from the output (filter out [INFO] and other lines) + databaseId = extractDatabaseId(dbIdOutput); + LOG.info("Astra database created with ID: {}", databaseId); // Download secure connect bundle downloadSecureConnectBundle(); @@ -207,6 +213,34 @@ private void downloadSecureConnectBundle() throws IOException, InterruptedExcept LOG.info("Secure connect bundle downloaded to: {}", scbFile.getAbsolutePath()); } + private static final Pattern UUID_PATTERN = + Pattern.compile( + "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"); + + /** + * Extract database ID (UUID) from Astra CLI output. The output may contain [INFO] lines and other + * messages, so we need to find the line that looks like a UUID. E.g. ✗ astra db get + * java_driver_test_db_1765404086049 --key id [INFO] You are using a non-production environment + * 'DEV' be2d3ad0-3bb0-4257-97d6-b83f2265b5f1 + */ + private String extractDatabaseId(String output) { + // Use Pattern.compile to split by newline to avoid String.split() warning + String[] lines = Pattern.compile("\n").split(output); + for (String line : lines) { + String trimmed = line.trim(); + // Skip lines that start with [INFO], [OK], [ERROR], etc. + if (trimmed.startsWith("[")) { + continue; + } + // Check if this line contains a UUID + Matcher matcher = UUID_PATTERN.matcher(trimmed); + if (matcher.find()) { + return matcher.group(); + } + } + throw new IllegalStateException("Could not extract database ID from output: " + output); + } + private String runAstraCommand(String... args) throws IOException, InterruptedException { List command = new ArrayList<>(); command.add("astra"); @@ -214,7 +248,7 @@ private String runAstraCommand(String... args) throws IOException, InterruptedEx command.add(arg); } - LOG.error("Running Astra CLI command: {}", String.join(" ", command)); + LOG.info("Running Astra CLI command: {}", String.join(" ", command)); ProcessBuilder pb = new ProcessBuilder(command); pb.redirectErrorStream(true); @@ -254,8 +288,8 @@ public synchronized void start() { public synchronized void stop() { // Astra databases are not automatically terminated // They can be manually terminated via: astra db delete - LOG.error("Astra database {} (ID: {}) is still running", databaseName, databaseId); - LOG.error("To terminate manually, run: astra db delete {}", databaseName); + LOG.info("Astra database {} (ID: {}) is still running", databaseName, databaseId); + LOG.info("To terminate manually, run: astra db delete {}", databaseName); } @Override @@ -287,6 +321,10 @@ public String getClientSecret() { return clientSecret; } + public String getToken() { + return ASTRA_TOKEN; + } + public BackendType getDistribution() { return DISTRIBUTION; } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java index 3b792374769..4e485a2e567 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java @@ -28,6 +28,7 @@ import com.datastax.oss.driver.api.core.metadata.schema.SchemaChangeListener; import com.datastax.oss.driver.api.core.session.Session; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.astra.BaseAstraRule; import com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule; import com.datastax.oss.driver.api.testinfra.ccm.CcmBridge; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; @@ -144,11 +145,29 @@ public SessionRule( @Override protected void before() { - session = - SessionUtils.newSession( - cassandraResource, null, nodeStateListener, schemaChangeListener, null, configLoader); + // For Astra, use the keyspace that was created with the database through Astra CLI + if (cassandraResource instanceof BaseAstraRule) { + BaseAstraRule astraRule = (BaseAstraRule) cassandraResource; + String astraKeyspace = astraRule.getAstraBridge().getKeyspace(); + CqlIdentifier sessionKeyspace = CqlIdentifier.fromCql(astraKeyspace); + session = + SessionUtils.newSession( + cassandraResource, + sessionKeyspace, + nodeStateListener, + schemaChangeListener, + null, + configLoader); + } else { + session = + SessionUtils.newSession( + cassandraResource, null, nodeStateListener, schemaChangeListener, null, configLoader); + } + slowProfile = SessionUtils.slowProfile(session); - if (keyspace != null) { + + // Only create keyspace for non-Astra resources + if (keyspace != null && !(cassandraResource instanceof BaseAstraRule)) { SessionUtils.createKeyspace(session, keyspace, slowProfile); session.execute( SimpleStatement.newInstance(String.format("USE %s", keyspace.asCql(false))), @@ -194,7 +213,10 @@ protected void after() { .setSystemQuery(true), ScriptGraphStatement.SYNC); } - if (keyspace != null) { + // Only drop keyspace for non-Astra resources (Astra keyspaces are managed by Astra) + if (keyspace != null + && !(cassandraResource + instanceof com.datastax.oss.driver.api.testinfra.astra.BaseAstraRule)) { SchemaChangeSynchronizer.withLock( () -> { SessionUtils.dropKeyspace(session, keyspace, slowProfile); diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java index 9925f73c1d7..e5ea07aeb77 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java @@ -157,11 +157,11 @@ private static SessionBuilder builder( if (secureConnectBundle != null) { builder.withCloudSecureConnectBundle(secureConnectBundle.toPath()); - // Add authentication credentials for Astra - String clientId = astraRule.getAstraBridge().getClientId(); - String clientSecret = astraRule.getAstraBridge().getClientSecret(); - if (clientId != null && clientSecret != null) { - builder.withAuthCredentials(clientId, clientSecret); + // Add authentication credentials for Astra using token + // For Astra, username is "token" and password is the actual token value + String token = astraRule.getAstraBridge().getToken(); + if (token != null) { + builder.withAuthCredentials("token", token); } } else { throw new IllegalStateException( From 642142819a57d0bbe3e3be120ad6a91092502ef8 Mon Sep 17 00:00:00 2001 From: janehe Date: Fri, 12 Dec 2025 11:29:14 -0800 Subject: [PATCH 03/27] can tear down --- .../api/testinfra/astra/AstraBridge.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index b280e44f1e6..b4a4cb24ed8 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -286,10 +286,22 @@ public synchronized void start() { } public synchronized void stop() { - // Astra databases are not automatically terminated - // They can be manually terminated via: astra db delete - LOG.info("Astra database {} (ID: {}) is still running", databaseName, databaseId); - LOG.info("To terminate manually, run: astra db delete {}", databaseName); + if (databaseName == null) { + LOG.info("No Astra database to terminate"); + return; + } + + try { + LOG.info("Terminating Astra database: {}", databaseName); + + // Delete the database asynchronously (don't wait for completion) + String deleteOutput = runAstraCommand("db", "delete", databaseName, "--async"); + LOG.info("Database deletion initiated: {}", deleteOutput); + LOG.info("Astra database {} (ID: {}) is being terminated", databaseName, databaseId); + } catch (Exception e) { + LOG.warn("Failed to terminate Astra database {}: {}", databaseName, e.getMessage()); + LOG.info("To terminate manually, run: astra db delete {}", databaseName); + } } @Override From 5e3efe022e35938db63cec7dc26bdf8acf87ca07 Mon Sep 17 00:00:00 2001 From: janehe Date: Mon, 15 Dec 2025 18:42:34 -0800 Subject: [PATCH 04/27] can tear down --- .../api/testinfra/astra/AstraBridge.java | 33 ++----------------- .../api/testinfra/astra/CustomAstraRule.java | 10 ------ 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index b4a4cb24ed8..8951ae5c36a 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -42,8 +42,6 @@ public class AstraBridge implements AutoCloseable { // Astra CLI configuration private static final String ASTRA_TOKEN = System.getProperty("astra.token"); - private static final String ASTRA_CLIENT_ID = System.getProperty("astra.client.id", "token"); - private static final String ASTRA_CLIENT_SECRET = System.getProperty("astra.client.secret"); private static final String ASTRA_CLOUD_PROVIDER = System.getProperty("astra.cloud.provider", "gcp"); private static final String ASTRA_REGION = System.getProperty("astra.region", "us-east1"); @@ -59,8 +57,6 @@ public class AstraBridge implements AutoCloseable { private final String cloudProvider; private final String region; - private final String clientId; - private final String clientSecret; private final AtomicBoolean created = new AtomicBoolean(); private final AtomicBoolean started = new AtomicBoolean(); private final Path configDirectory; @@ -73,16 +69,12 @@ private AstraBridge( String databaseName, String keyspace, String cloudProvider, - String region, - String clientId, - String clientSecret) { + String region) { this.configDirectory = configDirectory; this.databaseName = databaseName; this.keyspace = keyspace; this.cloudProvider = cloudProvider; this.region = region; - this.clientId = clientId; - this.clientSecret = clientSecret; } public static Builder builder() { @@ -108,8 +100,6 @@ public static class Builder { private String keyspace = DEFAULT_KEYSPACE; private String cloudProvider = ASTRA_CLOUD_PROVIDER; private String region = ASTRA_REGION; - private String clientId = ASTRA_CLIENT_ID; - private String clientSecret = ASTRA_CLIENT_SECRET; public Builder withDatabaseName(String databaseName) { this.databaseName = databaseName; @@ -131,21 +121,10 @@ public Builder withRegion(String region) { return this; } - public Builder withClientId(String clientId) { - this.clientId = clientId; - return this; - } - - public Builder withClientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - public AstraBridge build() { try { Path configDir = Files.createTempDirectory("astra-test-"); - return new AstraBridge( - configDir, databaseName, keyspace, cloudProvider, region, clientId, clientSecret); + return new AstraBridge(configDir, databaseName, keyspace, cloudProvider, region); } catch (IOException e) { throw new RuntimeException("Failed to create config directory", e); } @@ -325,14 +304,6 @@ public File getSecureConnectBundle() { return secureConnectBundle; } - public String getClientId() { - return clientId; - } - - public String getClientSecret() { - return clientSecret; - } - public String getToken() { return ASTRA_TOKEN; } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java index 0008f64607c..cc60305ced2 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java @@ -105,16 +105,6 @@ public Builder withRegion(String region) { return this; } - public Builder withClientId(String clientId) { - bridgeBuilder.withClientId(clientId); - return this; - } - - public Builder withClientSecret(String clientSecret) { - bridgeBuilder.withClientSecret(clientSecret); - return this; - } - public CustomAstraRule build() { return new CustomAstraRule(bridgeBuilder.build()); } From 6dcc5193cb65640e7b2e702d10f78bb7db52affc Mon Sep 17 00:00:00 2001 From: janehe Date: Tue, 16 Dec 2025 11:01:06 -0800 Subject: [PATCH 05/27] SimpleStatementIT works with both --- .../driver/core/cql/SimpleStatementIT.java | 415 ++++++++++++++++++ .../driver/api/testinfra/CassandraBridge.java | 66 +++ .../CassandraResourceRuleFactory.java | 78 ++++ .../api/testinfra/astra/AstraBridge.java | 7 +- .../driver/api/testinfra/ccm/CcmBridge.java | 11 +- 5 files changed, 575 insertions(+), 2 deletions(-) create mode 100644 integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementIT.java create mode 100644 test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraBridge.java create mode 100644 test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRuleFactory.java diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementIT.java new file mode 100644 index 00000000000..b7bb0875586 --- /dev/null +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementIT.java @@ -0,0 +1,415 @@ +/* + * 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 com.datastax.oss.driver.core.cql; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.cql.AsyncResultSet; +import com.datastax.oss.driver.api.core.cql.ResultSet; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.session.SessionRule; +import com.datastax.oss.driver.api.testinfra.session.SessionUtils; +import com.datastax.oss.driver.categories.ParallelizableTests; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeUnit; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.RuleChain; +import org.junit.rules.TestName; +import org.junit.rules.TestRule; + +/** + * Unified test for SimpleStatement that can run against either Cassandra (via CCM) or Astra. + * + *

The backend is selected via the {@code ccm.distribution} system property, which is handled by + * {@link CassandraResourceRuleFactory}. + * + *

To run against Cassandra OSS (default): + * + *

+ * mvn test -Dtest=SimpleStatementIT
+ * 
+ * + *

To run against Astra: + * + *

+ * mvn test -Dtest=SimpleStatementIT -Dastra.token="..." -Dccm.distribution=ASTRA
+ * 
+ */ +@Category(ParallelizableTests.class) +public class SimpleStatementIT { + + private static final CassandraResourceRule CASSANDRA_RESOURCE = + CassandraResourceRuleFactory.getInstance(); + + private static final SessionRule SESSION_RULE = + SessionRule.builder(CASSANDRA_RESOURCE) + .withConfigLoader( + SessionUtils.configLoaderBuilder() + .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 20) + .build()) + .build(); + + @ClassRule + public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); + + @Rule public TestName name = new TestName(); + + private static final String KEY = "test"; + + @BeforeClass + public static void setupSchema() { + // table where every column forms the primary key. + SESSION_RULE + .session() + .execute( + SimpleStatement.builder( + "CREATE TABLE IF NOT EXISTS test (k text, v int, PRIMARY KEY(k, v))") + .setExecutionProfile(SESSION_RULE.slowProfile()) + .build()); + for (int i = 0; i < 100; i++) { + SESSION_RULE + .session() + .execute( + SimpleStatement.builder("INSERT INTO test (k, v) VALUES (?, ?)") + .addPositionalValues(KEY, i) + .build()); + } + + // table with simple primary key, single cell. + SESSION_RULE + .session() + .execute( + SimpleStatement.builder("CREATE TABLE IF NOT EXISTS test2 (k text primary key, v int)") + .setExecutionProfile(SESSION_RULE.slowProfile()) + .build()); + } + + @Test + public void should_use_paging_state_when_copied() { + Statement st = + SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); + ResultSet result = SESSION_RULE.session().execute(st); + + // given a query created from a copy of a previous query with paging state from previous queries + // response. + st = st.copy(result.getExecutionInfo().getPagingState()); + + // when executing that query. + result = SESSION_RULE.session().execute(st); + + // then the response should start on the page boundary. + assertThat(result.iterator().next().getInt("v")).isEqualTo(20); + } + + @Test + public void should_use_paging_state_when_provided_to_new_statement() { + Statement st = + SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); + ResultSet result = SESSION_RULE.session().execute(st); + + // given a query created from a copy of a previous query with paging state from previous queries + // response. + st = + SimpleStatement.builder(String.format("SELECT v FROM test where k='%s'", KEY)) + .setPagingState(result.getExecutionInfo().getPagingState()) + .build(); + + // when executing that query. + result = SESSION_RULE.session().execute(st); + + // then the response should start on the page boundary. + assertThat(result.iterator().next().getInt("v")).isEqualTo(20); + } + + @Test + @Ignore + public void should_fail_if_using_paging_state_from_different_query() { + Statement st = + SimpleStatement.builder("SELECT v FROM test WHERE k=:k").addNamedValue("k", KEY).build(); + ResultSet result = SESSION_RULE.session().execute(st); + + // TODO Expect PagingStateException + + // given a new different query and providing the paging state from the previous query + // then an exception should be thrown indicating incompatible paging state + SimpleStatement.builder("SELECT v FROM test") + .setPagingState(result.getExecutionInfo().getPagingState()) + .build(); + } + + @Test + public void should_use_timestamp_when_set() { + // given inserting data with a timestamp 40 days in the past. + long timestamp = System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(40, TimeUnit.DAYS); + SimpleStatement insert = + SimpleStatement.builder("INSERT INTO test2 (k, v) values (?, ?)") + .addPositionalValues(name.getMethodName(), 0) + .setQueryTimestamp(timestamp) + .build(); + + SESSION_RULE.session().execute(insert); + + // when retrieving writetime of cell from that insert. + SimpleStatement select = + SimpleStatement.builder("SELECT writetime(v) as wv from test2 where k = ?") + .addPositionalValue(name.getMethodName()) + .build(); + + ResultSet result = SESSION_RULE.session().execute(select); + List rows = result.all(); + assertThat(rows).hasSize(1); + + // then the writetime should equal the timestamp provided. + Row row = rows.iterator().next(); + assertThat(row.getLong("wv")).isEqualTo(timestamp); + } + + @Test + @Ignore + public void should_use_tracing_when_set() { + // TODO currently there's no way to validate tracing was set since trace id is not set + // also write test to verify it is not set. + SESSION_RULE + .session() + .execute(SimpleStatement.builder("select * from test").setTracing().build()); + } + + @Test + public void should_execute_query() { + Statement statement = + SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); + + ResultSet result = SESSION_RULE.session().execute(statement); + + List rows = result.all(); + assertThat(rows).hasSize(100); + } + + @Test + public void should_execute_query_async() throws Exception { + Statement statement = + SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); + + CompletionStage future = SESSION_RULE.session().executeAsync(statement); + + AsyncResultSet result = CompletableFutures.getUninterruptibly(future); + + assertThat(result.remaining()).isEqualTo(20); + } + + @Test + public void should_use_positional_values() { + // given a statement with positional values + SimpleStatement insert = + SimpleStatement.builder("INSERT into test2 (k, v) values (?, ?)") + .addPositionalValue(name.getMethodName()) + .addPositionalValue(4) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then we should be able to retrieve the data as inserted. + SimpleStatement select = + SimpleStatement.builder("select k,v from test2 where k=?") + .addPositionalValue(name.getMethodName()) + .build(); + + ResultSet result = SESSION_RULE.session().execute(select); + List rows = result.all(); + assertThat(rows).hasSize(1); + + Row row = rows.iterator().next(); + assertThat(row.getString("k")).isEqualTo(name.getMethodName()); + assertThat(row.getInt("v")).isEqualTo(4); + } + + @Test + public void should_allow_nulls_in_positional_values() { + // given a statement with positional values + SimpleStatement insert = + SimpleStatement.builder("INSERT into test2 (k, v) values (?, ?)") + .addPositionalValue(name.getMethodName()) + .addPositionalValue(null) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then we should be able to retrieve the data as inserted. + SimpleStatement select = + SimpleStatement.builder("select k,v from test2 where k=?") + .addPositionalValue(name.getMethodName()) + .build(); + + ResultSet result = SESSION_RULE.session().execute(select); + List rows = result.all(); + assertThat(rows).hasSize(1); + + Row row = rows.iterator().next(); + assertThat(row.getString("k")).isEqualTo(name.getMethodName()); + assertThat(row.getObject("v")).isNull(); + } + + @Test(expected = InvalidQueryException.class) + public void should_fail_when_too_many_positional_values_provided() { + // given a statement with more bound values than anticipated (3 given vs. 2 expected) + SimpleStatement insert = + SimpleStatement.builder("INSERT into test (k, v) values (?, ?)") + .addPositionalValues(KEY, 0, 7) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then the server will throw an InvalidQueryException which is thrown up to the client. + } + + @Test(expected = InvalidQueryException.class) + public void should_fail_when_not_enough_positional_values_provided() { + // given a statement with not enough bound values (1 given vs. 2 expected) + SimpleStatement insert = + SimpleStatement.builder("SELECT * from test where k = ? and v = ?") + .addPositionalValue(KEY) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then the server will throw an InvalidQueryException which is thrown up to the client. + } + + @Test + public void should_use_named_values() { + // given a statement with named values + SimpleStatement insert = + SimpleStatement.builder("INSERT into test2 (k, v) values (:k, :v)") + .addNamedValue("k", name.getMethodName()) + .addNamedValue("v", 7) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then we should be able to retrieve the data as inserted. + SimpleStatement select = + SimpleStatement.builder("select k,v from test2 where k=:k") + .addNamedValue("k", name.getMethodName()) + .build(); + + ResultSet result = SESSION_RULE.session().execute(select); + List rows = result.all(); + assertThat(rows).hasSize(1); + + Row row = rows.iterator().next(); + assertThat(row.getString("k")).isEqualTo(name.getMethodName()); + assertThat(row.getInt("v")).isEqualTo(7); + } + + @Test + public void should_allow_nulls_in_named_values() { + // given a statement with named values + SimpleStatement insert = + SimpleStatement.builder("INSERT into test2 (k, v) values (:k, :v)") + .addNamedValue("k", name.getMethodName()) + .addNamedValue("v", null) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then we should be able to retrieve the data as inserted. + SimpleStatement select = + SimpleStatement.builder("select k,v from test2 where k=:k") + .addNamedValue("k", name.getMethodName()) + .build(); + + ResultSet result = SESSION_RULE.session().execute(select); + List rows = result.all(); + assertThat(rows).hasSize(1); + + Row row = rows.iterator().next(); + assertThat(row.getString("k")).isEqualTo(name.getMethodName()); + assertThat(row.getObject("v")).isNull(); + } + + @Test(expected = InvalidQueryException.class) + public void should_fail_when_named_value_missing() { + // given a statement with a missing named value (:k) + SimpleStatement insert = + SimpleStatement.builder("SELECT * from test where k = :k and v = :v") + .addNamedValue("v", 0) + .build(); + + // when executing that statement + SESSION_RULE.session().execute(insert); + + // then the server will throw an InvalidQueryException which is thrown up to the client. + } + + @Test(expected = IllegalArgumentException.class) + public void should_fail_when_mixing_named_and_positional_values() { + SimpleStatement.builder("SELECT * from test where k = :k and v = :v") + .addNamedValue("k", KEY) + .addPositionalValue(0) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void should_fail_when_mixing_positional_and_named_values() { + SimpleStatement.builder("SELECT * from test where k = :k and v = :v") + .addPositionalValue(0) + .addNamedValue("k", KEY) + .build(); + } + + @Test + public void should_use_positional_value_with_case_sensitive_id() { + SimpleStatement statement = + SimpleStatement.builder("SELECT count(*) FROM test2 WHERE k=:\"theKey\"") + .addNamedValue(CqlIdentifier.fromCql("\"theKey\""), 0) + .build(); + Row row = SESSION_RULE.session().execute(statement).one(); + assertThat(row.getLong(0)).isEqualTo(0); + } + + @Test + public void should_use_page_size() { + Statement st = SimpleStatement.builder("SELECT v FROM test").setPageSize(10).build(); + CompletionStage future = SESSION_RULE.session().executeAsync(st); + AsyncResultSet result = CompletableFutures.getUninterruptibly(future); + + // Should have only fetched 10 (page size) rows. + assertThat(result.remaining()).isEqualTo(10); + } +} diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraBridge.java new file mode 100644 index 00000000000..a3897aae2e4 --- /dev/null +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraBridge.java @@ -0,0 +1,66 @@ +/* + * 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 com.datastax.oss.driver.api.testinfra; + +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; + +/** + * Common interface for Cassandra backend bridges (CCM and Astra). + * + *

This interface defines the lifecycle methods and common operations that both {@link + * com.datastax.oss.driver.api.testinfra.ccm.CcmBridge} and {@link + * com.datastax.oss.driver.api.testinfra.astra.AstraBridge} implement. + */ +public interface CassandraBridge extends AutoCloseable { + + /** + * Creates the Cassandra backend (cluster or database). + * + *

For CCM, this creates a local cluster. For Astra, this creates a cloud database. + */ + void create(); + + /** + * Starts the Cassandra backend. + * + *

For CCM, this starts the cluster nodes. For Astra, this ensures the database is active. + */ + void start(); + + /** + * Stops the Cassandra backend. + * + *

For CCM, this stops the cluster nodes. For Astra, this initiates database termination. + */ + void stop(); + + /** + * Returns the backend type (distribution) of this bridge. + * + * @return the backend type + */ + BackendType getDistribution(); + + /** + * Closes this bridge and releases any resources. + * + *

For CCM, this removes the cluster. For Astra, this terminates the database. + */ + @Override + void close(); +} diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRuleFactory.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRuleFactory.java new file mode 100644 index 00000000000..088f1608bc0 --- /dev/null +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRuleFactory.java @@ -0,0 +1,78 @@ +/* + * 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 com.datastax.oss.driver.api.testinfra; + +import com.datastax.oss.driver.api.testinfra.astra.AstraRule; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; + +/** + * Factory for creating {@link CassandraResourceRule} instances based on the {@code + * ccm.distribution} system property. + * + *

This allows tests to run against different backends (Cassandra OSS, DSE, HCD, Astra) by simply + * setting the {@code ccm.distribution} system property. + * + *

Example usage: + * + *

{@code
+ * @ClassRule
+ * public static final CassandraResourceRule CASSANDRA_RESOURCE =
+ *     CassandraResourceRuleFactory.getInstance();
+ * }
+ * + *

To run against Cassandra OSS (default): + * + *

+ * mvn test -Dtest=MyTest
+ * 
+ * + *

To run against Astra: + * + *

+ * mvn test -Dtest=MyTest -Dccm.distribution=ASTRA -Dastra.token="..."
+ * 
+ */ +public class CassandraResourceRuleFactory { + + private static final BackendType DISTRIBUTION = + BackendType.valueOf( + System.getProperty("ccm.distribution", BackendType.CASSANDRA.name()).toUpperCase()); + + /** + * Returns a {@link CassandraResourceRule} instance based on the {@code ccm.distribution} system + * property. + * + *

If {@code ccm.distribution=ASTRA}, returns {@link AstraRule#getInstance()}. Otherwise, + * returns {@link CcmRule#getInstance()}. + * + * @return the appropriate CassandraResourceRule for the configured distribution + */ + public static CassandraResourceRule getInstance() { + return DISTRIBUTION == BackendType.ASTRA ? AstraRule.getInstance() : CcmRule.getInstance(); + } + + /** + * Returns the configured backend distribution type. + * + * @return the BackendType from the ccm.distribution system property + */ + public static BackendType getDistribution() { + return DISTRIBUTION; + } +} diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 8951ae5c36a..6cb9b810a32 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -18,6 +18,7 @@ package com.datastax.oss.driver.api.testinfra.astra; import com.datastax.oss.driver.api.core.Version; +import com.datastax.oss.driver.api.testinfra.CassandraBridge; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import java.io.BufferedReader; import java.io.File; @@ -34,7 +35,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class AstraBridge implements AutoCloseable { +public class AstraBridge implements CassandraBridge { private static final Logger LOG = LoggerFactory.getLogger(AstraBridge.class); @@ -131,6 +132,7 @@ public AstraBridge build() { } } + @Override public synchronized void create() { if (created.compareAndSet(false, true)) { try { @@ -258,12 +260,14 @@ private String runAstraCommand(String... args) throws IOException, InterruptedEx return output.toString(); } + @Override public synchronized void start() { if (started.compareAndSet(false, true)) { create(); } } + @Override public synchronized void stop() { if (databaseName == null) { LOG.info("No Astra database to terminate"); @@ -308,6 +312,7 @@ public String getToken() { return ASTRA_TOKEN; } + @Override public BackendType getDistribution() { return DISTRIBUTION; } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmBridge.java index f0ce6bc5b0e..31491cf37dc 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmBridge.java @@ -18,6 +18,7 @@ package com.datastax.oss.driver.api.testinfra.ccm; import com.datastax.oss.driver.api.core.Version; +import com.datastax.oss.driver.api.testinfra.CassandraBridge; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.shaded.guava.common.base.Joiner; import com.datastax.oss.driver.shaded.guava.common.io.Resources; @@ -51,7 +52,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class CcmBridge implements AutoCloseable { +public class CcmBridge implements CassandraBridge { private static final Logger LOG = LoggerFactory.getLogger(CcmBridge.class); @@ -215,6 +216,7 @@ private String getCcmVersionString(Version version) { return version.toString(); } + @Override public void create() { if (created.compareAndSet(false, true)) { if (INSTALL_DIRECTORY != null) { @@ -288,6 +290,7 @@ public void reloadCore(int node, String keyspace, String table, boolean reindex) dsetool(node, "reload_core", keyspace + "." + table, "reindex=" + reindex); } + @Override public void start() { if (started.compareAndSet(false, true)) { List cmdAndArgs = Lists.newArrayList("start", jvmArgs, "--wait-for-binary-proto"); @@ -302,6 +305,7 @@ public void start() { } } + @Override public void stop() { if (started.compareAndSet(true, false)) { execute("stop"); @@ -429,6 +433,11 @@ public void close() { } } + @Override + public BackendType getDistribution() { + return DISTRIBUTION; + } + /** * Extracts a keystore from the classpath into a temporary file. * From 68b2bafc89ed4fcb90c92404bba0a922ef62b6e3 Mon Sep 17 00:00:00 2001 From: janehe Date: Fri, 19 Dec 2025 12:44:12 -0800 Subject: [PATCH 06/27] creation of keyspace works. several tests work --- .../oss/driver/core/cql/AsyncResultSetIT.java | 18 +- .../oss/driver/core/cql/BatchStatementIT.java | 19 +- .../driver/core/cql/BoundStatementCcmIT.java | 65 +-- .../oss/driver/core/cql/NowInSecondsIT.java | 11 +- .../core/cql/PagingIterableSpliteratorIT.java | 19 +- .../oss/driver/core/cql/PagingStateIT.java | 23 +- .../driver/core/cql/PerRequestKeyspaceIT.java | 13 +- .../driver/core/cql/PreparedStatementIT.java | 26 +- .../oss/driver/core/cql/QueryTraceIT.java | 17 +- .../core/cql/SimpleStatementAstraIT.java | 372 ---------------- .../driver/core/cql/SimpleStatementCcmIT.java | 27 +- .../driver/core/cql/SimpleStatementIT.java | 415 ------------------ .../reactive/DefaultReactiveResultSetIT.java | 12 +- test-infra/revapi.json | 194 ++++++++ .../api/testinfra/CassandraResourceRule.java | 19 + .../api/testinfra/astra/AstraBridge.java | 29 ++ .../api/testinfra/astra/BaseAstraRule.java | 4 + .../driver/api/testinfra/ccm/BaseCcmRule.java | 17 + .../ccm/DistributionCassandraVersions.java | 8 + .../api/testinfra/session/SessionRule.java | 38 +- .../api/testinfra/session/SessionUtils.java | 15 + .../testinfra/simulacron/SimulacronRule.java | 25 ++ 22 files changed, 485 insertions(+), 901 deletions(-) delete mode 100644 integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementAstraIT.java delete mode 100644 integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementIT.java diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java index e109c28525e..3f9ebde917a 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java @@ -28,7 +28,8 @@ import com.datastax.oss.driver.api.core.cql.PreparedStatement; import com.datastax.oss.driver.api.core.cql.Row; import com.datastax.oss.driver.api.core.cql.SimpleStatement; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; @@ -52,10 +53,11 @@ public class AsyncResultSetIT { private static final String PARTITION_KEY1 = "part"; private static final String PARTITION_KEY2 = "part2"; - private static final CcmRule CCM_RULE = CcmRule.getInstance(); + private static final CassandraResourceRule CASSANDRA_RESOURCE = + CassandraResourceRuleFactory.getInstance(); private static final SessionRule SESSION_RULE = - SessionRule.builder(CCM_RULE) + SessionRule.builder(CASSANDRA_RESOURCE) .withConfigLoader( SessionUtils.configLoaderBuilder() .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, PAGE_SIZE) @@ -63,18 +65,24 @@ public class AsyncResultSetIT { .build(); @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); + public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); @BeforeClass public static void setupSchema() { // create table and load data across two partitions so we can test paging across tokens. SchemaChangeSynchronizer.withLock( () -> { + SESSION_RULE + .session() + .execute( + SimpleStatement.builder("DROP TABLE IF EXISTS test") + .setExecutionProfile(SESSION_RULE.slowProfile()) + .build()); SESSION_RULE .session() .execute( SimpleStatement.builder( - "CREATE TABLE IF NOT EXISTS test (k0 text, k1 int, v int, PRIMARY KEY(k0, k1))") + "CREATE TABLE test (k0 text, k1 int, v int, PRIMARY KEY(k0, k1))") .setExecutionProfile(SESSION_RULE.slowProfile()) .build()); }); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java index 8b652638729..7e6d68737dd 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java @@ -33,7 +33,8 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; @@ -53,11 +54,11 @@ @Category(ParallelizableTests.class) public class BatchStatementIT { - private CcmRule ccmRule = CcmRule.getInstance(); + private CassandraResourceRule cassandraResource = CassandraResourceRuleFactory.getInstance(); - private SessionRule sessionRule = SessionRule.builder(ccmRule).build(); + private SessionRule sessionRule = SessionRule.builder(cassandraResource).build(); - @Rule public TestRule chain = RuleChain.outerRule(ccmRule).around(sessionRule); + @Rule public TestRule chain = RuleChain.outerRule(cassandraResource).around(sessionRule); @Rule public TestName name = new TestName(); @@ -67,10 +68,10 @@ public class BatchStatementIT { public void createTable() { String[] schemaStatements = new String[] { - "CREATE TABLE test (k0 text, k1 int, v int, PRIMARY KEY (k0, k1))", - "CREATE TABLE counter1 (k0 text PRIMARY KEY, c counter)", - "CREATE TABLE counter2 (k0 text PRIMARY KEY, c counter)", - "CREATE TABLE counter3 (k0 text PRIMARY KEY, c counter)", + "CREATE TABLE IF NOT EXISTS test (k0 text, k1 int, v int, PRIMARY KEY (k0, k1))", + "CREATE TABLE IF NOT EXISTS counter1 (k0 text PRIMARY KEY, c counter)", + "CREATE TABLE IF NOT EXISTS counter2 (k0 text PRIMARY KEY, c counter)", + "CREATE TABLE IF NOT EXISTS counter3 (k0 text PRIMARY KEY, c counter)", }; SchemaChangeSynchronizer.withLock( @@ -353,7 +354,7 @@ public void should_not_allow_unset_value_when_protocol_less_than_v4() { SessionUtils.configLoaderBuilder() .withString(DefaultDriverOption.PROTOCOL_VERSION, "V3") .build(); - try (CqlSession v3Session = SessionUtils.newSession(ccmRule, loader)) { + try (CqlSession v3Session = SessionUtils.newSession(cassandraResource, loader)) { // Intentionally use fully qualified table here to avoid warnings as these are not supported // by v3 protocol version, see JAVA-3068 PreparedStatement prepared = diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java index 9e4b62cd230..65d96596d7c 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java @@ -38,8 +38,10 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder; import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.core.metadata.token.Token; +import com.datastax.oss.driver.api.core.session.SessionBuilder; import com.datastax.oss.driver.api.core.type.codec.TypeCodecs; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; @@ -72,19 +74,19 @@ @Category(ParallelizableTests.class) public class BoundStatementCcmIT { - private CcmRule ccmRule = CcmRule.getInstance(); + private CassandraResourceRule cassandraResource = CassandraResourceRuleFactory.getInstance(); - private final boolean atLeastV4 = ccmRule.getHighestProtocolVersion().getCode() >= 4; + private final boolean atLeastV4 = cassandraResource.getHighestProtocolVersion().getCode() >= 4; private SessionRule sessionRule = - SessionRule.builder(ccmRule) + SessionRule.builder(cassandraResource) .withConfigLoader( SessionUtils.configLoaderBuilder() .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 20) .build()) .build(); - @Rule public TestRule chain = RuleChain.outerRule(ccmRule).around(sessionRule); + @Rule public TestRule chain = RuleChain.outerRule(cassandraResource).around(sessionRule); @Rule public TestName name = new TestName(); @@ -100,8 +102,13 @@ public void setupSchema() { sessionRule .session() .execute( - SimpleStatement.builder( - "CREATE TABLE IF NOT EXISTS test (k text, v int, PRIMARY KEY(k, v))") + SimpleStatement.builder("DROP TABLE IF EXISTS test") + .setExecutionProfile(sessionRule.slowProfile()) + .build()); + sessionRule + .session() + .execute( + SimpleStatement.builder("CREATE TABLE test (k text, v int, PRIMARY KEY(k, v))") .setExecutionProfile(sessionRule.slowProfile()) .build()); for (int i = 0; i < 100; i++) { @@ -117,17 +124,28 @@ public void setupSchema() { sessionRule .session() .execute( - SimpleStatement.builder( - "CREATE TABLE IF NOT EXISTS test2 (k text primary key, v0 int)") + SimpleStatement.builder("DROP TABLE IF EXISTS test2") + .setExecutionProfile(sessionRule.slowProfile()) + .build()); + sessionRule + .session() + .execute( + SimpleStatement.builder("CREATE TABLE test2 (k text primary key, v0 int)") .setExecutionProfile(sessionRule.slowProfile()) .build()); // table with composite partition key + sessionRule + .session() + .execute( + SimpleStatement.builder("DROP TABLE IF EXISTS test3") + .setExecutionProfile(sessionRule.slowProfile()) + .build()); sessionRule .session() .execute( SimpleStatement.builder( - "CREATE TABLE IF NOT EXISTS test3 " + "CREATE TABLE test3 " + "(pk1 int, pk2 int, v int, " + "PRIMARY KEY ((pk1, pk2)))") .setExecutionProfile(sessionRule.slowProfile()) @@ -141,7 +159,7 @@ public void should_not_allow_unset_value_when_protocol_less_than_v4() { SessionUtils.configLoaderBuilder() .withString(DefaultDriverOption.PROTOCOL_VERSION, "V3") .build(); - try (CqlSession v3Session = SessionUtils.newSession(ccmRule, loader)) { + try (CqlSession v3Session = SessionUtils.newSession(cassandraResource, loader)) { // Intentionally use fully qualified table here to avoid warnings as these are not supported // by v3 protocol version, see JAVA-3068 PreparedStatement prepared = @@ -160,7 +178,7 @@ public void should_not_allow_unset_value_when_protocol_less_than_v4() { @Test public void should_not_write_tombstone_if_value_is_implicitly_unset() { assumeThat(atLeastV4).as("unset values require protocol V4+").isTrue(); - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO test2 (k, v0) values (?, ?)"); session.execute(prepared.bind(name.getMethodName(), VALUE)); @@ -175,7 +193,7 @@ public void should_not_write_tombstone_if_value_is_implicitly_unset() { @Test public void should_write_tombstone_if_value_is_explicitly_unset() { assumeThat(atLeastV4).as("unset values require protocol V4+").isTrue(); - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO test2 (k, v0) values (?, ?)"); session.execute(prepared.bind(name.getMethodName(), VALUE)); @@ -194,7 +212,7 @@ public void should_write_tombstone_if_value_is_explicitly_unset() { @Test public void should_write_tombstone_if_value_is_explicitly_unset_on_builder() { assumeThat(atLeastV4).as("unset values require protocol V4+").isTrue(); - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO test2 (k, v0) values (?, ?)"); session.execute(prepared.bind(name.getMethodName(), VALUE)); @@ -213,7 +231,7 @@ public void should_write_tombstone_if_value_is_explicitly_unset_on_builder() { @Test public void should_have_empty_result_definitions_for_update_query() { - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO test2 (k, v0) values (?, ?)"); assertThat(prepared.getResultSetDefinitions()).hasSize(0); @@ -225,7 +243,7 @@ public void should_have_empty_result_definitions_for_update_query() { @Test public void should_bind_null_value_when_setting_values_in_bulk() { - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO test2 (k, v0) values (?, ?)"); BoundStatement boundStatement = prepared.bind(name.getMethodName(), null); assertThat(boundStatement.get(1, TypeCodecs.INT)).isNull(); @@ -255,7 +273,7 @@ public void should_allow_custom_codecs_when_setting_values_in_bulk() { @Test public void should_use_page_size_from_simple_statement() { - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { SimpleStatement st = SimpleStatement.builder("SELECT v FROM test").setPageSize(10).build(); PreparedStatement prepared = session.prepare(st); CompletionStage future = session.executeAsync(prepared.bind()); @@ -268,7 +286,7 @@ public void should_use_page_size_from_simple_statement() { @Test public void should_use_page_size() { - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { // set page size on simple statement, but will be unused since // overridden by bound statement. SimpleStatement st = SimpleStatement.builder("SELECT v FROM test").setPageSize(10).build(); @@ -363,7 +381,7 @@ public void should_propagate_attributes_when_preparing_a_simple_statement() { @Test @BackendRequirement(type = BackendType.CASSANDRA, minInclusive = "2.2") public void should_compute_routing_key_when_indices_randomly_distributed() { - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { PreparedStatement ps = session.prepare("INSERT INTO test3 (v, pk2, pk1) VALUES (?,?,?)"); @@ -434,12 +452,9 @@ private static void verifyUnset( @SuppressWarnings("unchecked") private CqlSession sessionWithCustomCodec(CqlIntToStringCodec codec) { - return (CqlSession) - SessionUtils.baseBuilder() - .addContactEndPoints(ccmRule.getContactPoints()) - .withKeyspace(sessionRule.keyspace()) - .addTypeCodecs(codec) - .build(); + SessionBuilder builder = + SessionUtils.baseBuilder(cassandraResource, sessionRule.keyspace()); + return (CqlSession) builder.addTypeCodecs(codec).build(); } private boolean supportsPerRequestKeyspace(CqlSession session) { diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java index 191dc040ffd..da1ff76336a 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java @@ -26,7 +26,8 @@ import com.datastax.oss.driver.api.core.cql.ResultSet; import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.cql.Statement; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; @@ -49,12 +50,14 @@ description = "Feature not available in DSE yet") public class NowInSecondsIT { - private static final CcmRule CCM_RULE = CcmRule.getInstance(); + private static final CassandraResourceRule CASSANDRA_RESOURCE = + CassandraResourceRuleFactory.getInstance(); - private static final SessionRule SESSION_RULE = SessionRule.builder(CCM_RULE).build(); + private static final SessionRule SESSION_RULE = + SessionRule.builder(CASSANDRA_RESOURCE).build(); @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); + public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); @Before public void setup() { diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java index 02078b683db..7712a915b11 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java @@ -29,7 +29,8 @@ import com.datastax.oss.driver.api.core.cql.ResultSet; import com.datastax.oss.driver.api.core.cql.Row; import com.datastax.oss.driver.api.core.cql.SimpleStatement; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; import com.datastax.oss.driver.shaded.guava.common.collect.Lists; @@ -52,20 +53,28 @@ @Category(ParallelizableTests.class) public class PagingIterableSpliteratorIT { - private static final CcmRule CCM_RULE = CcmRule.getInstance(); + private static final CassandraResourceRule CASSANDRA_RESOURCE = + CassandraResourceRuleFactory.getInstance(); - private static final SessionRule SESSION_RULE = SessionRule.builder(CCM_RULE).build(); + private static final SessionRule SESSION_RULE = + SessionRule.builder(CASSANDRA_RESOURCE).build(); @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); + public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); @BeforeClass public static void setupSchema() { + SESSION_RULE + .session() + .execute( + SimpleStatement.builder("DROP TABLE IF EXISTS test") + .setExecutionProfile(SESSION_RULE.slowProfile()) + .build()); SESSION_RULE .session() .execute( SimpleStatement.builder( - "CREATE TABLE IF NOT EXISTS test (k0 int, k1 int, v int, PRIMARY KEY(k0, k1))") + "CREATE TABLE test (k0 int, k1 int, v int, PRIMARY KEY(k0, k1))") .setExecutionProfile(SESSION_RULE.slowProfile()) .build()); PreparedStatement prepared = diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java index 6d33f35238a..8d062c8abbd 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java @@ -29,7 +29,8 @@ import com.datastax.oss.driver.api.core.type.codec.CodecNotFoundException; import com.datastax.oss.driver.api.core.type.codec.MappingCodec; import com.datastax.oss.driver.api.core.type.reflect.GenericType; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; @@ -47,11 +48,14 @@ @Category(ParallelizableTests.class) public class PagingStateIT { - private static final CcmRule CCM_RULE = CcmRule.getInstance(); + private static final CassandraResourceRule CASSANDRA_RESOURCE = + CassandraResourceRuleFactory.getInstance(); - private static final SessionRule SESSION_RULE = SessionRule.builder(CCM_RULE).build(); + private static final SessionRule SESSION_RULE = + SessionRule.builder(CASSANDRA_RESOURCE).build(); - @ClassRule public static TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); + @ClassRule + public static TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); @Before public void setupSchema() { @@ -59,8 +63,11 @@ public void setupSchema() { SchemaChangeSynchronizer.withLock( () -> { session.execute( - SimpleStatement.builder( - "CREATE TABLE IF NOT EXISTS foo (k int, cc int, v int, PRIMARY KEY(k, cc))") + SimpleStatement.builder("DROP TABLE IF EXISTS foo") + .setExecutionProfile(SESSION_RULE.slowProfile()) + .build()); + session.execute( + SimpleStatement.builder("CREATE TABLE foo (k int, cc int, v int, PRIMARY KEY(k, cc))") .setExecutionProfile(SESSION_RULE.slowProfile()) .build()); }); @@ -110,10 +117,8 @@ private void should_extract_and_reuse(UnaryOperator transformation) public void should_inject_in_simple_statement_with_custom_codecs() { try (CqlSession session = (CqlSession) - SessionUtils.baseBuilder() + SessionUtils.baseBuilder(CASSANDRA_RESOURCE, SESSION_RULE.keyspace()) .addTypeCodecs(new IntWrapperCodec()) - .addContactEndPoints(CCM_RULE.getContactPoints()) - .withKeyspace(SESSION_RULE.keyspace()) .build()) { SimpleStatement statement = diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java index 9eb883144db..8e451fb47b0 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java @@ -30,7 +30,8 @@ import com.datastax.oss.driver.api.core.cql.Row; import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.cql.Statement; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; @@ -58,11 +59,11 @@ @Category(ParallelizableTests.class) public class PerRequestKeyspaceIT { - private CcmRule ccmRule = CcmRule.getInstance(); + private CassandraResourceRule cassandraResource = CassandraResourceRuleFactory.getInstance(); - private SessionRule sessionRule = SessionRule.builder(ccmRule).build(); + private SessionRule sessionRule = SessionRule.builder(cassandraResource).build(); - @Rule public TestRule chain = RuleChain.outerRule(ccmRule).around(sessionRule); + @Rule public TestRule chain = RuleChain.outerRule(cassandraResource).around(sessionRule); @Rule public TestName nameRule = new TestName(); @@ -118,7 +119,7 @@ private void should_reject_statement_with_keyspace_in_protocol_v4(Statement stat SessionUtils.configLoaderBuilder() .withString(DefaultDriverOption.PROTOCOL_VERSION, "V4") .build(); - try (CqlSession session = SessionUtils.newSession(ccmRule, loader)) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, loader)) { Throwable t = catchThrowable(() -> session.execute(statement)); assertThat(t) .isInstanceOf(IllegalArgumentException.class) @@ -226,7 +227,7 @@ public void should_reprepare_statement_with_keyspace_on_the_fly() { // Create a separate session because we don't want it to have a default keyspace SchemaChangeSynchronizer.withLock( () -> { - try (CqlSession session = SessionUtils.newSession(ccmRule)) { + try (CqlSession session = SessionUtils.newSession(cassandraResource)) { executeDdl( session, String.format( diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java index 5671a7684e5..b5f16b1b05b 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java @@ -36,7 +36,8 @@ import com.datastax.oss.driver.api.core.metrics.DefaultSessionMetric; import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; import com.datastax.oss.driver.api.core.type.DataTypes; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; @@ -70,10 +71,10 @@ @Category(ParallelizableTests.class) public class PreparedStatementIT { - private CcmRule ccmRule = CcmRule.getInstance(); + private CassandraResourceRule cassandraResource = CassandraResourceRuleFactory.getInstance(); private SessionRule sessionRule = - SessionRule.builder(ccmRule) + SessionRule.builder(cassandraResource) .withConfigLoader( SessionUtils.configLoaderBuilder() .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 2) @@ -81,7 +82,7 @@ public class PreparedStatementIT { .build()) .build(); - @Rule public TestRule chain = RuleChain.outerRule(ccmRule).around(sessionRule); + @Rule public TestRule chain = RuleChain.outerRule(cassandraResource).around(sessionRule); @Before public void setupSchema() { @@ -106,7 +107,7 @@ private void executeDdl(String query) { @Test public void should_have_empty_result_definitions_for_insert_query_without_bound_variable() { - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO prepared_statement_test (a, b, c) VALUES (1, 1, 1)"); assertThat(prepared.getVariableDefinitions()).isEmpty(); @@ -117,7 +118,7 @@ public void should_have_empty_result_definitions_for_insert_query_without_bound_ @Test public void should_have_non_empty_result_definitions_for_insert_query_with_bound_variable() { - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO prepared_statement_test (a, b, c) VALUES (?, ?, ?)"); assertThat(prepared.getVariableDefinitions()).hasSize(3); @@ -128,7 +129,7 @@ public void should_have_non_empty_result_definitions_for_insert_query_with_bound @Test public void should_have_empty_variable_definitions_for_select_query_without_bound_variable() { - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("SELECT a,b,c FROM prepared_statement_test WHERE a = 1"); assertThat(prepared.getVariableDefinitions()).isEmpty(); @@ -139,7 +140,7 @@ public void should_have_empty_variable_definitions_for_select_query_without_boun @Test public void should_have_non_empty_variable_definitions_for_select_query_with_bound_variable() { - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("SELECT a,b,c FROM prepared_statement_test WHERE a = ?"); assertThat(prepared.getVariableDefinitions()).hasSize(1); @@ -227,7 +228,7 @@ public void should_update_metadata_when_schema_changed_across_pages() { public void should_update_metadata_when_schema_changed_across_sessions() { // Given CqlSession session1 = sessionRule.session(); - CqlSession session2 = SessionUtils.newSession(ccmRule, sessionRule.keyspace()); + CqlSession session2 = SessionUtils.newSession(cassandraResource, sessionRule.keyspace()); PreparedStatement ps1 = session1.prepare("SELECT * FROM prepared_statement_test WHERE a = ?"); PreparedStatement ps2 = session2.prepare("SELECT * FROM prepared_statement_test WHERE a = ?"); @@ -302,7 +303,8 @@ public void should_not_store_metadata_for_conditional_updates_in_legacy_protocol .withString(DefaultDriverOption.PROTOCOL_VERSION, "V4") .withDuration(DefaultDriverOption.REQUEST_TIMEOUT, Duration.ofSeconds(30)) .build(); - try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace(), loader)) { + try (CqlSession session = + SessionUtils.newSession(cassandraResource, sessionRule.keyspace(), loader)) { should_not_store_metadata_for_conditional_updates(session); } } @@ -448,7 +450,7 @@ public void should_create_separate_instances_for_different_statement_parameters( * @see CASSANDRA-15252 */ private AbstractThrowableAssert assertableReprepareAfterIdChange() { - try (CqlSession session = SessionUtils.newSession(ccmRule)) { + try (CqlSession session = SessionUtils.newSession(cassandraResource)) { PreparedStatement preparedStatement = session.prepare( String.format( @@ -552,7 +554,7 @@ private static Iterable firstPageOf(CompletionStage stage) private CqlSession sessionWithCacheSizeMetric() { return SessionUtils.newSession( - ccmRule, + cassandraResource, sessionRule.keyspace(), SessionUtils.configLoaderBuilder() .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 2) diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java index 37a600efbc4..c1cdb3829bc 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java @@ -27,7 +27,8 @@ import com.datastax.oss.driver.api.core.cql.Row; import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.metadata.EndPoint; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; @@ -42,12 +43,14 @@ @Category(ParallelizableTests.class) public class QueryTraceIT { - private static final CcmRule CCM_RULE = CcmRule.getInstance(); + private static final CassandraResourceRule CASSANDRA_RESOURCE = + CassandraResourceRuleFactory.getInstance(); - private static final SessionRule SESSION_RULE = SessionRule.builder(CCM_RULE).build(); + private static final SessionRule SESSION_RULE = + SessionRule.builder(CASSANDRA_RESOURCE).build(); @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); + public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); @Test public void should_not_have_tracing_id_when_tracing_disabled() { @@ -79,11 +82,11 @@ public void should_fetch_trace_when_tracing_enabled() { assertThat(executionInfo.getTracingId()).isNotNull(); - EndPoint contactPoint = CCM_RULE.getContactPoints().iterator().next(); + EndPoint contactPoint = CASSANDRA_RESOURCE.getContactPoints().iterator().next(); InetAddress nodeAddress = ((InetSocketAddress) contactPoint.resolve()).getAddress(); boolean expectPorts = - CCM_RULE.getCassandraVersion().nextStable().compareTo(Version.V4_0_0) >= 0 - && !CCM_RULE.isDistributionOf(BackendType.DSE); + CASSANDRA_RESOURCE.getCassandraVersion().nextStable().compareTo(Version.V4_0_0) >= 0 + && !CASSANDRA_RESOURCE.isDistributionOf(BackendType.DSE); QueryTrace queryTrace = executionInfo.getQueryTrace(); assertThat(queryTrace.getTracingId()).isEqualTo(executionInfo.getTracingId()); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementAstraIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementAstraIT.java deleted file mode 100644 index 09ba8394893..00000000000 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementAstraIT.java +++ /dev/null @@ -1,372 +0,0 @@ -/* - * 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 com.datastax.oss.driver.core.cql; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.datastax.oss.driver.api.core.CqlIdentifier; -import com.datastax.oss.driver.api.core.CqlSession; -import com.datastax.oss.driver.api.core.config.DefaultDriverOption; -import com.datastax.oss.driver.api.core.cql.AsyncResultSet; -import com.datastax.oss.driver.api.core.cql.ResultSet; -import com.datastax.oss.driver.api.core.cql.Row; -import com.datastax.oss.driver.api.core.cql.SimpleStatement; -import com.datastax.oss.driver.api.core.cql.Statement; -import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; -import com.datastax.oss.driver.api.testinfra.astra.AstraRule; -import com.datastax.oss.driver.api.testinfra.session.SessionRule; -import com.datastax.oss.driver.api.testinfra.session.SessionUtils; -import com.datastax.oss.driver.categories.ParallelizableTests; -import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; -import java.util.List; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; -import org.junit.rules.TestName; -import org.junit.rules.TestRule; - -@Category(ParallelizableTests.class) -public class SimpleStatementAstraIT { - - private static final AstraRule ASTRA_RULE = AstraRule.getInstance(); - - private static final SessionRule SESSION_RULE = - SessionRule.builder(ASTRA_RULE) - .withConfigLoader( - SessionUtils.configLoaderBuilder() - .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 20) - .build()) - .build(); - - @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(ASTRA_RULE).around(SESSION_RULE); - - @Rule public TestName name = new TestName(); - - private static final String KEY = "test"; - - @BeforeClass - public static void setupSchema() { - // table where every column forms the primary key. - SESSION_RULE - .session() - .execute( - SimpleStatement.builder( - "CREATE TABLE IF NOT EXISTS test (k text, v int, PRIMARY KEY(k, v))") - .setExecutionProfile(SESSION_RULE.slowProfile()) - .build()); - for (int i = 0; i < 100; i++) { - SESSION_RULE - .session() - .execute( - SimpleStatement.builder("INSERT INTO test (k, v) VALUES (?, ?)") - .addPositionalValues(KEY, i) - .build()); - } - - // table with simple primary key, single cell. - SESSION_RULE - .session() - .execute( - SimpleStatement.builder("CREATE TABLE IF NOT EXISTS test2 (k text primary key, v int)") - .setExecutionProfile(SESSION_RULE.slowProfile()) - .build()); - } - - @Test - public void should_use_paging_state_when_copied() { - Statement st = - SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); - ResultSet result = SESSION_RULE.session().execute(st); - - // given a query created from a copy of a previous query with paging state from previous queries - // response. - st = st.copy(result.getExecutionInfo().getPagingState()); - - // when executing that query. - result = SESSION_RULE.session().execute(st); - - // then the response should start on the page boundary. - assertThat(result.iterator().next().getInt("v")).isEqualTo(20); - } - - @Test - public void should_use_paging_state_when_provided_to_new_statement() { - Statement st = - SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); - ResultSet result = SESSION_RULE.session().execute(st); - - // given a query created from a copy of a previous query with paging state from previous queries - // response. - st = - SimpleStatement.builder(String.format("SELECT v FROM test where k='%s'", KEY)) - .setPagingState(result.getExecutionInfo().getPagingState()) - .build(); - - // when executing that query. - result = SESSION_RULE.session().execute(st); - - // then the response should start on the page boundary. - assertThat(result.iterator().next().getInt("v")).isEqualTo(20); - } - - @Test - @Ignore - public void should_fail_if_using_paging_state_from_different_query() { - Statement st = - SimpleStatement.builder("SELECT v FROM test WHERE k=:k").addNamedValue("k", KEY).build(); - ResultSet result = SESSION_RULE.session().execute(st); - - // TODO Expect PagingStateException - - // given a new different query and providing the paging state from the previous query - // then an exception should be thrown indicating incompatible paging state - SimpleStatement.builder("SELECT v FROM test") - .setPagingState(result.getExecutionInfo().getPagingState()) - .build(); - } - - @Test - public void should_use_timestamp_when_set() { - // given inserting data with a timestamp 40 days in the past. - long timestamp = System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(40, TimeUnit.DAYS); - SimpleStatement insert = - SimpleStatement.builder("INSERT INTO test2 (k, v) values (?, ?)") - .addPositionalValues(name.getMethodName(), 0) - .setQueryTimestamp(timestamp) - .build(); - - SESSION_RULE.session().execute(insert); - - // when retrieving writetime of cell from that insert. - SimpleStatement select = - SimpleStatement.builder("SELECT writetime(v) as wv from test2 where k = ?") - .addPositionalValue(name.getMethodName()) - .build(); - - ResultSet result = SESSION_RULE.session().execute(select); - List rows = result.all(); - assertThat(rows).hasSize(1); - - // then the writetime should equal the timestamp provided. - Row row = rows.iterator().next(); - assertThat(row.getLong("wv")).isEqualTo(timestamp); - } - - @Test - @Ignore - public void should_use_tracing_when_set() { - // TODO currently there's no way to validate tracing was set since trace id is not set - // also write test to verify it is not set. - SESSION_RULE - .session() - .execute(SimpleStatement.builder("select * from test").setTracing().build()); - } - - @Test - public void should_use_positional_values() { - // given a statement with positional values - SimpleStatement insert = - SimpleStatement.builder("INSERT into test2 (k, v) values (?, ?)") - .addPositionalValue(name.getMethodName()) - .addPositionalValue(4) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then we should be able to retrieve the data as inserted. - SimpleStatement select = - SimpleStatement.builder("select k,v from test2 where k=?") - .addPositionalValue(name.getMethodName()) - .build(); - - ResultSet result = SESSION_RULE.session().execute(select); - List rows = result.all(); - assertThat(rows).hasSize(1); - - Row row = rows.iterator().next(); - assertThat(row.getString("k")).isEqualTo(name.getMethodName()); - assertThat(row.getInt("v")).isEqualTo(4); - } - - @Test - public void should_allow_nulls_in_positional_values() { - // given a statement with positional values - SimpleStatement insert = - SimpleStatement.builder("INSERT into test2 (k, v) values (?, ?)") - .addPositionalValue(name.getMethodName()) - .addPositionalValue(null) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then we should be able to retrieve the data as inserted. - SimpleStatement select = - SimpleStatement.builder("select k,v from test2 where k=?") - .addPositionalValue(name.getMethodName()) - .build(); - - ResultSet result = SESSION_RULE.session().execute(select); - List rows = result.all(); - assertThat(rows).hasSize(1); - - Row row = rows.iterator().next(); - assertThat(row.getString("k")).isEqualTo(name.getMethodName()); - assertThat(row.getObject("v")).isNull(); - } - - @Test(expected = InvalidQueryException.class) - public void should_fail_when_too_many_positional_values_provided() { - // given a statement with more bound values than anticipated (3 given vs. 2 expected) - SimpleStatement insert = - SimpleStatement.builder("INSERT into test (k, v) values (?, ?)") - .addPositionalValues(KEY, 0, 7) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then the server will throw an InvalidQueryException which is thrown up to the client. - } - - @Test(expected = InvalidQueryException.class) - public void should_fail_when_not_enough_positional_values_provided() { - // given a statement with not enough bound values (1 given vs. 2 expected) - SimpleStatement insert = - SimpleStatement.builder("SELECT * from test where k = ? and v = ?") - .addPositionalValue(KEY) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then the server will throw an InvalidQueryException which is thrown up to the client. - } - - @Test - public void should_use_named_values() { - // given a statement with named values - SimpleStatement insert = - SimpleStatement.builder("INSERT into test2 (k, v) values (:k, :v)") - .addNamedValue("k", name.getMethodName()) - .addNamedValue("v", 7) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then we should be able to retrieve the data as inserted. - SimpleStatement select = - SimpleStatement.builder("select k,v from test2 where k=:k") - .addNamedValue("k", name.getMethodName()) - .build(); - - ResultSet result = SESSION_RULE.session().execute(select); - List rows = result.all(); - assertThat(rows).hasSize(1); - - Row row = rows.iterator().next(); - assertThat(row.getString("k")).isEqualTo(name.getMethodName()); - assertThat(row.getInt("v")).isEqualTo(7); - } - - @Test - public void should_allow_nulls_in_named_values() { - // given a statement with named values - SimpleStatement insert = - SimpleStatement.builder("INSERT into test2 (k, v) values (:k, :v)") - .addNamedValue("k", name.getMethodName()) - .addNamedValue("v", null) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then we should be able to retrieve the data as inserted. - SimpleStatement select = - SimpleStatement.builder("select k,v from test2 where k=:k") - .addNamedValue("k", name.getMethodName()) - .build(); - - ResultSet result = SESSION_RULE.session().execute(select); - List rows = result.all(); - assertThat(rows).hasSize(1); - - Row row = rows.iterator().next(); - assertThat(row.getString("k")).isEqualTo(name.getMethodName()); - assertThat(row.getObject("v")).isNull(); - } - - @Test(expected = InvalidQueryException.class) - public void should_fail_when_named_value_missing() { - // given a statement with a missing named value (:k) - SimpleStatement insert = - SimpleStatement.builder("SELECT * from test where k = :k and v = :v") - .addNamedValue("v", 0) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then the server will throw an InvalidQueryException which is thrown up to the client. - } - - @Test(expected = IllegalArgumentException.class) - public void should_fail_when_mixing_named_and_positional_values() { - SimpleStatement.builder("SELECT * from test where k = :k and v = :v") - .addNamedValue("k", KEY) - .addPositionalValue(0) - .build(); - } - - @Test(expected = IllegalArgumentException.class) - public void should_fail_when_mixing_positional_and_named_values() { - SimpleStatement.builder("SELECT * from test where k = :k and v = :v") - .addPositionalValue(0) - .addNamedValue("k", KEY) - .build(); - } - - @Test - public void should_use_positional_value_with_case_sensitive_id() { - SimpleStatement statement = - SimpleStatement.builder("SELECT count(*) FROM test2 WHERE k=:\"theKey\"") - .addNamedValue(CqlIdentifier.fromCql("\"theKey\""), 0) - .build(); - Row row = SESSION_RULE.session().execute(statement).one(); - assertThat(row.getLong(0)).isEqualTo(0); - } - - @Test - public void should_use_page_size() { - Statement st = SimpleStatement.builder("SELECT v FROM test").setPageSize(10).build(); - CompletionStage future = SESSION_RULE.session().executeAsync(st); - AsyncResultSet result = CompletableFutures.getUninterruptibly(future); - - // Should have only fetched 10 (page size) rows. - assertThat(result.remaining()).isEqualTo(10); - } -} diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java index b903f59efcc..409b8c91c84 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java @@ -28,7 +28,8 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; import com.datastax.oss.driver.categories.ParallelizableTests; @@ -49,10 +50,11 @@ @Category(ParallelizableTests.class) public class SimpleStatementCcmIT { - private static final CcmRule CCM_RULE = CcmRule.getInstance(); + private static final CassandraResourceRule CASSANDRA_RESOURCE = + CassandraResourceRuleFactory.getInstance(); private static final SessionRule SESSION_RULE = - SessionRule.builder(CCM_RULE) + SessionRule.builder(CASSANDRA_RESOURCE) .withConfigLoader( SessionUtils.configLoaderBuilder() .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 20) @@ -60,7 +62,7 @@ public class SimpleStatementCcmIT { .build(); @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); + public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); @Rule public TestName name = new TestName(); @@ -72,8 +74,13 @@ public static void setupSchema() { SESSION_RULE .session() .execute( - SimpleStatement.builder( - "CREATE TABLE IF NOT EXISTS test (k text, v int, PRIMARY KEY(k, v))") + SimpleStatement.builder("DROP TABLE IF EXISTS test") + .setExecutionProfile(SESSION_RULE.slowProfile()) + .build()); + SESSION_RULE + .session() + .execute( + SimpleStatement.builder("CREATE TABLE test (k text, v int, PRIMARY KEY(k, v))") .setExecutionProfile(SESSION_RULE.slowProfile()) .build()); for (int i = 0; i < 100; i++) { @@ -89,7 +96,13 @@ public static void setupSchema() { SESSION_RULE .session() .execute( - SimpleStatement.builder("CREATE TABLE IF NOT EXISTS test2 (k text primary key, v int)") + SimpleStatement.builder("DROP TABLE IF EXISTS test2") + .setExecutionProfile(SESSION_RULE.slowProfile()) + .build()); + SESSION_RULE + .session() + .execute( + SimpleStatement.builder("CREATE TABLE test2 (k text primary key, v int)") .setExecutionProfile(SESSION_RULE.slowProfile()) .build()); } diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementIT.java deleted file mode 100644 index b7bb0875586..00000000000 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementIT.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * 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 com.datastax.oss.driver.core.cql; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.datastax.oss.driver.api.core.CqlIdentifier; -import com.datastax.oss.driver.api.core.CqlSession; -import com.datastax.oss.driver.api.core.config.DefaultDriverOption; -import com.datastax.oss.driver.api.core.cql.AsyncResultSet; -import com.datastax.oss.driver.api.core.cql.ResultSet; -import com.datastax.oss.driver.api.core.cql.Row; -import com.datastax.oss.driver.api.core.cql.SimpleStatement; -import com.datastax.oss.driver.api.core.cql.Statement; -import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; -import com.datastax.oss.driver.api.testinfra.session.SessionRule; -import com.datastax.oss.driver.api.testinfra.session.SessionUtils; -import com.datastax.oss.driver.categories.ParallelizableTests; -import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; -import java.util.List; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; -import org.junit.rules.TestName; -import org.junit.rules.TestRule; - -/** - * Unified test for SimpleStatement that can run against either Cassandra (via CCM) or Astra. - * - *

The backend is selected via the {@code ccm.distribution} system property, which is handled by - * {@link CassandraResourceRuleFactory}. - * - *

To run against Cassandra OSS (default): - * - *

- * mvn test -Dtest=SimpleStatementIT
- * 
- * - *

To run against Astra: - * - *

- * mvn test -Dtest=SimpleStatementIT -Dastra.token="..." -Dccm.distribution=ASTRA
- * 
- */ -@Category(ParallelizableTests.class) -public class SimpleStatementIT { - - private static final CassandraResourceRule CASSANDRA_RESOURCE = - CassandraResourceRuleFactory.getInstance(); - - private static final SessionRule SESSION_RULE = - SessionRule.builder(CASSANDRA_RESOURCE) - .withConfigLoader( - SessionUtils.configLoaderBuilder() - .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 20) - .build()) - .build(); - - @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); - - @Rule public TestName name = new TestName(); - - private static final String KEY = "test"; - - @BeforeClass - public static void setupSchema() { - // table where every column forms the primary key. - SESSION_RULE - .session() - .execute( - SimpleStatement.builder( - "CREATE TABLE IF NOT EXISTS test (k text, v int, PRIMARY KEY(k, v))") - .setExecutionProfile(SESSION_RULE.slowProfile()) - .build()); - for (int i = 0; i < 100; i++) { - SESSION_RULE - .session() - .execute( - SimpleStatement.builder("INSERT INTO test (k, v) VALUES (?, ?)") - .addPositionalValues(KEY, i) - .build()); - } - - // table with simple primary key, single cell. - SESSION_RULE - .session() - .execute( - SimpleStatement.builder("CREATE TABLE IF NOT EXISTS test2 (k text primary key, v int)") - .setExecutionProfile(SESSION_RULE.slowProfile()) - .build()); - } - - @Test - public void should_use_paging_state_when_copied() { - Statement st = - SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); - ResultSet result = SESSION_RULE.session().execute(st); - - // given a query created from a copy of a previous query with paging state from previous queries - // response. - st = st.copy(result.getExecutionInfo().getPagingState()); - - // when executing that query. - result = SESSION_RULE.session().execute(st); - - // then the response should start on the page boundary. - assertThat(result.iterator().next().getInt("v")).isEqualTo(20); - } - - @Test - public void should_use_paging_state_when_provided_to_new_statement() { - Statement st = - SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); - ResultSet result = SESSION_RULE.session().execute(st); - - // given a query created from a copy of a previous query with paging state from previous queries - // response. - st = - SimpleStatement.builder(String.format("SELECT v FROM test where k='%s'", KEY)) - .setPagingState(result.getExecutionInfo().getPagingState()) - .build(); - - // when executing that query. - result = SESSION_RULE.session().execute(st); - - // then the response should start on the page boundary. - assertThat(result.iterator().next().getInt("v")).isEqualTo(20); - } - - @Test - @Ignore - public void should_fail_if_using_paging_state_from_different_query() { - Statement st = - SimpleStatement.builder("SELECT v FROM test WHERE k=:k").addNamedValue("k", KEY).build(); - ResultSet result = SESSION_RULE.session().execute(st); - - // TODO Expect PagingStateException - - // given a new different query and providing the paging state from the previous query - // then an exception should be thrown indicating incompatible paging state - SimpleStatement.builder("SELECT v FROM test") - .setPagingState(result.getExecutionInfo().getPagingState()) - .build(); - } - - @Test - public void should_use_timestamp_when_set() { - // given inserting data with a timestamp 40 days in the past. - long timestamp = System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(40, TimeUnit.DAYS); - SimpleStatement insert = - SimpleStatement.builder("INSERT INTO test2 (k, v) values (?, ?)") - .addPositionalValues(name.getMethodName(), 0) - .setQueryTimestamp(timestamp) - .build(); - - SESSION_RULE.session().execute(insert); - - // when retrieving writetime of cell from that insert. - SimpleStatement select = - SimpleStatement.builder("SELECT writetime(v) as wv from test2 where k = ?") - .addPositionalValue(name.getMethodName()) - .build(); - - ResultSet result = SESSION_RULE.session().execute(select); - List rows = result.all(); - assertThat(rows).hasSize(1); - - // then the writetime should equal the timestamp provided. - Row row = rows.iterator().next(); - assertThat(row.getLong("wv")).isEqualTo(timestamp); - } - - @Test - @Ignore - public void should_use_tracing_when_set() { - // TODO currently there's no way to validate tracing was set since trace id is not set - // also write test to verify it is not set. - SESSION_RULE - .session() - .execute(SimpleStatement.builder("select * from test").setTracing().build()); - } - - @Test - public void should_execute_query() { - Statement statement = - SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); - - ResultSet result = SESSION_RULE.session().execute(statement); - - List rows = result.all(); - assertThat(rows).hasSize(100); - } - - @Test - public void should_execute_query_async() throws Exception { - Statement statement = - SimpleStatement.builder(String.format("SELECT v FROM test WHERE k='%s'", KEY)).build(); - - CompletionStage future = SESSION_RULE.session().executeAsync(statement); - - AsyncResultSet result = CompletableFutures.getUninterruptibly(future); - - assertThat(result.remaining()).isEqualTo(20); - } - - @Test - public void should_use_positional_values() { - // given a statement with positional values - SimpleStatement insert = - SimpleStatement.builder("INSERT into test2 (k, v) values (?, ?)") - .addPositionalValue(name.getMethodName()) - .addPositionalValue(4) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then we should be able to retrieve the data as inserted. - SimpleStatement select = - SimpleStatement.builder("select k,v from test2 where k=?") - .addPositionalValue(name.getMethodName()) - .build(); - - ResultSet result = SESSION_RULE.session().execute(select); - List rows = result.all(); - assertThat(rows).hasSize(1); - - Row row = rows.iterator().next(); - assertThat(row.getString("k")).isEqualTo(name.getMethodName()); - assertThat(row.getInt("v")).isEqualTo(4); - } - - @Test - public void should_allow_nulls_in_positional_values() { - // given a statement with positional values - SimpleStatement insert = - SimpleStatement.builder("INSERT into test2 (k, v) values (?, ?)") - .addPositionalValue(name.getMethodName()) - .addPositionalValue(null) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then we should be able to retrieve the data as inserted. - SimpleStatement select = - SimpleStatement.builder("select k,v from test2 where k=?") - .addPositionalValue(name.getMethodName()) - .build(); - - ResultSet result = SESSION_RULE.session().execute(select); - List rows = result.all(); - assertThat(rows).hasSize(1); - - Row row = rows.iterator().next(); - assertThat(row.getString("k")).isEqualTo(name.getMethodName()); - assertThat(row.getObject("v")).isNull(); - } - - @Test(expected = InvalidQueryException.class) - public void should_fail_when_too_many_positional_values_provided() { - // given a statement with more bound values than anticipated (3 given vs. 2 expected) - SimpleStatement insert = - SimpleStatement.builder("INSERT into test (k, v) values (?, ?)") - .addPositionalValues(KEY, 0, 7) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then the server will throw an InvalidQueryException which is thrown up to the client. - } - - @Test(expected = InvalidQueryException.class) - public void should_fail_when_not_enough_positional_values_provided() { - // given a statement with not enough bound values (1 given vs. 2 expected) - SimpleStatement insert = - SimpleStatement.builder("SELECT * from test where k = ? and v = ?") - .addPositionalValue(KEY) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then the server will throw an InvalidQueryException which is thrown up to the client. - } - - @Test - public void should_use_named_values() { - // given a statement with named values - SimpleStatement insert = - SimpleStatement.builder("INSERT into test2 (k, v) values (:k, :v)") - .addNamedValue("k", name.getMethodName()) - .addNamedValue("v", 7) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then we should be able to retrieve the data as inserted. - SimpleStatement select = - SimpleStatement.builder("select k,v from test2 where k=:k") - .addNamedValue("k", name.getMethodName()) - .build(); - - ResultSet result = SESSION_RULE.session().execute(select); - List rows = result.all(); - assertThat(rows).hasSize(1); - - Row row = rows.iterator().next(); - assertThat(row.getString("k")).isEqualTo(name.getMethodName()); - assertThat(row.getInt("v")).isEqualTo(7); - } - - @Test - public void should_allow_nulls_in_named_values() { - // given a statement with named values - SimpleStatement insert = - SimpleStatement.builder("INSERT into test2 (k, v) values (:k, :v)") - .addNamedValue("k", name.getMethodName()) - .addNamedValue("v", null) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then we should be able to retrieve the data as inserted. - SimpleStatement select = - SimpleStatement.builder("select k,v from test2 where k=:k") - .addNamedValue("k", name.getMethodName()) - .build(); - - ResultSet result = SESSION_RULE.session().execute(select); - List rows = result.all(); - assertThat(rows).hasSize(1); - - Row row = rows.iterator().next(); - assertThat(row.getString("k")).isEqualTo(name.getMethodName()); - assertThat(row.getObject("v")).isNull(); - } - - @Test(expected = InvalidQueryException.class) - public void should_fail_when_named_value_missing() { - // given a statement with a missing named value (:k) - SimpleStatement insert = - SimpleStatement.builder("SELECT * from test where k = :k and v = :v") - .addNamedValue("v", 0) - .build(); - - // when executing that statement - SESSION_RULE.session().execute(insert); - - // then the server will throw an InvalidQueryException which is thrown up to the client. - } - - @Test(expected = IllegalArgumentException.class) - public void should_fail_when_mixing_named_and_positional_values() { - SimpleStatement.builder("SELECT * from test where k = :k and v = :v") - .addNamedValue("k", KEY) - .addPositionalValue(0) - .build(); - } - - @Test(expected = IllegalArgumentException.class) - public void should_fail_when_mixing_positional_and_named_values() { - SimpleStatement.builder("SELECT * from test where k = :k and v = :v") - .addPositionalValue(0) - .addNamedValue("k", KEY) - .build(); - } - - @Test - public void should_use_positional_value_with_case_sensitive_id() { - SimpleStatement statement = - SimpleStatement.builder("SELECT count(*) FROM test2 WHERE k=:\"theKey\"") - .addNamedValue(CqlIdentifier.fromCql("\"theKey\""), 0) - .build(); - Row row = SESSION_RULE.session().execute(statement).one(); - assertThat(row.getLong(0)).isEqualTo(0); - } - - @Test - public void should_use_page_size() { - Statement st = SimpleStatement.builder("SELECT v FROM test").setPageSize(10).build(); - CompletionStage future = SESSION_RULE.session().executeAsync(st); - AsyncResultSet result = CompletableFutures.getUninterruptibly(future); - - // Should have only fetched 10 (page size) rows. - assertThat(result.remaining()).isEqualTo(10); - } -} diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java index c00cf064e51..61f7ca6e468 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java @@ -31,7 +31,8 @@ import com.datastax.oss.driver.api.core.cql.ExecutionInfo; import com.datastax.oss.driver.api.core.cql.PreparedStatement; import com.datastax.oss.driver.api.core.cql.SimpleStatement; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; @@ -56,11 +57,14 @@ @Category(ParallelizableTests.class) public class DefaultReactiveResultSetIT { - private static CcmRule ccmRule = CcmRule.getInstance(); + private static CassandraResourceRule cassandraResource = + CassandraResourceRuleFactory.getInstance(); - private static SessionRule sessionRule = SessionRule.builder(ccmRule).build(); + private static SessionRule sessionRule = + SessionRule.builder(cassandraResource).build(); - @ClassRule public static TestRule chain = RuleChain.outerRule(ccmRule).around(sessionRule); + @ClassRule + public static TestRule chain = RuleChain.outerRule(cassandraResource).around(sessionRule); @BeforeClass public static void initialize() { diff --git a/test-infra/revapi.json b/test-infra/revapi.json index a16d06d75da..e2944de87bd 100644 --- a/test-infra/revapi.json +++ b/test-infra/revapi.json @@ -17,6 +17,200 @@ } }, "ignore": [ + { + "code": "java.method.returnTypeTypeParametersChanged", + "old": "method java.util.Set com.datastax.oss.driver.api.testinfra.CassandraResourceRule::getContactPoints()", + "new": "method java.util.Set com.datastax.oss.driver.api.testinfra.CassandraResourceRule::getContactPoints()", + "justification": "JAVA-2165: Abstract node connection information" + }, + { + "code": "java.method.numberOfParametersChanged", + "old": "method void com.datastax.oss.driver.api.testinfra.loadbalancing.SortingLoadBalancingPolicy::init(java.util.Map, com.datastax.oss.driver.api.core.loadbalancing.LoadBalancingPolicy.DistanceReporter, java.util.Set)", + "new": "method void com.datastax.oss.driver.api.testinfra.loadbalancing.SortingLoadBalancingPolicy::init(java.util.Map, com.datastax.oss.driver.api.core.loadbalancing.LoadBalancingPolicy.DistanceReporter)", + "justification": "JAVA-2165: Abstract node connection information" + }, + { + "code": "java.method.returnTypeTypeParametersChanged", + "old": "method java.util.Set com.datastax.oss.driver.api.testinfra.simulacron.SimulacronRule::getContactPoints()", + "new": "method java.util.Set com.datastax.oss.driver.api.testinfra.simulacron.SimulacronRule::getContactPoints()", + "justification": "JAVA-2165: Abstract node connection information" + }, + { + "code": "java.method.returnTypeChanged", + "old": "method com.datastax.oss.driver.internal.core.config.typesafe.DefaultDriverConfigLoaderBuilder com.datastax.oss.driver.api.testinfra.session.SessionUtils::configLoaderBuilder()", + "new": "method com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder com.datastax.oss.driver.api.testinfra.session.SessionUtils::configLoaderBuilder()", + "justification": "JAVA-2201: Expose a public API for programmatic config" + }, + { + "code": "java.annotation.removed", + "old": "parameter java.util.Queue com.datastax.oss.driver.api.testinfra.loadbalancing.SortingLoadBalancingPolicy::newQueryPlan(===com.datastax.oss.driver.api.core.session.Request===, com.datastax.oss.driver.api.core.session.Session)", + "new": "parameter java.util.Queue com.datastax.oss.driver.api.testinfra.loadbalancing.SortingLoadBalancingPolicy::newQueryPlan(===com.datastax.oss.driver.api.core.session.Request===, com.datastax.oss.driver.api.core.session.Session)", + "annotation": "@edu.umd.cs.findbugs.annotations.NonNull", + "justification": "Method arguments were mistakenly annotated with @NonNull" + }, + { + "code": "java.annotation.added", + "old": "parameter java.util.Queue com.datastax.oss.driver.api.testinfra.loadbalancing.SortingLoadBalancingPolicy::newQueryPlan(===com.datastax.oss.driver.api.core.session.Request===, com.datastax.oss.driver.api.core.session.Session)", + "new": "parameter java.util.Queue com.datastax.oss.driver.api.testinfra.loadbalancing.SortingLoadBalancingPolicy::newQueryPlan(===com.datastax.oss.driver.api.core.session.Request===, com.datastax.oss.driver.api.core.session.Session)", + "annotation": "@edu.umd.cs.findbugs.annotations.Nullable", + "justification": "Method arguments were mistakenly annotated with @NonNull" + }, + { + "code": "java.annotation.removed", + "old": "parameter java.util.Queue com.datastax.oss.driver.api.testinfra.loadbalancing.SortingLoadBalancingPolicy::newQueryPlan(com.datastax.oss.driver.api.core.session.Request, ===com.datastax.oss.driver.api.core.session.Session===)", + "new": "parameter java.util.Queue com.datastax.oss.driver.api.testinfra.loadbalancing.SortingLoadBalancingPolicy::newQueryPlan(com.datastax.oss.driver.api.core.session.Request, ===com.datastax.oss.driver.api.core.session.Session===)", + "annotation": "@edu.umd.cs.findbugs.annotations.NonNull", + "justification": "Method arguments were mistakenly annotated with @NonNull" + }, + { + "code": "java.annotation.added", + "old": "parameter java.util.Queue com.datastax.oss.driver.api.testinfra.loadbalancing.SortingLoadBalancingPolicy::newQueryPlan(com.datastax.oss.driver.api.core.session.Request, ===com.datastax.oss.driver.api.core.session.Session===)", + "new": "parameter java.util.Queue com.datastax.oss.driver.api.testinfra.loadbalancing.SortingLoadBalancingPolicy::newQueryPlan(com.datastax.oss.driver.api.core.session.Request, ===com.datastax.oss.driver.api.core.session.Session===)", + "annotation": "@edu.umd.cs.findbugs.annotations.Nullable", + "justification": "Method arguments were mistakenly annotated with @NonNull" + }, + { + "code": "java.method.parameterTypeParameterChanged", + "old": "parameter com.datastax.oss.driver.api.testinfra.simulacron.QueryCounter.QueryCounterBuilder com.datastax.oss.driver.api.testinfra.simulacron.QueryCounter::builder(===com.datastax.oss.simulacron.server.BoundTopic===)", + "new": "parameter com.datastax.oss.driver.api.testinfra.simulacron.QueryCounter.QueryCounterBuilder com.datastax.oss.driver.api.testinfra.simulacron.QueryCounter::builder(===com.datastax.oss.simulacron.server.BoundTopic===)", + "justification": "Fix usage of raw type BoundTopic" + }, + { + "code": "java.field.constantValueChanged", + "old": "field com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.DEFAULT_CLIENT_KEYSTORE_PASSWORD", + "new": "field com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.DEFAULT_CLIENT_KEYSTORE_PASSWORD", + "justification": "JAVA-2620: Use clearly dummy passwords in tests" + }, + { + "code": "java.field.constantValueChanged", + "old": "field com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.DEFAULT_CLIENT_TRUSTSTORE_PASSWORD", + "new": "field com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.DEFAULT_CLIENT_TRUSTSTORE_PASSWORD", + "justification": "JAVA-2620: Use clearly dummy passwords in tests" + }, + { + "code": "java.field.constantValueChanged", + "old": "field com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.DEFAULT_SERVER_KEYSTORE_PASSWORD", + "new": "field com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.DEFAULT_SERVER_KEYSTORE_PASSWORD", + "justification": "JAVA-2620: Use clearly dummy passwords in tests" + }, + { + "code": "java.field.constantValueChanged", + "old": "field com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.DEFAULT_SERVER_TRUSTSTORE_PASSWORD", + "new": "field com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.DEFAULT_SERVER_TRUSTSTORE_PASSWORD", + "justification": "JAVA-2620: Use clearly dummy passwords in tests" + }, + { + "code": "java.missing.newClass", + "new": "missing-class com.datastax.oss.simulacron.common.cluster.ClusterSpec", + "justification":"Dependency was made optional" + }, + { + "code": "java.missing.newClass", + "new": "missing-class com.datastax.oss.simulacron.common.cluster.ClusterSpec.Builder", + "justification":"Dependency was made optional" + }, + { + "code": "java.missing.newClass", + "new": "missing-class com.datastax.oss.simulacron.common.cluster.QueryLog", + "justification":"Dependency was made optional" + }, + { + "code": "java.missing.newClass", + "new": "missing-class com.datastax.oss.simulacron.server.BoundCluster", + "justification":"Dependency was made optional" + }, + { + "code": "java.missing.newClass", + "new": "missing-class com.datastax.oss.simulacron.server.BoundTopic", + "justification":"Dependency was made optional" + }, + { + "code": "java.missing.newClass", + "new": "missing-class com.datastax.oss.simulacron.server.Server", + "justification":"Dependency was made optional" + }, + { + "code": "java.missing.oldClass", + "old": "missing-class com.datastax.oss.simulacron.common.cluster.ClusterSpec", + "new": "missing-class com.datastax.oss.simulacron.common.cluster.ClusterSpec", + "justification": "Dependency was made optional" + }, + { + "code": "java.missing.oldClass", + "old": "missing-class com.datastax.oss.simulacron.common.cluster.ClusterSpec.Builder", + "new": "missing-class com.datastax.oss.simulacron.common.cluster.ClusterSpec.Builder", + "justification": "Dependency was made optional" + }, + { + "code": "java.missing.oldClass", + "old": "missing-class com.datastax.oss.simulacron.common.cluster.QueryLog", + "new": "missing-class com.datastax.oss.simulacron.common.cluster.QueryLog", + "justification": "Dependency was made optional" + }, + { + "code": "java.missing.oldClass", + "old": "missing-class com.datastax.oss.simulacron.server.BoundCluster", + "new": "missing-class com.datastax.oss.simulacron.server.BoundCluster", + "justification": "Dependency was made optional" + }, + { + "code": "java.missing.oldClass", + "old": "missing-class com.datastax.oss.simulacron.server.BoundTopic", + "new": "missing-class com.datastax.oss.simulacron.server.BoundTopic", + "justification": "Dependency was made optional" + }, + { + "code": "java.missing.oldClass", + "old": "missing-class com.datastax.oss.simulacron.server.Server", + "new": "missing-class com.datastax.oss.simulacron.server.Server", + "justification": "Dependency was made optional" + }, + { + "code": "java.method.removed", + "old": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmRule::reloadCore(int, java.lang.String, java.lang.String, boolean)", + "justification": "Modifying the state of a globally shared CCM instance is dangerous" + }, + { + "code": "java.method.removed", + "old": "method java.util.Optional com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule::getDseVersion()", + "justification": "Method has been replaced with more generic isDistributionOf(BackendType) and getDistributionVersion()" + }, + { + "code": "java.field.removed", + "old": "field com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.DSE_ENABLEMENT", + "justification": "Method has been replaced with more generic isDistributionOf(BackendType) and getDistributionVersion()" + }, + { + "code": "java.method.nowStatic", + "old": "method com.datastax.oss.driver.api.core.Version com.datastax.oss.driver.api.testinfra.ccm.CcmBridge::getCassandraVersion()", + "new": "method com.datastax.oss.driver.api.core.Version com.datastax.oss.driver.api.testinfra.ccm.CcmBridge::getCassandraVersion()", + "justification": "Previous and current implemntation do not relay on non-static fields" + }, + { + "code": "java.method.removed", + "old": "method java.util.Optional com.datastax.oss.driver.api.testinfra.ccm.CcmBridge::getDseVersion()", + "justification": "Method has been replaced with more generic isDistributionOf(BackendType) and getDistributionVersion()" + }, + { + "code": "java.method.abstractMethodAdded", + "new": "method com.datastax.oss.driver.api.core.Version com.datastax.oss.driver.api.testinfra.CassandraResourceRule::getCassandraVersion()", + "justification": "Adding methods to support polymorphic backend selection for Astra and CCM tests" + }, + { + "code": "java.method.abstractMethodAdded", + "new": "method com.datastax.oss.driver.api.testinfra.requirement.BackendType com.datastax.oss.driver.api.testinfra.CassandraResourceRule::getDistribution()", + "justification": "Adding methods to support polymorphic backend selection for Astra and CCM tests" + }, + { + "code": "java.method.abstractMethodAdded", + "new": "method com.datastax.oss.driver.api.core.Version com.datastax.oss.driver.api.testinfra.CassandraResourceRule::getDistributionVersion()", + "justification": "Adding methods to support polymorphic backend selection for Astra and CCM tests" + }, + { + "code": "java.method.abstractMethodAdded", + "new": "method boolean com.datastax.oss.driver.api.testinfra.CassandraResourceRule::isDistributionOf(com.datastax.oss.driver.api.testinfra.requirement.BackendType)", + "justification": "Adding methods to support polymorphic backend selection for Astra and CCM tests" + } ] } } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRule.java index 83c27b45e3b..4972fc958fa 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRule.java @@ -18,7 +18,9 @@ package com.datastax.oss.driver.api.testinfra; import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.Version; import com.datastax.oss.driver.api.core.metadata.EndPoint; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.internal.core.metadata.DefaultEndPoint; import java.net.InetSocketAddress; @@ -58,4 +60,21 @@ public Set getContactPoints() { /** @return The highest protocol version supported by this resource. */ public abstract ProtocolVersion getHighestProtocolVersion(); + + /** @return The backend distribution type (CASSANDRA, DSE, HCD, ASTRA). */ + public abstract BackendType getDistribution(); + + /** + * Checks if this resource is of the specified backend type. + * + * @param type the backend type to check + * @return true if this resource is of the specified type + */ + public abstract boolean isDistributionOf(BackendType type); + + /** @return The distribution version (e.g., DSE version, Cassandra version). */ + public abstract Version getDistributionVersion(); + + /** @return The Cassandra version (may differ from distribution version for DSE/HCD). */ + public abstract Version getCassandraVersion(); } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 6cb9b810a32..ece0873bb75 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -316,4 +316,33 @@ public String getToken() { public BackendType getDistribution() { return DISTRIBUTION; } + + /** + * Creates a new keyspace in the Astra database using the Astra CLI. + * + * @param keyspaceName the name of the keyspace to create + * @throws RuntimeException if the keyspace creation fails + */ + public void createKeyspace(String keyspaceName) { + if (databaseName == null) { + throw new IllegalStateException( + "Cannot create keyspace: Astra database has not been created yet"); + } + + try { + LOG.info("Creating keyspace '{}' in Astra database '{}'", keyspaceName, databaseName); + + // Create keyspace using: astra db create-keyspace -k --if-not-exist + String output = + runAstraCommand( + "db", "create-keyspace", databaseName, "-k", keyspaceName, "--if-not-exist"); + LOG.info("Keyspace creation output: {}", output); + LOG.info("Keyspace '{}' created successfully", keyspaceName); + + } catch (IOException | InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException( + "Failed to create keyspace '" + keyspaceName + "' in database '" + databaseName + "'", e); + } + } } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java index 52c8ffdf414..a4b278da19d 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java @@ -76,18 +76,22 @@ public void evaluate() { } } + @Override public BackendType getDistribution() { return AstraBridge.DISTRIBUTION; } + @Override public boolean isDistributionOf(BackendType type) { return AstraBridge.isDistributionOf(type); } + @Override public Version getDistributionVersion() { return AstraBridge.getDistributionVersion(); } + @Override public Version getCassandraVersion() { return AstraBridge.getCassandraVersion(); } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/BaseCcmRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/BaseCcmRule.java index 882cd55b948..270df44a6fd 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/BaseCcmRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/BaseCcmRule.java @@ -58,6 +58,19 @@ protected void after() { @Override public Statement apply(Statement base, Description description) { + // Skip CCM-specific tests when running with Astra + if (CcmBridge.DISTRIBUTION == BackendType.ASTRA) { + return new Statement() { + @Override + public void evaluate() { + throw new AssumptionViolatedException( + "Test uses CCM-specific rule and cannot run against Astra. " + + "Use CassandraResourceRuleFactory.getInstance() instead of CcmRule or CustomCcmRule " + + "to support both CCM and Astra backends."); + } + }; + } + if (BackendRequirementRule.meetsDescriptionRequirements(description)) { return super.apply(base, description); } else { @@ -72,10 +85,12 @@ public void evaluate() { } } + @Override public BackendType getDistribution() { return CcmBridge.DISTRIBUTION; } + @Override public boolean isDistributionOf(BackendType type) { return CcmBridge.isDistributionOf(type); } @@ -84,10 +99,12 @@ public boolean isDistributionOf(BackendType type, CcmBridge.VersionComparator co return CcmBridge.isDistributionOf(type, comparator); } + @Override public Version getDistributionVersion() { return CcmBridge.getDistributionVersion(); } + @Override public Version getCassandraVersion() { return CcmBridge.getCassandraVersion(); } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/DistributionCassandraVersions.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/DistributionCassandraVersions.java index 9f7634d1b37..0c07bc9de29 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/DistributionCassandraVersions.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/DistributionCassandraVersions.java @@ -25,6 +25,7 @@ /** Defines mapping of various distributions to shipped Apache Cassandra version. */ public abstract class DistributionCassandraVersions { + private static final Map> mappings = new HashMap<>(); @@ -45,6 +46,13 @@ public abstract class DistributionCassandraVersions { ImmutableSortedMap.of(Version.V1_0_0, CcmBridge.V4_0_11); mappings.put(BackendType.HCD, hcd); } + { + // Astra + // TODO: to be confirmed + ImmutableSortedMap astra = + ImmutableSortedMap.of(Version.V1_0_0, CcmBridge.V4_0_11); + mappings.put(BackendType.ASTRA, astra); + } } public static Version getCassandraVersion(BackendType type, Version version) { diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java index 4e485a2e567..075d0204aa6 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java @@ -101,6 +101,8 @@ public SessionRule( this.cassandraResource = cassandraResource; this.nodeStateListener = nodeStateListener; this.schemaChangeListener = schemaChangeListener; + // Generate a unique keyspace for all backends except Simulacron (when createKeyspace is true) + // For Simulacron or when createKeyspace is false, don't generate a keyspace this.keyspace = (cassandraResource instanceof SimulacronRule || !createKeyspace) ? null @@ -145,30 +147,24 @@ public SessionRule( @Override protected void before() { - // For Astra, use the keyspace that was created with the database through Astra CLI - if (cassandraResource instanceof BaseAstraRule) { - BaseAstraRule astraRule = (BaseAstraRule) cassandraResource; - String astraKeyspace = astraRule.getAstraBridge().getKeyspace(); - CqlIdentifier sessionKeyspace = CqlIdentifier.fromCql(astraKeyspace); - session = - SessionUtils.newSession( - cassandraResource, - sessionKeyspace, - nodeStateListener, - schemaChangeListener, - null, - configLoader); - } else { - session = - SessionUtils.newSession( - cassandraResource, null, nodeStateListener, schemaChangeListener, null, configLoader); - } + // Create session without keyspace first + session = + SessionUtils.newSession( + cassandraResource, null, nodeStateListener, schemaChangeListener, null, configLoader); slowProfile = SessionUtils.slowProfile(session); - // Only create keyspace for non-Astra resources - if (keyspace != null && !(cassandraResource instanceof BaseAstraRule)) { - SessionUtils.createKeyspace(session, keyspace, slowProfile); + // Create keyspace if needed + if (keyspace != null) { + if (cassandraResource instanceof BaseAstraRule) { + // For Astra, create keyspace using Astra CLI + BaseAstraRule astraRule = (BaseAstraRule) cassandraResource; + astraRule.getAstraBridge().createKeyspace(keyspace.asInternal()); + } else { + // For CCM and other backends, create keyspace using CQL + SessionUtils.createKeyspace(session, keyspace, slowProfile); + } + // Switch to the keyspace session.execute( SimpleStatement.newInstance(String.format("USE %s", keyspace.asCql(false))), Statement.SYNC); diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java index e5ea07aeb77..4eef9948496 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionUtils.java @@ -142,6 +142,21 @@ public static SessionT newSession( return newSession(cassandraResourceRule, keyspace, null, null, null, loader); } + /** + * Returns a SessionBuilder configured for the given CassandraResourceRule. + * + *

This method handles the differences between Astra (which uses Secure Connect Bundle) and + * other backends (which use contact points). + * + * @param cassandraResource the Cassandra resource to connect to + * @param keyspace the keyspace to connect to (can be null) + * @return a SessionBuilder configured for the given resource + */ + public static SessionBuilder baseBuilder( + CassandraResourceRule cassandraResource, CqlIdentifier keyspace) { + return builder(cassandraResource, keyspace, null, null, null); + } + private static SessionBuilder builder( CassandraResourceRule cassandraResource, CqlIdentifier keyspace, diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/simulacron/SimulacronRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/simulacron/SimulacronRule.java index d958d097a5d..93fb4c22857 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/simulacron/SimulacronRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/simulacron/SimulacronRule.java @@ -19,8 +19,10 @@ import com.datastax.oss.driver.api.core.DefaultProtocolVersion; import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.Version; import com.datastax.oss.driver.api.core.metadata.EndPoint; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.internal.core.metadata.DefaultEndPoint; import com.datastax.oss.simulacron.common.cluster.ClusterSpec; import com.datastax.oss.simulacron.server.BoundCluster; @@ -95,4 +97,27 @@ public Set getContactPoints() { public ProtocolVersion getHighestProtocolVersion() { return DefaultProtocolVersion.V4; } + + @Override + public BackendType getDistribution() { + // Simulacron simulates Cassandra + return BackendType.CASSANDRA; + } + + @Override + public boolean isDistributionOf(BackendType type) { + return type == BackendType.CASSANDRA; + } + + @Override + public Version getDistributionVersion() { + // Simulacron simulates Cassandra 4.0 + return Version.parse("4.0.0"); + } + + @Override + public Version getCassandraVersion() { + // Simulacron simulates Cassandra 4.0 + return Version.parse("4.0.0"); + } } From fe9827571b33d30025d199756e6dcd7183c28adf Mon Sep 17 00:00:00 2001 From: janehe Date: Fri, 19 Dec 2025 13:06:40 -0800 Subject: [PATCH 07/27] annotation? --- .../oss/driver/core/cql/QueryTraceIT.java | 3 ++ .../requirement/BackendRequirement.java | 15 ++++++ .../requirement/VersionRequirement.java | 51 ++++++++++++++++++- 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java index c1cdb3829bc..41f1d22ce5a 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java @@ -29,6 +29,7 @@ import com.datastax.oss.driver.api.core.metadata.EndPoint; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; @@ -41,6 +42,7 @@ import org.junit.rules.TestRule; @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class QueryTraceIT { private static final CassandraResourceRule CASSANDRA_RESOURCE = @@ -80,6 +82,7 @@ public void should_fetch_trace_when_tracing_enabled() { .build()) .getExecutionInfo(); + //TODO: to be confirmed. It's failing against Astra assertThat(executionInfo.getTracingId()).isNotNull(); EndPoint contactPoint = CASSANDRA_RESOURCE.getContactPoints().iterator().next(); diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/BackendRequirement.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/BackendRequirement.java index 9b1400b6313..31478de4ea5 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/BackendRequirement.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/BackendRequirement.java @@ -24,6 +24,15 @@ /** * Annotation for a Class or Method that defines a database backend Version requirement. If the * type/version in use does not meet the requirement, the test is skipped. + * + *

When {@code include = true} (default), the test runs only if the backend type and version + * match the specified criteria. + * + *

When {@code include = false}, the test is skipped if the backend type matches (version + * constraints are ignored for exclusions). + * + *

Example: {@code @BackendRequirement(type = BackendType.ASTRA, include = false)} will skip the + * test when running with Astra. */ @Repeatable(BackendRequirements.class) @Retention(RetentionPolicy.RUNTIME) @@ -35,4 +44,10 @@ String maxExclusive() default ""; String description() default ""; + + /** + * Whether to include or exclude this backend type. When {@code true} (default), the test runs + * only if the backend matches. When {@code false}, the test is skipped if the backend matches. + */ + boolean include() default true; } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/VersionRequirement.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/VersionRequirement.java index 6b184490a41..7105537a48f 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/VersionRequirement.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/VersionRequirement.java @@ -36,15 +36,26 @@ public class VersionRequirement { final Optional minInclusive; final Optional maxExclusive; final String description; + final boolean include; public VersionRequirement( BackendType backendType, String minInclusive, String maxExclusive, String description) { + this(backendType, minInclusive, maxExclusive, description, true); + } + + public VersionRequirement( + BackendType backendType, + String minInclusive, + String maxExclusive, + String description, + boolean include) { this.backendType = backendType; this.minInclusive = minInclusive.isEmpty() ? Optional.empty() : Optional.of(Version.parse(minInclusive)); this.maxExclusive = maxExclusive.isEmpty() ? Optional.empty() : Optional.of(Version.parse(maxExclusive)); this.description = description; + this.include = include; } public BackendType getBackendType() { @@ -60,6 +71,16 @@ public Optional getMaxExclusive() { } public String readableString() { + // For exclusions, just show "NOT " + if (!include) { + if (!description.isEmpty()) { + return String.format("NOT %s [%s]", backendType.getFriendlyName(), description); + } else { + return String.format("NOT %s", backendType.getFriendlyName()); + } + } + + // For inclusions, show version range final String versionRange; if (minInclusive.isPresent() && maxExclusive.isPresent()) { versionRange = @@ -84,7 +105,8 @@ public static VersionRequirement fromBackendRequirement(BackendRequirement requi requirement.type(), requirement.minInclusive(), requirement.maxExclusive(), - requirement.description()); + requirement.description(), + requirement.include()); } public static VersionRequirement fromCassandraRequirement(CassandraRequirement requirement) { @@ -134,9 +156,36 @@ public static boolean meetsAny( return true; } + // First check for exclusions (include=false) + // If any requirement explicitly excludes the current backend, skip the test + boolean isExcluded = + requirements.stream() + .anyMatch( + requirement -> + !requirement.include && requirement.getBackendType() == configuredBackend); + if (isExcluded) { + return false; + } + + // Check if there are any inclusion requirements + boolean hasInclusionRequirements = + requirements.stream().anyMatch(requirement -> requirement.include); + + // If there are no inclusion requirements (only exclusions), and we're not excluded, pass + if (!hasInclusionRequirements) { + return true; + } + + // Then check for inclusions (include=true) + // At least one requirement must match the backend type and version return requirements.stream() .anyMatch( requirement -> { + // Skip exclusion requirements + if (!requirement.include) { + return false; + } + // requirement is different db type if (requirement.getBackendType() != configuredBackend) { return false; From 1b96e908a1d8dcabeb319146126959b007bf9a57 Mon Sep 17 00:00:00 2001 From: janehe Date: Fri, 19 Dec 2025 16:16:28 -0800 Subject: [PATCH 08/27] in mid of refactoring so Astra extends CCM. Not working yet. --- .../oss/driver/core/cql/AsyncResultSetIT.java | 11 +-- .../oss/driver/core/cql/BatchStatementIT.java | 4 +- .../driver/core/cql/BoundStatementCcmIT.java | 4 +- .../oss/driver/core/cql/NowInSecondsIT.java | 5 +- .../core/cql/PagingIterableSpliteratorIT.java | 5 +- .../oss/driver/core/cql/PagingStateIT.java | 5 +- .../driver/core/cql/PerRequestKeyspaceIT.java | 4 +- .../driver/core/cql/PreparedStatementIT.java | 4 +- .../oss/driver/core/cql/QueryTraceIT.java | 7 +- .../driver/core/cql/SimpleStatementCcmIT.java | 5 +- .../reactive/DefaultReactiveResultSetIT.java | 5 +- test-infra/revapi.json | 6 ++ .../CassandraResourceRuleFactory.java | 78 ------------------- .../api/testinfra/astra/BaseAstraRule.java | 5 +- .../oss/driver/api/testinfra/ccm/CcmRule.java | 29 ++++++- 15 files changed, 60 insertions(+), 117 deletions(-) delete mode 100644 test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRuleFactory.java diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java index 3f9ebde917a..61d5ffcd953 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java @@ -28,8 +28,7 @@ import com.datastax.oss.driver.api.core.cql.PreparedStatement; import com.datastax.oss.driver.api.core.cql.Row; import com.datastax.oss.driver.api.core.cql.SimpleStatement; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; @@ -53,19 +52,17 @@ public class AsyncResultSetIT { private static final String PARTITION_KEY1 = "part"; private static final String PARTITION_KEY2 = "part2"; - private static final CassandraResourceRule CASSANDRA_RESOURCE = - CassandraResourceRuleFactory.getInstance(); + private static final CcmRule ccmRule = CcmRule.getInstance(); private static final SessionRule SESSION_RULE = - SessionRule.builder(CASSANDRA_RESOURCE) + SessionRule.builder(ccmRule) .withConfigLoader( SessionUtils.configLoaderBuilder() .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, PAGE_SIZE) .build()) .build(); - @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); + @ClassRule public static final TestRule CHAIN = RuleChain.outerRule(ccmRule).around(SESSION_RULE); @BeforeClass public static void setupSchema() { diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java index 7e6d68737dd..77ebfb052da 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java @@ -34,7 +34,7 @@ import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; @@ -54,7 +54,7 @@ @Category(ParallelizableTests.class) public class BatchStatementIT { - private CassandraResourceRule cassandraResource = CassandraResourceRuleFactory.getInstance(); + private CassandraResourceRule cassandraResource = CcmRule.getInstance(); private SessionRule sessionRule = SessionRule.builder(cassandraResource).build(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java index 65d96596d7c..6441af9eef0 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java @@ -41,7 +41,7 @@ import com.datastax.oss.driver.api.core.session.SessionBuilder; import com.datastax.oss.driver.api.core.type.codec.TypeCodecs; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; @@ -74,7 +74,7 @@ @Category(ParallelizableTests.class) public class BoundStatementCcmIT { - private CassandraResourceRule cassandraResource = CassandraResourceRuleFactory.getInstance(); + private CassandraResourceRule cassandraResource = CcmRule.getInstance(); private final boolean atLeastV4 = cassandraResource.getHighestProtocolVersion().getCode() >= 4; diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java index da1ff76336a..5e66d10bb5c 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java @@ -27,7 +27,7 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; @@ -50,8 +50,7 @@ description = "Feature not available in DSE yet") public class NowInSecondsIT { - private static final CassandraResourceRule CASSANDRA_RESOURCE = - CassandraResourceRuleFactory.getInstance(); + private static final CassandraResourceRule CASSANDRA_RESOURCE = CcmRule.getInstance(); private static final SessionRule SESSION_RULE = SessionRule.builder(CASSANDRA_RESOURCE).build(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java index 7712a915b11..962b4851a6a 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java @@ -30,7 +30,7 @@ import com.datastax.oss.driver.api.core.cql.Row; import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; import com.datastax.oss.driver.shaded.guava.common.collect.Lists; @@ -53,8 +53,7 @@ @Category(ParallelizableTests.class) public class PagingIterableSpliteratorIT { - private static final CassandraResourceRule CASSANDRA_RESOURCE = - CassandraResourceRuleFactory.getInstance(); + private static final CassandraResourceRule CASSANDRA_RESOURCE = CcmRule.getInstance(); private static final SessionRule SESSION_RULE = SessionRule.builder(CASSANDRA_RESOURCE).build(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java index 8d062c8abbd..279c354e61c 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java @@ -30,7 +30,7 @@ import com.datastax.oss.driver.api.core.type.codec.MappingCodec; import com.datastax.oss.driver.api.core.type.reflect.GenericType; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; @@ -48,8 +48,7 @@ @Category(ParallelizableTests.class) public class PagingStateIT { - private static final CassandraResourceRule CASSANDRA_RESOURCE = - CassandraResourceRuleFactory.getInstance(); + private static final CassandraResourceRule CASSANDRA_RESOURCE = CcmRule.getInstance(); private static final SessionRule SESSION_RULE = SessionRule.builder(CASSANDRA_RESOURCE).build(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java index 8e451fb47b0..f142ea9c340 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java @@ -31,7 +31,7 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; @@ -59,7 +59,7 @@ @Category(ParallelizableTests.class) public class PerRequestKeyspaceIT { - private CassandraResourceRule cassandraResource = CassandraResourceRuleFactory.getInstance(); + private CassandraResourceRule cassandraResource = CcmRule.getInstance(); private SessionRule sessionRule = SessionRule.builder(cassandraResource).build(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java index b5f16b1b05b..88f7cae1c5a 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java @@ -37,7 +37,7 @@ import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; import com.datastax.oss.driver.api.core.type.DataTypes; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; @@ -71,7 +71,7 @@ @Category(ParallelizableTests.class) public class PreparedStatementIT { - private CassandraResourceRule cassandraResource = CassandraResourceRuleFactory.getInstance(); + private CassandraResourceRule cassandraResource = CcmRule.getInstance(); private SessionRule sessionRule = SessionRule.builder(cassandraResource) diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java index 41f1d22ce5a..a21d79dde77 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java @@ -28,7 +28,7 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.metadata.EndPoint; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; @@ -45,8 +45,7 @@ @BackendRequirement(type = BackendType.ASTRA, include = false) public class QueryTraceIT { - private static final CassandraResourceRule CASSANDRA_RESOURCE = - CassandraResourceRuleFactory.getInstance(); + private static final CassandraResourceRule CASSANDRA_RESOURCE = CcmRule.getInstance(); private static final SessionRule SESSION_RULE = SessionRule.builder(CASSANDRA_RESOURCE).build(); @@ -82,7 +81,7 @@ public void should_fetch_trace_when_tracing_enabled() { .build()) .getExecutionInfo(); - //TODO: to be confirmed. It's failing against Astra + // TODO: to be confirmed. It's failing against Astra assertThat(executionInfo.getTracingId()).isNotNull(); EndPoint contactPoint = CASSANDRA_RESOURCE.getContactPoints().iterator().next(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java index 409b8c91c84..f604db4cee1 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java @@ -29,7 +29,7 @@ import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; import com.datastax.oss.driver.categories.ParallelizableTests; @@ -50,8 +50,7 @@ @Category(ParallelizableTests.class) public class SimpleStatementCcmIT { - private static final CassandraResourceRule CASSANDRA_RESOURCE = - CassandraResourceRuleFactory.getInstance(); + private static final CassandraResourceRule CASSANDRA_RESOURCE = CcmRule.getInstance(); private static final SessionRule SESSION_RULE = SessionRule.builder(CASSANDRA_RESOURCE) diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java index 61f7ca6e468..6cfc96559a1 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java @@ -32,7 +32,7 @@ import com.datastax.oss.driver.api.core.cql.PreparedStatement; import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRuleFactory; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; @@ -57,8 +57,7 @@ @Category(ParallelizableTests.class) public class DefaultReactiveResultSetIT { - private static CassandraResourceRule cassandraResource = - CassandraResourceRuleFactory.getInstance(); + private static CassandraResourceRule cassandraResource = CcmRule.getInstance(); private static SessionRule sessionRule = SessionRule.builder(cassandraResource).build(); diff --git a/test-infra/revapi.json b/test-infra/revapi.json index e2944de87bd..ca41318ab9f 100644 --- a/test-infra/revapi.json +++ b/test-infra/revapi.json @@ -210,6 +210,12 @@ "code": "java.method.abstractMethodAdded", "new": "method boolean com.datastax.oss.driver.api.testinfra.CassandraResourceRule::isDistributionOf(com.datastax.oss.driver.api.testinfra.requirement.BackendType)", "justification": "Adding methods to support polymorphic backend selection for Astra and CCM tests" + }, + { + "code": "java.method.returnTypeChanged", + "old": "method com.datastax.oss.driver.api.testinfra.ccm.CcmRule com.datastax.oss.driver.api.testinfra.ccm.CcmRule::getInstance()", + "new": "method com.datastax.oss.driver.api.testinfra.CassandraResourceRule com.datastax.oss.driver.api.testinfra.ccm.CcmRule::getInstance()", + "justification": "CcmRule.getInstance() now returns CassandraResourceRule to support polymorphic backend selection (CCM or Astra) based on ccm.distribution system property" } ] } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRuleFactory.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRuleFactory.java deleted file mode 100644 index 088f1608bc0..00000000000 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraResourceRuleFactory.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 com.datastax.oss.driver.api.testinfra; - -import com.datastax.oss.driver.api.testinfra.astra.AstraRule; -import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; -import com.datastax.oss.driver.api.testinfra.requirement.BackendType; - -/** - * Factory for creating {@link CassandraResourceRule} instances based on the {@code - * ccm.distribution} system property. - * - *

This allows tests to run against different backends (Cassandra OSS, DSE, HCD, Astra) by simply - * setting the {@code ccm.distribution} system property. - * - *

Example usage: - * - *

{@code
- * @ClassRule
- * public static final CassandraResourceRule CASSANDRA_RESOURCE =
- *     CassandraResourceRuleFactory.getInstance();
- * }
- * - *

To run against Cassandra OSS (default): - * - *

- * mvn test -Dtest=MyTest
- * 
- * - *

To run against Astra: - * - *

- * mvn test -Dtest=MyTest -Dccm.distribution=ASTRA -Dastra.token="..."
- * 
- */ -public class CassandraResourceRuleFactory { - - private static final BackendType DISTRIBUTION = - BackendType.valueOf( - System.getProperty("ccm.distribution", BackendType.CASSANDRA.name()).toUpperCase()); - - /** - * Returns a {@link CassandraResourceRule} instance based on the {@code ccm.distribution} system - * property. - * - *

If {@code ccm.distribution=ASTRA}, returns {@link AstraRule#getInstance()}. Otherwise, - * returns {@link CcmRule#getInstance()}. - * - * @return the appropriate CassandraResourceRule for the configured distribution - */ - public static CassandraResourceRule getInstance() { - return DISTRIBUTION == BackendType.ASTRA ? AstraRule.getInstance() : CcmRule.getInstance(); - } - - /** - * Returns the configured backend distribution type. - * - * @return the BackendType from the ccm.distribution system property - */ - public static BackendType getDistribution() { - return DISTRIBUTION; - } -} diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java index a4b278da19d..910cb33e44c 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java @@ -21,7 +21,7 @@ import com.datastax.oss.driver.api.core.ProtocolVersion; import com.datastax.oss.driver.api.core.Version; import com.datastax.oss.driver.api.core.metadata.EndPoint; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; +import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirementRule; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import java.io.File; @@ -31,11 +31,12 @@ import org.junit.runner.Description; import org.junit.runners.model.Statement; -public abstract class BaseAstraRule extends CassandraResourceRule { +public abstract class BaseAstraRule extends CcmRule { protected final AstraBridge astraBridge; BaseAstraRule(AstraBridge astraBridge) { + super(); this.astraBridge = astraBridge; Runtime.getRuntime() .addShutdownHook( diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmRule.java index e6483c37877..eefe3054b80 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmRule.java @@ -17,6 +17,8 @@ */ package com.datastax.oss.driver.api.testinfra.ccm; +import com.datastax.oss.driver.api.testinfra.astra.AstraRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.categories.ParallelizableTests; import java.lang.reflect.Method; import org.junit.AssumptionViolatedException; @@ -32,14 +34,20 @@ * *

Note that this rule should be considered mutually exclusive with {@link CustomCcmRule}. * Creating instances of these rules can create resource issues. + * + *

When {@code ccm.distribution} system property is set to {@code ASTRA}, this rule will delegate + * to {@link AstraRule} instead of creating a CCM cluster. */ public class CcmRule extends BaseCcmRule { - private static final CcmRule INSTANCE = new CcmRule(); + private static final CcmRule CCM_INSTANCE = new CcmRule(); + private static final BackendType DISTRIBUTION = + BackendType.valueOf( + System.getProperty("ccm.distribution", BackendType.CASSANDRA.name()).toUpperCase()); private volatile boolean started = false; - private CcmRule() { + protected CcmRule() { super(configureCcmBridge(CcmBridge.builder()).build()); } @@ -99,7 +107,22 @@ public void evaluate() { return super.apply(base, description); } + /** + * Returns a singleton instance of a Cassandra resource rule. + * + *

The actual implementation returned depends on the {@code ccm.distribution} system property: + * + *

    + *
  • If set to {@code ASTRA}, returns {@link AstraRule#getInstance()} + *
  • Otherwise, returns a {@link CcmRule} instance + *
+ * + * @return a singleton Cassandra resource rule + */ public static CcmRule getInstance() { - return INSTANCE; + if (DISTRIBUTION == BackendType.ASTRA) { + return AstraRule.getInstance(); + } + return CCM_INSTANCE; } } From 273fdc35b68e1479b8748028c0ea70d301c0d6ad Mon Sep 17 00:00:00 2001 From: janehe Date: Fri, 19 Dec 2025 16:32:21 -0800 Subject: [PATCH 09/27] revert some changes --- .../oss/driver/core/cql/AsyncResultSetIT.java | 15 ++--- .../oss/driver/core/cql/BatchStatementIT.java | 17 +++-- .../driver/core/cql/BoundStatementCcmIT.java | 63 +++++++------------ .../oss/driver/core/cql/NowInSecondsIT.java | 8 +-- .../core/cql/PagingIterableSpliteratorIT.java | 16 ++--- .../oss/driver/core/cql/PagingStateIT.java | 20 +++--- .../driver/core/cql/PerRequestKeyspaceIT.java | 11 ++-- .../driver/core/cql/PreparedStatementIT.java | 24 ++++--- .../oss/driver/core/cql/QueryTraceIT.java | 17 ++--- .../driver/core/cql/SimpleStatementCcmIT.java | 24 ++----- .../reactive/DefaultReactiveResultSetIT.java | 9 +-- 11 files changed, 83 insertions(+), 141 deletions(-) diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java index 61d5ffcd953..e109c28525e 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/AsyncResultSetIT.java @@ -52,34 +52,29 @@ public class AsyncResultSetIT { private static final String PARTITION_KEY1 = "part"; private static final String PARTITION_KEY2 = "part2"; - private static final CcmRule ccmRule = CcmRule.getInstance(); + private static final CcmRule CCM_RULE = CcmRule.getInstance(); private static final SessionRule SESSION_RULE = - SessionRule.builder(ccmRule) + SessionRule.builder(CCM_RULE) .withConfigLoader( SessionUtils.configLoaderBuilder() .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, PAGE_SIZE) .build()) .build(); - @ClassRule public static final TestRule CHAIN = RuleChain.outerRule(ccmRule).around(SESSION_RULE); + @ClassRule + public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); @BeforeClass public static void setupSchema() { // create table and load data across two partitions so we can test paging across tokens. SchemaChangeSynchronizer.withLock( () -> { - SESSION_RULE - .session() - .execute( - SimpleStatement.builder("DROP TABLE IF EXISTS test") - .setExecutionProfile(SESSION_RULE.slowProfile()) - .build()); SESSION_RULE .session() .execute( SimpleStatement.builder( - "CREATE TABLE test (k0 text, k1 int, v int, PRIMARY KEY(k0, k1))") + "CREATE TABLE IF NOT EXISTS test (k0 text, k1 int, v int, PRIMARY KEY(k0, k1))") .setExecutionProfile(SESSION_RULE.slowProfile()) .build()); }); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java index 77ebfb052da..8b652638729 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BatchStatementIT.java @@ -33,7 +33,6 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; @@ -54,11 +53,11 @@ @Category(ParallelizableTests.class) public class BatchStatementIT { - private CassandraResourceRule cassandraResource = CcmRule.getInstance(); + private CcmRule ccmRule = CcmRule.getInstance(); - private SessionRule sessionRule = SessionRule.builder(cassandraResource).build(); + private SessionRule sessionRule = SessionRule.builder(ccmRule).build(); - @Rule public TestRule chain = RuleChain.outerRule(cassandraResource).around(sessionRule); + @Rule public TestRule chain = RuleChain.outerRule(ccmRule).around(sessionRule); @Rule public TestName name = new TestName(); @@ -68,10 +67,10 @@ public class BatchStatementIT { public void createTable() { String[] schemaStatements = new String[] { - "CREATE TABLE IF NOT EXISTS test (k0 text, k1 int, v int, PRIMARY KEY (k0, k1))", - "CREATE TABLE IF NOT EXISTS counter1 (k0 text PRIMARY KEY, c counter)", - "CREATE TABLE IF NOT EXISTS counter2 (k0 text PRIMARY KEY, c counter)", - "CREATE TABLE IF NOT EXISTS counter3 (k0 text PRIMARY KEY, c counter)", + "CREATE TABLE test (k0 text, k1 int, v int, PRIMARY KEY (k0, k1))", + "CREATE TABLE counter1 (k0 text PRIMARY KEY, c counter)", + "CREATE TABLE counter2 (k0 text PRIMARY KEY, c counter)", + "CREATE TABLE counter3 (k0 text PRIMARY KEY, c counter)", }; SchemaChangeSynchronizer.withLock( @@ -354,7 +353,7 @@ public void should_not_allow_unset_value_when_protocol_less_than_v4() { SessionUtils.configLoaderBuilder() .withString(DefaultDriverOption.PROTOCOL_VERSION, "V3") .build(); - try (CqlSession v3Session = SessionUtils.newSession(cassandraResource, loader)) { + try (CqlSession v3Session = SessionUtils.newSession(ccmRule, loader)) { // Intentionally use fully qualified table here to avoid warnings as these are not supported // by v3 protocol version, see JAVA-3068 PreparedStatement prepared = diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java index 6441af9eef0..9e4b62cd230 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java @@ -38,9 +38,7 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder; import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.core.metadata.token.Token; -import com.datastax.oss.driver.api.core.session.SessionBuilder; import com.datastax.oss.driver.api.core.type.codec.TypeCodecs; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; @@ -74,19 +72,19 @@ @Category(ParallelizableTests.class) public class BoundStatementCcmIT { - private CassandraResourceRule cassandraResource = CcmRule.getInstance(); + private CcmRule ccmRule = CcmRule.getInstance(); - private final boolean atLeastV4 = cassandraResource.getHighestProtocolVersion().getCode() >= 4; + private final boolean atLeastV4 = ccmRule.getHighestProtocolVersion().getCode() >= 4; private SessionRule sessionRule = - SessionRule.builder(cassandraResource) + SessionRule.builder(ccmRule) .withConfigLoader( SessionUtils.configLoaderBuilder() .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 20) .build()) .build(); - @Rule public TestRule chain = RuleChain.outerRule(cassandraResource).around(sessionRule); + @Rule public TestRule chain = RuleChain.outerRule(ccmRule).around(sessionRule); @Rule public TestName name = new TestName(); @@ -102,13 +100,8 @@ public void setupSchema() { sessionRule .session() .execute( - SimpleStatement.builder("DROP TABLE IF EXISTS test") - .setExecutionProfile(sessionRule.slowProfile()) - .build()); - sessionRule - .session() - .execute( - SimpleStatement.builder("CREATE TABLE test (k text, v int, PRIMARY KEY(k, v))") + SimpleStatement.builder( + "CREATE TABLE IF NOT EXISTS test (k text, v int, PRIMARY KEY(k, v))") .setExecutionProfile(sessionRule.slowProfile()) .build()); for (int i = 0; i < 100; i++) { @@ -124,28 +117,17 @@ public void setupSchema() { sessionRule .session() .execute( - SimpleStatement.builder("DROP TABLE IF EXISTS test2") - .setExecutionProfile(sessionRule.slowProfile()) - .build()); - sessionRule - .session() - .execute( - SimpleStatement.builder("CREATE TABLE test2 (k text primary key, v0 int)") + SimpleStatement.builder( + "CREATE TABLE IF NOT EXISTS test2 (k text primary key, v0 int)") .setExecutionProfile(sessionRule.slowProfile()) .build()); // table with composite partition key - sessionRule - .session() - .execute( - SimpleStatement.builder("DROP TABLE IF EXISTS test3") - .setExecutionProfile(sessionRule.slowProfile()) - .build()); sessionRule .session() .execute( SimpleStatement.builder( - "CREATE TABLE test3 " + "CREATE TABLE IF NOT EXISTS test3 " + "(pk1 int, pk2 int, v int, " + "PRIMARY KEY ((pk1, pk2)))") .setExecutionProfile(sessionRule.slowProfile()) @@ -159,7 +141,7 @@ public void should_not_allow_unset_value_when_protocol_less_than_v4() { SessionUtils.configLoaderBuilder() .withString(DefaultDriverOption.PROTOCOL_VERSION, "V3") .build(); - try (CqlSession v3Session = SessionUtils.newSession(cassandraResource, loader)) { + try (CqlSession v3Session = SessionUtils.newSession(ccmRule, loader)) { // Intentionally use fully qualified table here to avoid warnings as these are not supported // by v3 protocol version, see JAVA-3068 PreparedStatement prepared = @@ -178,7 +160,7 @@ public void should_not_allow_unset_value_when_protocol_less_than_v4() { @Test public void should_not_write_tombstone_if_value_is_implicitly_unset() { assumeThat(atLeastV4).as("unset values require protocol V4+").isTrue(); - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO test2 (k, v0) values (?, ?)"); session.execute(prepared.bind(name.getMethodName(), VALUE)); @@ -193,7 +175,7 @@ public void should_not_write_tombstone_if_value_is_implicitly_unset() { @Test public void should_write_tombstone_if_value_is_explicitly_unset() { assumeThat(atLeastV4).as("unset values require protocol V4+").isTrue(); - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO test2 (k, v0) values (?, ?)"); session.execute(prepared.bind(name.getMethodName(), VALUE)); @@ -212,7 +194,7 @@ public void should_write_tombstone_if_value_is_explicitly_unset() { @Test public void should_write_tombstone_if_value_is_explicitly_unset_on_builder() { assumeThat(atLeastV4).as("unset values require protocol V4+").isTrue(); - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO test2 (k, v0) values (?, ?)"); session.execute(prepared.bind(name.getMethodName(), VALUE)); @@ -231,7 +213,7 @@ public void should_write_tombstone_if_value_is_explicitly_unset_on_builder() { @Test public void should_have_empty_result_definitions_for_update_query() { - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO test2 (k, v0) values (?, ?)"); assertThat(prepared.getResultSetDefinitions()).hasSize(0); @@ -243,7 +225,7 @@ public void should_have_empty_result_definitions_for_update_query() { @Test public void should_bind_null_value_when_setting_values_in_bulk() { - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO test2 (k, v0) values (?, ?)"); BoundStatement boundStatement = prepared.bind(name.getMethodName(), null); assertThat(boundStatement.get(1, TypeCodecs.INT)).isNull(); @@ -273,7 +255,7 @@ public void should_allow_custom_codecs_when_setting_values_in_bulk() { @Test public void should_use_page_size_from_simple_statement() { - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { SimpleStatement st = SimpleStatement.builder("SELECT v FROM test").setPageSize(10).build(); PreparedStatement prepared = session.prepare(st); CompletionStage future = session.executeAsync(prepared.bind()); @@ -286,7 +268,7 @@ public void should_use_page_size_from_simple_statement() { @Test public void should_use_page_size() { - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { // set page size on simple statement, but will be unused since // overridden by bound statement. SimpleStatement st = SimpleStatement.builder("SELECT v FROM test").setPageSize(10).build(); @@ -381,7 +363,7 @@ public void should_propagate_attributes_when_preparing_a_simple_statement() { @Test @BackendRequirement(type = BackendType.CASSANDRA, minInclusive = "2.2") public void should_compute_routing_key_when_indices_randomly_distributed() { - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { PreparedStatement ps = session.prepare("INSERT INTO test3 (v, pk2, pk1) VALUES (?,?,?)"); @@ -452,9 +434,12 @@ private static void verifyUnset( @SuppressWarnings("unchecked") private CqlSession sessionWithCustomCodec(CqlIntToStringCodec codec) { - SessionBuilder builder = - SessionUtils.baseBuilder(cassandraResource, sessionRule.keyspace()); - return (CqlSession) builder.addTypeCodecs(codec).build(); + return (CqlSession) + SessionUtils.baseBuilder() + .addContactEndPoints(ccmRule.getContactPoints()) + .withKeyspace(sessionRule.keyspace()) + .addTypeCodecs(codec) + .build(); } private boolean supportsPerRequestKeyspace(CqlSession session) { diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java index 5e66d10bb5c..191dc040ffd 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/NowInSecondsIT.java @@ -26,7 +26,6 @@ import com.datastax.oss.driver.api.core.cql.ResultSet; import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.cql.Statement; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; @@ -50,13 +49,12 @@ description = "Feature not available in DSE yet") public class NowInSecondsIT { - private static final CassandraResourceRule CASSANDRA_RESOURCE = CcmRule.getInstance(); + private static final CcmRule CCM_RULE = CcmRule.getInstance(); - private static final SessionRule SESSION_RULE = - SessionRule.builder(CASSANDRA_RESOURCE).build(); + private static final SessionRule SESSION_RULE = SessionRule.builder(CCM_RULE).build(); @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); + public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); @Before public void setup() { diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java index 962b4851a6a..02078b683db 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingIterableSpliteratorIT.java @@ -29,7 +29,6 @@ import com.datastax.oss.driver.api.core.cql.ResultSet; import com.datastax.oss.driver.api.core.cql.Row; import com.datastax.oss.driver.api.core.cql.SimpleStatement; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; @@ -53,27 +52,20 @@ @Category(ParallelizableTests.class) public class PagingIterableSpliteratorIT { - private static final CassandraResourceRule CASSANDRA_RESOURCE = CcmRule.getInstance(); + private static final CcmRule CCM_RULE = CcmRule.getInstance(); - private static final SessionRule SESSION_RULE = - SessionRule.builder(CASSANDRA_RESOURCE).build(); + private static final SessionRule SESSION_RULE = SessionRule.builder(CCM_RULE).build(); @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); + public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); @BeforeClass public static void setupSchema() { - SESSION_RULE - .session() - .execute( - SimpleStatement.builder("DROP TABLE IF EXISTS test") - .setExecutionProfile(SESSION_RULE.slowProfile()) - .build()); SESSION_RULE .session() .execute( SimpleStatement.builder( - "CREATE TABLE test (k0 int, k1 int, v int, PRIMARY KEY(k0, k1))") + "CREATE TABLE IF NOT EXISTS test (k0 int, k1 int, v int, PRIMARY KEY(k0, k1))") .setExecutionProfile(SESSION_RULE.slowProfile()) .build()); PreparedStatement prepared = diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java index 279c354e61c..6d33f35238a 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java @@ -29,7 +29,6 @@ import com.datastax.oss.driver.api.core.type.codec.CodecNotFoundException; import com.datastax.oss.driver.api.core.type.codec.MappingCodec; import com.datastax.oss.driver.api.core.type.reflect.GenericType; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.session.SessionRule; @@ -48,13 +47,11 @@ @Category(ParallelizableTests.class) public class PagingStateIT { - private static final CassandraResourceRule CASSANDRA_RESOURCE = CcmRule.getInstance(); + private static final CcmRule CCM_RULE = CcmRule.getInstance(); - private static final SessionRule SESSION_RULE = - SessionRule.builder(CASSANDRA_RESOURCE).build(); + private static final SessionRule SESSION_RULE = SessionRule.builder(CCM_RULE).build(); - @ClassRule - public static TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); + @ClassRule public static TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); @Before public void setupSchema() { @@ -62,11 +59,8 @@ public void setupSchema() { SchemaChangeSynchronizer.withLock( () -> { session.execute( - SimpleStatement.builder("DROP TABLE IF EXISTS foo") - .setExecutionProfile(SESSION_RULE.slowProfile()) - .build()); - session.execute( - SimpleStatement.builder("CREATE TABLE foo (k int, cc int, v int, PRIMARY KEY(k, cc))") + SimpleStatement.builder( + "CREATE TABLE IF NOT EXISTS foo (k int, cc int, v int, PRIMARY KEY(k, cc))") .setExecutionProfile(SESSION_RULE.slowProfile()) .build()); }); @@ -116,8 +110,10 @@ private void should_extract_and_reuse(UnaryOperator transformation) public void should_inject_in_simple_statement_with_custom_codecs() { try (CqlSession session = (CqlSession) - SessionUtils.baseBuilder(CASSANDRA_RESOURCE, SESSION_RULE.keyspace()) + SessionUtils.baseBuilder() .addTypeCodecs(new IntWrapperCodec()) + .addContactEndPoints(CCM_RULE.getContactPoints()) + .withKeyspace(SESSION_RULE.keyspace()) .build()) { SimpleStatement statement = diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java index f142ea9c340..9eb883144db 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PerRequestKeyspaceIT.java @@ -30,7 +30,6 @@ import com.datastax.oss.driver.api.core.cql.Row; import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.cql.Statement; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; @@ -59,11 +58,11 @@ @Category(ParallelizableTests.class) public class PerRequestKeyspaceIT { - private CassandraResourceRule cassandraResource = CcmRule.getInstance(); + private CcmRule ccmRule = CcmRule.getInstance(); - private SessionRule sessionRule = SessionRule.builder(cassandraResource).build(); + private SessionRule sessionRule = SessionRule.builder(ccmRule).build(); - @Rule public TestRule chain = RuleChain.outerRule(cassandraResource).around(sessionRule); + @Rule public TestRule chain = RuleChain.outerRule(ccmRule).around(sessionRule); @Rule public TestName nameRule = new TestName(); @@ -119,7 +118,7 @@ private void should_reject_statement_with_keyspace_in_protocol_v4(Statement stat SessionUtils.configLoaderBuilder() .withString(DefaultDriverOption.PROTOCOL_VERSION, "V4") .build(); - try (CqlSession session = SessionUtils.newSession(cassandraResource, loader)) { + try (CqlSession session = SessionUtils.newSession(ccmRule, loader)) { Throwable t = catchThrowable(() -> session.execute(statement)); assertThat(t) .isInstanceOf(IllegalArgumentException.class) @@ -227,7 +226,7 @@ public void should_reprepare_statement_with_keyspace_on_the_fly() { // Create a separate session because we don't want it to have a default keyspace SchemaChangeSynchronizer.withLock( () -> { - try (CqlSession session = SessionUtils.newSession(cassandraResource)) { + try (CqlSession session = SessionUtils.newSession(ccmRule)) { executeDdl( session, String.format( diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java index 88f7cae1c5a..5671a7684e5 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementIT.java @@ -36,7 +36,6 @@ import com.datastax.oss.driver.api.core.metrics.DefaultSessionMetric; import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; import com.datastax.oss.driver.api.core.type.DataTypes; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; @@ -71,10 +70,10 @@ @Category(ParallelizableTests.class) public class PreparedStatementIT { - private CassandraResourceRule cassandraResource = CcmRule.getInstance(); + private CcmRule ccmRule = CcmRule.getInstance(); private SessionRule sessionRule = - SessionRule.builder(cassandraResource) + SessionRule.builder(ccmRule) .withConfigLoader( SessionUtils.configLoaderBuilder() .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 2) @@ -82,7 +81,7 @@ public class PreparedStatementIT { .build()) .build(); - @Rule public TestRule chain = RuleChain.outerRule(cassandraResource).around(sessionRule); + @Rule public TestRule chain = RuleChain.outerRule(ccmRule).around(sessionRule); @Before public void setupSchema() { @@ -107,7 +106,7 @@ private void executeDdl(String query) { @Test public void should_have_empty_result_definitions_for_insert_query_without_bound_variable() { - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO prepared_statement_test (a, b, c) VALUES (1, 1, 1)"); assertThat(prepared.getVariableDefinitions()).isEmpty(); @@ -118,7 +117,7 @@ public void should_have_empty_result_definitions_for_insert_query_without_bound_ @Test public void should_have_non_empty_result_definitions_for_insert_query_with_bound_variable() { - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("INSERT INTO prepared_statement_test (a, b, c) VALUES (?, ?, ?)"); assertThat(prepared.getVariableDefinitions()).hasSize(3); @@ -129,7 +128,7 @@ public void should_have_non_empty_result_definitions_for_insert_query_with_bound @Test public void should_have_empty_variable_definitions_for_select_query_without_bound_variable() { - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("SELECT a,b,c FROM prepared_statement_test WHERE a = 1"); assertThat(prepared.getVariableDefinitions()).isEmpty(); @@ -140,7 +139,7 @@ public void should_have_empty_variable_definitions_for_select_query_without_boun @Test public void should_have_non_empty_variable_definitions_for_select_query_with_bound_variable() { - try (CqlSession session = SessionUtils.newSession(cassandraResource, sessionRule.keyspace())) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace())) { PreparedStatement prepared = session.prepare("SELECT a,b,c FROM prepared_statement_test WHERE a = ?"); assertThat(prepared.getVariableDefinitions()).hasSize(1); @@ -228,7 +227,7 @@ public void should_update_metadata_when_schema_changed_across_pages() { public void should_update_metadata_when_schema_changed_across_sessions() { // Given CqlSession session1 = sessionRule.session(); - CqlSession session2 = SessionUtils.newSession(cassandraResource, sessionRule.keyspace()); + CqlSession session2 = SessionUtils.newSession(ccmRule, sessionRule.keyspace()); PreparedStatement ps1 = session1.prepare("SELECT * FROM prepared_statement_test WHERE a = ?"); PreparedStatement ps2 = session2.prepare("SELECT * FROM prepared_statement_test WHERE a = ?"); @@ -303,8 +302,7 @@ public void should_not_store_metadata_for_conditional_updates_in_legacy_protocol .withString(DefaultDriverOption.PROTOCOL_VERSION, "V4") .withDuration(DefaultDriverOption.REQUEST_TIMEOUT, Duration.ofSeconds(30)) .build(); - try (CqlSession session = - SessionUtils.newSession(cassandraResource, sessionRule.keyspace(), loader)) { + try (CqlSession session = SessionUtils.newSession(ccmRule, sessionRule.keyspace(), loader)) { should_not_store_metadata_for_conditional_updates(session); } } @@ -450,7 +448,7 @@ public void should_create_separate_instances_for_different_statement_parameters( * @see CASSANDRA-15252 */ private AbstractThrowableAssert assertableReprepareAfterIdChange() { - try (CqlSession session = SessionUtils.newSession(cassandraResource)) { + try (CqlSession session = SessionUtils.newSession(ccmRule)) { PreparedStatement preparedStatement = session.prepare( String.format( @@ -554,7 +552,7 @@ private static Iterable firstPageOf(CompletionStage stage) private CqlSession sessionWithCacheSizeMetric() { return SessionUtils.newSession( - cassandraResource, + ccmRule, sessionRule.keyspace(), SessionUtils.configLoaderBuilder() .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 2) diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java index a21d79dde77..37a600efbc4 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java @@ -27,9 +27,7 @@ import com.datastax.oss.driver.api.core.cql.Row; import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.metadata.EndPoint; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; -import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; @@ -42,16 +40,14 @@ import org.junit.rules.TestRule; @Category(ParallelizableTests.class) -@BackendRequirement(type = BackendType.ASTRA, include = false) public class QueryTraceIT { - private static final CassandraResourceRule CASSANDRA_RESOURCE = CcmRule.getInstance(); + private static final CcmRule CCM_RULE = CcmRule.getInstance(); - private static final SessionRule SESSION_RULE = - SessionRule.builder(CASSANDRA_RESOURCE).build(); + private static final SessionRule SESSION_RULE = SessionRule.builder(CCM_RULE).build(); @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); + public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); @Test public void should_not_have_tracing_id_when_tracing_disabled() { @@ -81,14 +77,13 @@ public void should_fetch_trace_when_tracing_enabled() { .build()) .getExecutionInfo(); - // TODO: to be confirmed. It's failing against Astra assertThat(executionInfo.getTracingId()).isNotNull(); - EndPoint contactPoint = CASSANDRA_RESOURCE.getContactPoints().iterator().next(); + EndPoint contactPoint = CCM_RULE.getContactPoints().iterator().next(); InetAddress nodeAddress = ((InetSocketAddress) contactPoint.resolve()).getAddress(); boolean expectPorts = - CASSANDRA_RESOURCE.getCassandraVersion().nextStable().compareTo(Version.V4_0_0) >= 0 - && !CASSANDRA_RESOURCE.isDistributionOf(BackendType.DSE); + CCM_RULE.getCassandraVersion().nextStable().compareTo(Version.V4_0_0) >= 0 + && !CCM_RULE.isDistributionOf(BackendType.DSE); QueryTrace queryTrace = executionInfo.getQueryTrace(); assertThat(queryTrace.getTracingId()).isEqualTo(executionInfo.getTracingId()); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java index f604db4cee1..b903f59efcc 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/SimpleStatementCcmIT.java @@ -28,7 +28,6 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; @@ -50,10 +49,10 @@ @Category(ParallelizableTests.class) public class SimpleStatementCcmIT { - private static final CassandraResourceRule CASSANDRA_RESOURCE = CcmRule.getInstance(); + private static final CcmRule CCM_RULE = CcmRule.getInstance(); private static final SessionRule SESSION_RULE = - SessionRule.builder(CASSANDRA_RESOURCE) + SessionRule.builder(CCM_RULE) .withConfigLoader( SessionUtils.configLoaderBuilder() .withInt(DefaultDriverOption.REQUEST_PAGE_SIZE, 20) @@ -61,7 +60,7 @@ public class SimpleStatementCcmIT { .build(); @ClassRule - public static final TestRule CHAIN = RuleChain.outerRule(CASSANDRA_RESOURCE).around(SESSION_RULE); + public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); @Rule public TestName name = new TestName(); @@ -73,13 +72,8 @@ public static void setupSchema() { SESSION_RULE .session() .execute( - SimpleStatement.builder("DROP TABLE IF EXISTS test") - .setExecutionProfile(SESSION_RULE.slowProfile()) - .build()); - SESSION_RULE - .session() - .execute( - SimpleStatement.builder("CREATE TABLE test (k text, v int, PRIMARY KEY(k, v))") + SimpleStatement.builder( + "CREATE TABLE IF NOT EXISTS test (k text, v int, PRIMARY KEY(k, v))") .setExecutionProfile(SESSION_RULE.slowProfile()) .build()); for (int i = 0; i < 100; i++) { @@ -95,13 +89,7 @@ public static void setupSchema() { SESSION_RULE .session() .execute( - SimpleStatement.builder("DROP TABLE IF EXISTS test2") - .setExecutionProfile(SESSION_RULE.slowProfile()) - .build()); - SESSION_RULE - .session() - .execute( - SimpleStatement.builder("CREATE TABLE test2 (k text primary key, v int)") + SimpleStatement.builder("CREATE TABLE IF NOT EXISTS test2 (k text primary key, v int)") .setExecutionProfile(SESSION_RULE.slowProfile()) .build()); } diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java index 6cfc96559a1..c00cf064e51 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java @@ -31,7 +31,6 @@ import com.datastax.oss.driver.api.core.cql.ExecutionInfo; import com.datastax.oss.driver.api.core.cql.PreparedStatement; import com.datastax.oss.driver.api.core.cql.SimpleStatement; -import com.datastax.oss.driver.api.testinfra.CassandraResourceRule; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; import com.datastax.oss.driver.api.testinfra.session.SessionRule; @@ -57,13 +56,11 @@ @Category(ParallelizableTests.class) public class DefaultReactiveResultSetIT { - private static CassandraResourceRule cassandraResource = CcmRule.getInstance(); + private static CcmRule ccmRule = CcmRule.getInstance(); - private static SessionRule sessionRule = - SessionRule.builder(cassandraResource).build(); + private static SessionRule sessionRule = SessionRule.builder(ccmRule).build(); - @ClassRule - public static TestRule chain = RuleChain.outerRule(cassandraResource).around(sessionRule); + @ClassRule public static TestRule chain = RuleChain.outerRule(ccmRule).around(sessionRule); @BeforeClass public static void initialize() { From 440d65204caed337d79d903c159b61523b3e4bd3 Mon Sep 17 00:00:00 2001 From: janehe Date: Thu, 8 Jan 2026 23:28:46 -0800 Subject: [PATCH 10/27] refactor so Astra extends CCM. Create keyspace for each test suite. --- test-infra/revapi.json | 56 ++++++++++++++-- .../driver/api/testinfra/CassandraBridge.java | 66 ------------------- .../api/testinfra/astra/AstraBridge.java | 17 ++++- .../driver/api/testinfra/astra/AstraRule.java | 10 ++- .../api/testinfra/astra/BaseAstraRule.java | 2 +- .../api/testinfra/astra/CustomAstraRule.java | 2 +- .../driver/api/testinfra/ccm/BaseCcmRule.java | 19 +----- .../driver/api/testinfra/ccm/CcmBridge.java | 11 +--- .../oss/driver/api/testinfra/ccm/CcmRule.java | 24 +++++-- .../api/testinfra/ccm/CustomCcmRule.java | 3 + .../api/testinfra/session/SessionRule.java | 7 ++ 11 files changed, 113 insertions(+), 104 deletions(-) delete mode 100644 test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraBridge.java diff --git a/test-infra/revapi.json b/test-infra/revapi.json index ca41318ab9f..5284a6e30e1 100644 --- a/test-infra/revapi.json +++ b/test-infra/revapi.json @@ -212,10 +212,58 @@ "justification": "Adding methods to support polymorphic backend selection for Astra and CCM tests" }, { - "code": "java.method.returnTypeChanged", - "old": "method com.datastax.oss.driver.api.testinfra.ccm.CcmRule com.datastax.oss.driver.api.testinfra.ccm.CcmRule::getInstance()", - "new": "method com.datastax.oss.driver.api.testinfra.CassandraResourceRule com.datastax.oss.driver.api.testinfra.ccm.CcmRule::getInstance()", - "justification": "CcmRule.getInstance() now returns CassandraResourceRule to support polymorphic backend selection (CCM or Astra) based on ccm.distribution system property" + "ignore": true, + "code": "java.method.visibilityIncreased", + "old": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmRule::()", + "new": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmRule::()", + "oldVisibility": "private", + "newVisibility": "protected", + "justification": "So that AstraRule can extend CcmRule" + }, + { + "ignore": true, + "code": "java.method.visibilityIncreased", + "old": "method void com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule::(com.datastax.oss.driver.api.testinfra.ccm.CcmBridge)", + "new": "method void com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule::(com.datastax.oss.driver.api.testinfra.CassandraBridge)", + "oldVisibility": "package", + "newVisibility": "protected", + "justification": "So that AstraRule can extend CcmRule and pass AstraBridge" + }, + { + "ignore": true, + "code": "java.field.typeChanged", + "old": "field com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule.ccmBridge", + "new": "field com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule.ccmBridge", + "oldType": "com.datastax.oss.driver.api.testinfra.ccm.CcmBridge", + "newType": "com.datastax.oss.driver.api.testinfra.CassandraBridge", + "justification": "So that AstraRule can extend CcmRule and use AstraBridge" + }, + { + "ignore": true, + "code": "java.method.visibilityIncreased", + "old": "method void com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule::(com.datastax.oss.driver.api.testinfra.ccm.CcmBridge)", + "new": "method void com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule::(com.datastax.oss.driver.api.testinfra.ccm.CcmBridge)", + "oldVisibility": "package", + "newVisibility": "protected", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" + }, + { + "ignore": true, + "code": "java.method.visibilityIncreased", + "old": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.Builder::()", + "new": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.Builder::()", + "oldVisibility": "private", + "newVisibility": "protected", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" + }, + { + "ignore": true, + "code": "java.method.visibilityIncreased", + "old": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge::(java.nio.file.Path, int[], java.lang.String, java.util.Map, java.util.Map, java.util.List, java.util.List, java.util.Collection, java.util.List)", + "new": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge::(java.nio.file.Path, int[], java.lang.String, java.util.Map, java.util.Map, java.util.List, java.util.List, java.util.Collection, java.util.List)", + "oldVisibility": "private", + "newVisibility": "protected", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" } ] } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraBridge.java deleted file mode 100644 index a3897aae2e4..00000000000 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/CassandraBridge.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 com.datastax.oss.driver.api.testinfra; - -import com.datastax.oss.driver.api.testinfra.requirement.BackendType; - -/** - * Common interface for Cassandra backend bridges (CCM and Astra). - * - *

This interface defines the lifecycle methods and common operations that both {@link - * com.datastax.oss.driver.api.testinfra.ccm.CcmBridge} and {@link - * com.datastax.oss.driver.api.testinfra.astra.AstraBridge} implement. - */ -public interface CassandraBridge extends AutoCloseable { - - /** - * Creates the Cassandra backend (cluster or database). - * - *

For CCM, this creates a local cluster. For Astra, this creates a cloud database. - */ - void create(); - - /** - * Starts the Cassandra backend. - * - *

For CCM, this starts the cluster nodes. For Astra, this ensures the database is active. - */ - void start(); - - /** - * Stops the Cassandra backend. - * - *

For CCM, this stops the cluster nodes. For Astra, this initiates database termination. - */ - void stop(); - - /** - * Returns the backend type (distribution) of this bridge. - * - * @return the backend type - */ - BackendType getDistribution(); - - /** - * Closes this bridge and releases any resources. - * - *

For CCM, this removes the cluster. For Astra, this terminates the database. - */ - @Override - void close(); -} diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index ece0873bb75..55cf58258d3 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -18,7 +18,7 @@ package com.datastax.oss.driver.api.testinfra.astra; import com.datastax.oss.driver.api.core.Version; -import com.datastax.oss.driver.api.testinfra.CassandraBridge; +import com.datastax.oss.driver.api.testinfra.ccm.CcmBridge; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import java.io.BufferedReader; import java.io.File; @@ -35,7 +35,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class AstraBridge implements CassandraBridge { +public class AstraBridge extends CcmBridge { private static final Logger LOG = LoggerFactory.getLogger(AstraBridge.class); @@ -71,6 +71,16 @@ private AstraBridge( String keyspace, String cloudProvider, String region) { + super( + configDirectory, + new int[] {1}, + "127.0.0", + java.util.Collections.emptyMap(), + java.util.Collections.emptyMap(), + java.util.Collections.emptyList(), + java.util.Collections.emptyList(), + java.util.Collections.emptyList(), + java.util.Collections.emptyList()); this.configDirectory = configDirectory; this.databaseName = databaseName; this.keyspace = keyspace; @@ -96,7 +106,7 @@ public static Version getCassandraVersion() { return Version.parse("4.0.0"); } - public static class Builder { + public static class Builder extends CcmBridge.Builder { private String databaseName = DATABASE_NAME_PREFIX + System.currentTimeMillis(); private String keyspace = DEFAULT_KEYSPACE; private String cloudProvider = ASTRA_CLOUD_PROVIDER; @@ -122,6 +132,7 @@ public Builder withRegion(String region) { return this; } + @Override public AstraBridge build() { try { Path configDir = Files.createTempDirectory("astra-test-"); diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraRule.java index 6b300f7dec2..12640cf6fdf 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraRule.java @@ -31,7 +31,7 @@ */ public class AstraRule extends BaseAstraRule { - private static final AstraRule INSTANCE = new AstraRule(); + private static volatile AstraRule INSTANCE; private volatile boolean started = false; @@ -77,6 +77,14 @@ public void evaluate() { } public static AstraRule getInstance() { + // Lazy initialization to avoid creating AstraBridge when not using Astra + if (INSTANCE == null) { + synchronized (AstraRule.class) { + if (INSTANCE == null) { + INSTANCE = new AstraRule(); + } + } + } return INSTANCE; } } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java index 910cb33e44c..7934cd1c281 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java @@ -51,7 +51,7 @@ public abstract class BaseAstraRule extends CcmRule { } @Override - protected void before() { + protected synchronized void before() { astraBridge.create(); astraBridge.start(); } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java index cc60305ced2..c474d2add61 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/CustomAstraRule.java @@ -40,7 +40,7 @@ public class CustomAstraRule extends BaseAstraRule { } @Override - protected void before() { + protected synchronized void before() { if (CURRENT.get() == null && CURRENT.compareAndSet(null, this)) { try { super.before(); diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/BaseCcmRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/BaseCcmRule.java index 270df44a6fd..63dce14403c 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/BaseCcmRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/BaseCcmRule.java @@ -31,7 +31,7 @@ public abstract class BaseCcmRule extends CassandraResourceRule { protected final CcmBridge ccmBridge; - BaseCcmRule(CcmBridge ccmBridge) { + protected BaseCcmRule(CcmBridge ccmBridge) { this.ccmBridge = ccmBridge; Runtime.getRuntime() .addShutdownHook( @@ -58,19 +58,6 @@ protected void after() { @Override public Statement apply(Statement base, Description description) { - // Skip CCM-specific tests when running with Astra - if (CcmBridge.DISTRIBUTION == BackendType.ASTRA) { - return new Statement() { - @Override - public void evaluate() { - throw new AssumptionViolatedException( - "Test uses CCM-specific rule and cannot run against Astra. " - + "Use CassandraResourceRuleFactory.getInstance() instead of CcmRule or CustomCcmRule " - + "to support both CCM and Astra backends."); - } - }; - } - if (BackendRequirementRule.meetsDescriptionRequirements(description)) { return super.apply(base, description); } else { @@ -87,12 +74,12 @@ public void evaluate() { @Override public BackendType getDistribution() { - return CcmBridge.DISTRIBUTION; + return ccmBridge.getDistribution(); } @Override public boolean isDistributionOf(BackendType type) { - return CcmBridge.isDistributionOf(type); + return ccmBridge.getDistribution() == type; } public boolean isDistributionOf(BackendType type, CcmBridge.VersionComparator comparator) { diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmBridge.java index 31491cf37dc..5ab5f9af7b1 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmBridge.java @@ -18,7 +18,6 @@ package com.datastax.oss.driver.api.testinfra.ccm; import com.datastax.oss.driver.api.core.Version; -import com.datastax.oss.driver.api.testinfra.CassandraBridge; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.shaded.guava.common.base.Joiner; import com.datastax.oss.driver.shaded.guava.common.io.Resources; @@ -52,7 +51,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class CcmBridge implements CassandraBridge { +public class CcmBridge implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(CcmBridge.class); @@ -133,7 +132,7 @@ public class CcmBridge implements CassandraBridge { private final List dseWorkloads; private final String jvmArgs; - private CcmBridge( + protected CcmBridge( Path configDirectory, int[] nodes, String ipPrefix, @@ -216,7 +215,6 @@ private String getCcmVersionString(Version version) { return version.toString(); } - @Override public void create() { if (created.compareAndSet(false, true)) { if (INSTALL_DIRECTORY != null) { @@ -290,7 +288,6 @@ public void reloadCore(int node, String keyspace, String table, boolean reindex) dsetool(node, "reload_core", keyspace + "." + table, "reindex=" + reindex); } - @Override public void start() { if (started.compareAndSet(false, true)) { List cmdAndArgs = Lists.newArrayList("start", jvmArgs, "--wait-for-binary-proto"); @@ -305,7 +302,6 @@ public void start() { } } - @Override public void stop() { if (started.compareAndSet(true, false)) { execute("stop"); @@ -433,7 +429,6 @@ public void close() { } } - @Override public BackendType getDistribution() { return DISTRIBUTION; } @@ -547,7 +542,7 @@ public static class Builder { private final Path configDirectory; - private Builder() { + protected Builder() { try { this.configDirectory = Files.createTempDirectory("ccm"); // mark the ccm temp directories for deletion when the JVM exits diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmRule.java index eefe3054b80..0fadea1679f 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmRule.java @@ -40,7 +40,7 @@ */ public class CcmRule extends BaseCcmRule { - private static final CcmRule CCM_INSTANCE = new CcmRule(); + private static volatile CcmRule CCM_INSTANCE; private static final BackendType DISTRIBUTION = BackendType.valueOf( System.getProperty("ccm.distribution", BackendType.CASSANDRA.name()).toUpperCase()); @@ -51,6 +51,14 @@ protected CcmRule() { super(configureCcmBridge(CcmBridge.builder()).build()); } + /** + * Protected constructor for subclasses (like AstraRule) that want to provide their own bridge + * implementation. + */ + protected CcmRule(CcmBridge bridge) { + super(bridge); + } + public static CcmBridge.Builder configureCcmBridge(CcmBridge.Builder builder) { Logger logger = LoggerFactory.getLogger(CcmRule.class); String customizerClass = @@ -108,21 +116,29 @@ public void evaluate() { } /** - * Returns a singleton instance of a Cassandra resource rule. + * Returns a singleton instance of a CCM rule. * *

The actual implementation returned depends on the {@code ccm.distribution} system property: * *

    - *
  • If set to {@code ASTRA}, returns {@link AstraRule#getInstance()} + *
  • If set to {@code ASTRA}, returns {@link AstraRule#getInstance()} (which extends CcmRule) *
  • Otherwise, returns a {@link CcmRule} instance *
* - * @return a singleton Cassandra resource rule + * @return a singleton CCM rule */ public static CcmRule getInstance() { if (DISTRIBUTION == BackendType.ASTRA) { return AstraRule.getInstance(); } + // Lazy initialization to avoid creating CcmBridge when using Astra + if (CCM_INSTANCE == null) { + synchronized (CcmRule.class) { + if (CCM_INSTANCE == null) { + CCM_INSTANCE = new CcmRule(); + } + } + } return CCM_INSTANCE; } } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CustomCcmRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CustomCcmRule.java index 5ea1bf7ed3c..904ca997b56 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CustomCcmRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CustomCcmRule.java @@ -17,7 +17,9 @@ */ package com.datastax.oss.driver.api.testinfra.ccm; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import java.util.concurrent.atomic.AtomicReference; +import org.junit.Assume; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,6 +43,7 @@ public class CustomCcmRule extends BaseCcmRule { @Override protected void before() { + Assume.assumeTrue("Skipping for Astra", !isDistributionOf(BackendType.ASTRA)); if (CURRENT.get() == null && CURRENT.compareAndSet(null, this)) { try { super.before(); diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java index 075d0204aa6..0c25142c227 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java @@ -36,6 +36,8 @@ import com.datastax.oss.driver.api.testinfra.simulacron.SimulacronRule; import java.util.Objects; import org.junit.rules.ExternalResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Creates and manages a {@link Session} instance for a test. @@ -64,6 +66,7 @@ */ public class SessionRule extends ExternalResource { + private static final Logger LOG = LoggerFactory.getLogger(SessionRule.class); private static final Version V6_8_0 = Objects.requireNonNull(Version.parse("6.8.0")); // the CCM or Simulacron rule to depend on @@ -156,6 +159,10 @@ protected void before() { // Create keyspace if needed if (keyspace != null) { + LOG.warn( + "Creating keyspace: {} with CassandraResource: {}", + keyspace, + cassandraResource.getClass().getSimpleName()); if (cassandraResource instanceof BaseAstraRule) { // For Astra, create keyspace using Astra CLI BaseAstraRule astraRule = (BaseAstraRule) cassandraResource; From f981524f130a1bb4de4bee331d0d0bb4ec93ed4b Mon Sep 17 00:00:00 2001 From: janehe Date: Fri, 9 Jan 2026 12:51:29 -0800 Subject: [PATCH 11/27] can use an existing DB --- .../api/testinfra/astra/AstraBridge.java | 179 ++++++++++++++---- 1 file changed, 141 insertions(+), 38 deletions(-) diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 55cf58258d3..418fdfbea78 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -47,6 +47,9 @@ public class AstraBridge extends CcmBridge { System.getProperty("astra.cloud.provider", "gcp"); private static final String ASTRA_REGION = System.getProperty("astra.region", "us-east1"); + // Existing database ID (if provided, use existing database instead of creating new one) + private static final String ASTRA_DATABASE_ID = System.getProperty("astra.database.id"); + // Database configuration private static final String DATABASE_NAME_PREFIX = "java_driver_test_db_"; private static final String DEFAULT_KEYSPACE = "java_driver_test"; @@ -62,6 +65,9 @@ public class AstraBridge extends CcmBridge { private final AtomicBoolean started = new AtomicBoolean(); private final Path configDirectory; + // Flag to track if we're using an existing database (should not be destroyed) + private final boolean usingExistingDatabase; + private String databaseId; private File secureConnectBundle; @@ -70,7 +76,8 @@ private AstraBridge( String databaseName, String keyspace, String cloudProvider, - String region) { + String region, + String existingDatabaseId) { super( configDirectory, new int[] {1}, @@ -82,10 +89,33 @@ private AstraBridge( java.util.Collections.emptyList(), java.util.Collections.emptyList()); this.configDirectory = configDirectory; - this.databaseName = databaseName; this.keyspace = keyspace; this.cloudProvider = cloudProvider; this.region = region; + this.usingExistingDatabase = existingDatabaseId != null; + this.databaseId = existingDatabaseId; // Will be set if using existing database + + // If using existing database, extract the database name from Astra + if (usingExistingDatabase) { + try { + // Setup Astra CLI with token first + runAstraCommand("setup", "--token", ASTRA_TOKEN); + + // Get database info using CSV output format + String dbInfoOutput = runAstraCommand("db", "get", existingDatabaseId, "-o", "csv"); + this.databaseName = extractDatabaseNameFromCsv(dbInfoOutput); + LOG.info( + "Extracted database name '{}' for existing database ID: {}", + this.databaseName, + existingDatabaseId); + } catch (IOException | InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException( + "Failed to extract database name for existing database ID: " + existingDatabaseId, e); + } + } else { + this.databaseName = databaseName; + } } public static Builder builder() { @@ -111,6 +141,7 @@ public static class Builder extends CcmBridge.Builder { private String keyspace = DEFAULT_KEYSPACE; private String cloudProvider = ASTRA_CLOUD_PROVIDER; private String region = ASTRA_REGION; + private String existingDatabaseId = ASTRA_DATABASE_ID; public Builder withDatabaseName(String databaseName) { this.databaseName = databaseName; @@ -132,11 +163,17 @@ public Builder withRegion(String region) { return this; } + public Builder withExistingDatabaseId(String databaseId) { + this.existingDatabaseId = databaseId; + return this; + } + @Override public AstraBridge build() { try { Path configDir = Files.createTempDirectory("astra-test-"); - return new AstraBridge(configDir, databaseName, keyspace, cloudProvider, region); + return new AstraBridge( + configDir, databaseName, keyspace, cloudProvider, region, existingDatabaseId); } catch (IOException e) { throw new RuntimeException("Failed to create config directory", e); } @@ -147,39 +184,49 @@ public AstraBridge build() { public synchronized void create() { if (created.compareAndSet(false, true)) { try { - LOG.info("Creating Astra database: {}", databaseName); - - // Setup Astra CLI with token - runAstraCommand("setup", "--token", ASTRA_TOKEN); - - // Create database using Astra CLI - // astra db create --no-async --non-vector --if-not-exists -k -r - // - List createArgs = new ArrayList<>(); - createArgs.add("db"); - createArgs.add("create"); - createArgs.add("--no-async"); - createArgs.add("--non-vector"); - createArgs.add("--if-not-exists"); - createArgs.add("-k"); - createArgs.add(keyspace); - createArgs.add("-r"); - createArgs.add(region); - createArgs.add(databaseName); - - String output = runAstraCommand(createArgs.toArray(new String[0])); - LOG.info("Database creation output: {}", output); - - // Get database ID using: astra db get --key id - String dbIdOutput = runAstraCommand("db", "get", databaseName, "--key", "id"); - LOG.info("Database ID output: {}", dbIdOutput); - - // Extract the UUID from the output (filter out [INFO] and other lines) - databaseId = extractDatabaseId(dbIdOutput); - LOG.info("Astra database created with ID: {}", databaseId); - - // Download secure connect bundle - downloadSecureConnectBundle(); + // Setup Astra CLI with token (skip if already done in constructor for existing DB) + if (!usingExistingDatabase) { + runAstraCommand("setup", "--token", ASTRA_TOKEN); + } + + if (usingExistingDatabase) { + LOG.info("Using existing Astra database '{}' with ID: {}", databaseName, databaseId); + + // Download secure connect bundle for existing database + downloadSecureConnectBundleById(); + + } else { + LOG.info("Creating Astra database: {}", databaseName); + + // Create database using Astra CLI + // astra db create --no-async --non-vector --if-not-exists -k -r + // + List createArgs = new ArrayList<>(); + createArgs.add("db"); + createArgs.add("create"); + createArgs.add("--no-async"); + createArgs.add("--non-vector"); + createArgs.add("--if-not-exists"); + createArgs.add("-k"); + createArgs.add(keyspace); + createArgs.add("-r"); + createArgs.add(region); + createArgs.add(databaseName); + + String output = runAstraCommand(createArgs.toArray(new String[0])); + LOG.info("Database creation output: {}", output); + + // Get database ID using: astra db get --key id + String dbIdOutput = runAstraCommand("db", "get", databaseName, "--key", "id"); + LOG.info("Database ID output: {}", dbIdOutput); + + // Extract the UUID from the output (filter out [INFO] and other lines) + databaseId = extractDatabaseId(dbIdOutput); + LOG.info("Astra database created with ID: {}", databaseId); + + // Download secure connect bundle + downloadSecureConnectBundle(); + } } catch (IOException | InterruptedException e) { Thread.currentThread().interrupt(); @@ -205,6 +252,23 @@ private void downloadSecureConnectBundle() throws IOException, InterruptedExcept LOG.info("Secure connect bundle downloaded to: {}", scbFile.getAbsolutePath()); } + private void downloadSecureConnectBundleById() throws IOException, InterruptedException { + // Create SCB directory + Path scbDir = configDirectory.resolve("scb"); + Files.createDirectories(scbDir); + + // Download SCB using database ID: astra db download-scb -f + File scbFile = scbDir.resolve("scb_" + databaseId + ".zip").toFile(); + runAstraCommand("db", "download-scb", databaseId, "-f", scbFile.getAbsolutePath()); + + if (!scbFile.exists()) { + throw new IOException("Secure connect bundle was not downloaded: " + scbFile); + } + + this.secureConnectBundle = scbFile; + LOG.info("Secure connect bundle downloaded by ID to: {}", scbFile.getAbsolutePath()); + } + private static final Pattern UUID_PATTERN = Pattern.compile( "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"); @@ -233,6 +297,40 @@ private String extractDatabaseId(String output) { throw new IllegalStateException("Could not extract database ID from output: " + output); } + /** + * Extract database name from Astra CLI CSV output. The CSV output format is: + * + *
+   * Attribute,Value
+   * Name,java_driver_test_db_1767831564069
+   * id,67750433-fe2e-48d4-bd60-7aef5e76be27
+   * ...
+   * 
+ * + * @param output the CSV output from 'astra db get -o csv' + * @return the database name + */ + private String extractDatabaseNameFromCsv(String output) { + // Use Pattern.compile to split by newline to avoid String.split() warning + String[] lines = Pattern.compile("\n").split(output); + for (String line : lines) { + String trimmed = line.trim(); + // Skip lines that start with [INFO], [OK], [ERROR], etc. + if (trimmed.startsWith("[")) { + continue; + } + // Look for the line that starts with "Name," + if (trimmed.startsWith("Name,")) { + // Extract the value after the comma + String[] parts = trimmed.split(",", 2); + if (parts.length == 2) { + return parts[1].trim(); + } + } + } + throw new IllegalStateException("Could not extract database name from CSV output: " + output); + } + private String runAstraCommand(String... args) throws IOException, InterruptedException { List command = new ArrayList<>(); command.add("astra"); @@ -280,13 +378,18 @@ public synchronized void start() { @Override public synchronized void stop() { - if (databaseName == null) { + if (databaseId == null) { LOG.info("No Astra database to terminate"); return; } + if (usingExistingDatabase) { + LOG.info("Using existing Astra database (ID: {}), skipping deletion", databaseId); + return; + } + try { - LOG.info("Terminating Astra database: {}", databaseName); + LOG.info("Terminating Astra database: {} (ID: {})", databaseName, databaseId); // Delete the database asynchronously (don't wait for completion) String deleteOutput = runAstraCommand("db", "delete", databaseName, "--async"); From 3acdff7d39c93f0c843ba6aa4168caa0b26d912e Mon Sep 17 00:00:00 2001 From: janehe Date: Fri, 9 Jan 2026 12:58:57 -0800 Subject: [PATCH 12/27] skip two tests --- .../java/com/datastax/oss/driver/core/cql/PagingStateIT.java | 3 +++ .../java/com/datastax/oss/driver/core/cql/QueryTraceIT.java | 2 ++ 2 files changed, 5 insertions(+) diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java index 6d33f35238a..dc7ea0ca94e 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PagingStateIT.java @@ -31,12 +31,14 @@ import com.datastax.oss.driver.api.core.type.reflect.GenericType; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; import com.datastax.oss.driver.categories.ParallelizableTests; import com.datastax.oss.driver.internal.core.type.codec.IntCodec; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.function.UnaryOperator; +import org.junit.Assume; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; @@ -108,6 +110,7 @@ private void should_extract_and_reuse(UnaryOperator transformation) @Test public void should_inject_in_simple_statement_with_custom_codecs() { + Assume.assumeFalse("Skipped for Astra", CCM_RULE.isDistributionOf(BackendType.ASTRA)); try (CqlSession session = (CqlSession) SessionUtils.baseBuilder() diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java index 37a600efbc4..708bc3cbd43 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/QueryTraceIT.java @@ -28,6 +28,7 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.metadata.EndPoint; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; @@ -40,6 +41,7 @@ import org.junit.rules.TestRule; @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class QueryTraceIT { private static final CcmRule CCM_RULE = CcmRule.getInstance(); From 09d5fe4ac01a587680817d93fb796ea2a4632765 Mon Sep 17 00:00:00 2001 From: janehe Date: Tue, 20 Jan 2026 11:40:49 -0800 Subject: [PATCH 13/27] Upgrade to the latest astra CLI version. skip some tests. --- .../oss/driver/core/PeersV2NodeRefreshIT.java | 4 + .../driver/core/cql/BoundStatementCcmIT.java | 10 +- .../core/cql/PreparedStatementCachingIT.java | 4 + .../reactive/DefaultReactiveResultSetIT.java | 27 +++++- .../driver/core/metadata/NodeMetadataIT.java | 1 + .../api/testinfra/astra/AstraBridge.java | 97 ++++++++++++++++++- 6 files changed, 127 insertions(+), 16 deletions(-) diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/PeersV2NodeRefreshIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/PeersV2NodeRefreshIT.java index 47f3e3957af..c1df9158a62 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/PeersV2NodeRefreshIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/PeersV2NodeRefreshIT.java @@ -17,11 +17,13 @@ */ package com.datastax.oss.driver.core; +import static com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.isDistributionOf; import static org.assertj.core.api.Assertions.assertThat; import com.datastax.oss.driver.api.core.CqlSession; import com.datastax.oss.driver.api.core.metadata.EndPoint; import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.internal.core.context.InternalDriverContext; import com.datastax.oss.protocol.internal.request.Query; import com.datastax.oss.simulacron.common.cluster.ClusterSpec; @@ -30,6 +32,7 @@ import com.datastax.oss.simulacron.server.Server; import java.util.concurrent.ExecutionException; import org.junit.AfterClass; +import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; @@ -41,6 +44,7 @@ public class PeersV2NodeRefreshIT { @BeforeClass public static void setup() { + Assume.assumeTrue("Skipping for Astra", !isDistributionOf(BackendType.ASTRA)); peersV2Server = Server.builder().withMultipleNodesPerIp(true).build(); cluster = peersV2Server.register(ClusterSpec.builder().withNodes(2)); } diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java index 9e4b62cd230..d192f36301d 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/BoundStatementCcmIT.java @@ -38,6 +38,7 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder; import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.core.metadata.token.Token; +import com.datastax.oss.driver.api.core.session.SessionBuilder; import com.datastax.oss.driver.api.core.type.codec.TypeCodecs; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; @@ -434,12 +435,9 @@ private static void verifyUnset( @SuppressWarnings("unchecked") private CqlSession sessionWithCustomCodec(CqlIntToStringCodec codec) { - return (CqlSession) - SessionUtils.baseBuilder() - .addContactEndPoints(ccmRule.getContactPoints()) - .withKeyspace(sessionRule.keyspace()) - .addTypeCodecs(codec) - .build(); + SessionBuilder builder = + SessionUtils.baseBuilder(ccmRule, sessionRule.keyspace()); + return (CqlSession) builder.addTypeCodecs(codec).build(); } private boolean supportsPerRequestKeyspace(CqlSession session) { diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementCachingIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementCachingIT.java index 617d489fb95..ebb8bd34f16 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementCachingIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/PreparedStatementCachingIT.java @@ -28,8 +28,10 @@ import com.datastax.oss.driver.api.core.metrics.DefaultSessionMetric; import com.datastax.oss.driver.api.core.session.ProgrammaticArguments; import com.datastax.oss.driver.api.core.session.SessionBuilder; +import com.datastax.oss.driver.api.testinfra.ccm.CcmBridge; import com.datastax.oss.driver.api.testinfra.ccm.CustomCcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; import com.datastax.oss.driver.categories.IsolatedTests; @@ -59,6 +61,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import org.junit.AfterClass; +import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; @@ -177,6 +180,7 @@ protected DriverContext buildContext( @BeforeClass public static void setup() { + Assume.assumeTrue("Skipped for Astra", !CcmBridge.isDistributionOf(BackendType.ASTRA)); System.setProperty( SessionUtils.SESSION_BUILDER_CLASS_PROPERTY, PreparedStatementCachingIT.class.getName()); } diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java index c00cf064e51..e051162f25d 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java @@ -307,10 +307,27 @@ private static BatchStatement createCASBatch() { private static void partiallyDeleteInsertedRows() { CqlSession session = sessionRule.session(); - session.execute(" DELETE FROM test_reactive_write WHERE pk = 0 and cc = 5"); - session.execute(" DELETE FROM test_reactive_write WHERE pk = 0 and cc = 6"); - session.execute(" DELETE FROM test_reactive_write WHERE pk = 0 and cc = 7"); - session.execute(" DELETE FROM test_reactive_write WHERE pk = 0 and cc = 8"); - session.execute(" DELETE FROM test_reactive_write WHERE pk = 0 and cc = 9"); + // Use ALL consistency level to ensure deletes are immediately visible across all replicas + // This is important for Astra (distributed system) to avoid eventual consistency issues + session.execute( + SimpleStatement.builder("DELETE FROM test_reactive_write WHERE pk = 0 and cc = 5") + .setConsistencyLevel(com.datastax.oss.driver.api.core.DefaultConsistencyLevel.ALL) + .build()); + session.execute( + SimpleStatement.builder("DELETE FROM test_reactive_write WHERE pk = 0 and cc = 6") + .setConsistencyLevel(com.datastax.oss.driver.api.core.DefaultConsistencyLevel.ALL) + .build()); + session.execute( + SimpleStatement.builder("DELETE FROM test_reactive_write WHERE pk = 0 and cc = 7") + .setConsistencyLevel(com.datastax.oss.driver.api.core.DefaultConsistencyLevel.ALL) + .build()); + session.execute( + SimpleStatement.builder("DELETE FROM test_reactive_write WHERE pk = 0 and cc = 8") + .setConsistencyLevel(com.datastax.oss.driver.api.core.DefaultConsistencyLevel.ALL) + .build()); + session.execute( + SimpleStatement.builder("DELETE FROM test_reactive_write WHERE pk = 0 and cc = 9") + .setConsistencyLevel(com.datastax.oss.driver.api.core.DefaultConsistencyLevel.ALL) + .build()); } } diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/NodeMetadataIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/NodeMetadataIT.java index 8f5680ff41a..a58f69e8bbf 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/NodeMetadataIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/NodeMetadataIT.java @@ -43,6 +43,7 @@ import org.junit.experimental.categories.Category; @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class NodeMetadataIT { @Rule public CcmRule ccmRule = CcmRule.getInstance(); diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 418fdfbea78..2a4aee9effc 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -99,7 +99,14 @@ private AstraBridge( if (usingExistingDatabase) { try { // Setup Astra CLI with token first - runAstraCommand("setup", "--token", ASTRA_TOKEN); + runAstraCommand( + "config", + "create", + "java_driver_test", + "--token", + ASTRA_TOKEN, + "--default", + "--overwrite"); // Get database info using CSV output format String dbInfoOutput = runAstraCommand("db", "get", existingDatabaseId, "-o", "csv"); @@ -186,7 +193,14 @@ public synchronized void create() { try { // Setup Astra CLI with token (skip if already done in constructor for existing DB) if (!usingExistingDatabase) { - runAstraCommand("setup", "--token", ASTRA_TOKEN); + runAstraCommand( + "config", + "create", + "java_driver_test", + "--token", + ASTRA_TOKEN, + "--default", + "--overwrite"); } if (usingExistingDatabase) { @@ -331,7 +345,71 @@ private String extractDatabaseNameFromCsv(String output) { throw new IllegalStateException("Could not extract database name from CSV output: " + output); } + /** + * Checks if the error message indicates a transient error that should be retried. + * + * @param errorMessage the error message from the failed command + * @return true if the error is retryable + */ + private boolean isRetryableAstraError(String errorMessage) { + if (errorMessage == null) { + return false; + } + // Check for invalid state errors (e.g., MAINTENANCE mode) + if (errorMessage.contains("invalid state")) { + return true; + } + // Check for internal HTTP errors + if (errorMessage.contains("INTERNAL_ERROR") && errorMessage.contains("HTTP Request")) { + return true; + } + // Check for HttpEntity errors + if (errorMessage.contains("HttpEntity")) { + return true; + } + return false; + } + private String runAstraCommand(String... args) throws IOException, InterruptedException { + int maxRetries = 3; + int retryDelaySeconds = 10; + IOException lastException = null; + + for (int attempt = 0; attempt <= maxRetries; attempt++) { + try { + return executeAstraCommand(args); + } catch (IOException e) { + lastException = e; + String errorMessage = e.getMessage(); + + // Check if this is a retryable error + if (isRetryableAstraError(errorMessage) && attempt < maxRetries) { + LOG.error( + "Astra CLI command failed with transient error (attempt {}/{}): {}", + attempt + 1, + maxRetries + 1, + errorMessage); + LOG.info("Waiting {} seconds before retry...", retryDelaySeconds); + + try { + Thread.sleep(retryDelaySeconds * 1000L); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new IOException("Interrupted while waiting to retry Astra CLI command", ie); + } + // Continue to next retry iteration + } else { + // Non-retryable error or max retries reached, throw immediately + throw e; + } + } + } + + // Should not reach here, but just in case + throw lastException; + } + + private String executeAstraCommand(String... args) throws IOException, InterruptedException { List command = new ArrayList<>(); command.add("astra"); for (String arg : args) { @@ -432,7 +510,8 @@ public BackendType getDistribution() { } /** - * Creates a new keyspace in the Astra database using the Astra CLI. + * Creates a new keyspace in the Astra database using the Astra CLI. Retries are handled + * automatically by runAstraCommand() for transient errors. * * @param keyspaceName the name of the keyspace to create * @throws RuntimeException if the keyspace creation fails @@ -446,10 +525,18 @@ public void createKeyspace(String keyspaceName) { try { LOG.info("Creating keyspace '{}' in Astra database '{}'", keyspaceName, databaseName); - // Create keyspace using: astra db create-keyspace -k --if-not-exist + // Create keyspace using: astra db create-keyspace -k --if-not-exists + // runAstraCommand() will automatically retry on transient errors String output = runAstraCommand( - "db", "create-keyspace", databaseName, "-k", keyspaceName, "--if-not-exist"); + "db", + "create-keyspace", + databaseName, + "-k", + keyspaceName, + "--if-not-exists", + "--timeout", + "120"); LOG.info("Keyspace creation output: {}", output); LOG.info("Keyspace '{}' created successfully", keyspaceName); From b3aa65e29488b0abe7ac1831e2ac4fa6691d14fb Mon Sep 17 00:00:00 2001 From: janehe Date: Wed, 21 Jan 2026 21:40:15 -0800 Subject: [PATCH 14/27] drop all tables and udts between test suites. skip more tests. --- .../config/DriverExecutionProfileCcmIT.java | 3 + .../reactive/DefaultReactiveResultSetIT.java | 14 +- .../oss/driver/core/data/DataTypeIT.java | 3 + .../core/metadata/CaseSensitiveUdtIT.java | 3 + .../oss/driver/core/metadata/DescribeIT.java | 2 + .../oss/driver/core/metadata/MetadataIT.java | 3 + .../oss/driver/core/metadata/SchemaIT.java | 1 + .../core/session/RequestProcessorIT.java | 3 + .../type/codec/registry/CodecRegistryIT.java | 3 + .../oss/driver/mapper/DefaultKeyspaceIT.java | 3 + .../osgi/OsgiCustomLoadBalancingPolicyIT.java | 3 + .../driver/internal/osgi/OsgiDefaultIT.java | 3 + .../oss/driver/internal/osgi/OsgiLz4IT.java | 3 + .../driver/internal/osgi/OsgiReactiveIT.java | 3 + .../driver/internal/osgi/OsgiShadedIT.java | 3 + test-infra/pom.xml | 6 - test-infra/revapi.json | 74 --------- .../api/testinfra/astra/AstraBridge.java | 115 +++++++++++-- .../api/testinfra/astra/BaseAstraRule.java | 153 +++++++++++++++++- .../requirement/VersionRequirement.java | 20 ++- .../api/testinfra/session/SessionRule.java | 40 +++-- 21 files changed, 351 insertions(+), 110 deletions(-) diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/config/DriverExecutionProfileCcmIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/config/DriverExecutionProfileCcmIT.java index 1eee9c304b6..8500f267da2 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/config/DriverExecutionProfileCcmIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/config/DriverExecutionProfileCcmIT.java @@ -31,6 +31,8 @@ import com.datastax.oss.driver.api.core.cql.PreparedStatement; import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; import com.datastax.oss.driver.categories.ParallelizableTests; import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; @@ -40,6 +42,7 @@ import org.junit.experimental.categories.Category; @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class DriverExecutionProfileCcmIT { @ClassRule public static final CcmRule CCM_RULE = CcmRule.getInstance(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java index e051162f25d..dbc7823efb5 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/cql/reactive/DefaultReactiveResultSetIT.java @@ -22,6 +22,7 @@ import com.datastax.dse.driver.api.core.cql.reactive.ReactiveResultSet; import com.datastax.dse.driver.api.core.cql.reactive.ReactiveRow; import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.DefaultConsistencyLevel; import com.datastax.oss.driver.api.core.config.DefaultDriverOption; import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; import com.datastax.oss.driver.api.core.cql.BatchStatement; @@ -33,6 +34,8 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; import com.datastax.oss.driver.internal.core.cql.EmptyColumnDefinitions; @@ -54,6 +57,7 @@ @RunWith(DataProviderRunner.class) @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class DefaultReactiveResultSetIT { private static CcmRule ccmRule = CcmRule.getInstance(); @@ -311,23 +315,23 @@ private static void partiallyDeleteInsertedRows() { // This is important for Astra (distributed system) to avoid eventual consistency issues session.execute( SimpleStatement.builder("DELETE FROM test_reactive_write WHERE pk = 0 and cc = 5") - .setConsistencyLevel(com.datastax.oss.driver.api.core.DefaultConsistencyLevel.ALL) + .setConsistencyLevel(DefaultConsistencyLevel.ALL) .build()); session.execute( SimpleStatement.builder("DELETE FROM test_reactive_write WHERE pk = 0 and cc = 6") - .setConsistencyLevel(com.datastax.oss.driver.api.core.DefaultConsistencyLevel.ALL) + .setConsistencyLevel(DefaultConsistencyLevel.ALL) .build()); session.execute( SimpleStatement.builder("DELETE FROM test_reactive_write WHERE pk = 0 and cc = 7") - .setConsistencyLevel(com.datastax.oss.driver.api.core.DefaultConsistencyLevel.ALL) + .setConsistencyLevel(DefaultConsistencyLevel.ALL) .build()); session.execute( SimpleStatement.builder("DELETE FROM test_reactive_write WHERE pk = 0 and cc = 8") - .setConsistencyLevel(com.datastax.oss.driver.api.core.DefaultConsistencyLevel.ALL) + .setConsistencyLevel(DefaultConsistencyLevel.ALL) .build()); session.execute( SimpleStatement.builder("DELETE FROM test_reactive_write WHERE pk = 0 and cc = 9") - .setConsistencyLevel(com.datastax.oss.driver.api.core.DefaultConsistencyLevel.ALL) + .setConsistencyLevel(DefaultConsistencyLevel.ALL) .build()); } } diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/data/DataTypeIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/data/DataTypeIT.java index e3d891454de..2f719f3039e 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/data/DataTypeIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/data/DataTypeIT.java @@ -47,6 +47,8 @@ import com.datastax.oss.driver.api.core.type.UserDefinedType; import com.datastax.oss.driver.api.core.type.codec.TypeCodec; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; import com.datastax.oss.driver.internal.core.type.DefaultListType; @@ -89,6 +91,7 @@ @Category(ParallelizableTests.class) @RunWith(DataProviderRunner.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class DataTypeIT { private static final CcmRule CCM_RULE = CcmRule.getInstance(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/CaseSensitiveUdtIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/CaseSensitiveUdtIT.java index f80b02207f8..982e31692e9 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/CaseSensitiveUdtIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/CaseSensitiveUdtIT.java @@ -26,6 +26,8 @@ import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; import com.datastax.oss.driver.api.core.type.UserDefinedType; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; import com.datastax.oss.driver.categories.ParallelizableTests; @@ -46,6 +48,7 @@ * @see JAVA-2028 */ @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class CaseSensitiveUdtIT { private static final CcmRule CCM_RULE = CcmRule.getInstance(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/DescribeIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/DescribeIT.java index 4d6c2a7a3b1..172fb633bb1 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/DescribeIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/DescribeIT.java @@ -29,6 +29,7 @@ import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; @@ -58,6 +59,7 @@ import org.slf4j.LoggerFactory; @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class DescribeIT { private static final Logger LOG = LoggerFactory.getLogger(DescribeIT.class); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/MetadataIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/MetadataIT.java index 1b1aed4b3de..422ee5217f9 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/MetadataIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/MetadataIT.java @@ -23,6 +23,8 @@ import com.datastax.oss.driver.api.core.metadata.Metadata; import com.datastax.oss.driver.api.testinfra.ccm.CcmBridge; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; import org.junit.Rule; @@ -32,6 +34,7 @@ import org.junit.rules.TestRule; @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class MetadataIT { private CcmRule ccmRule = CcmRule.getInstance(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/SchemaIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/SchemaIT.java index df5571974c1..79897664205 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/SchemaIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/SchemaIT.java @@ -77,6 +77,7 @@ public void should_not_expose_system_and_test_keyspace() { } @Test + @BackendRequirement(type = BackendType.ASTRA, include = false) public void should_expose_test_keyspace() { Map keyspaces = sessionRule.session().getMetadata().getKeyspaces(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/session/RequestProcessorIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/session/RequestProcessorIT.java index e2b3caeb1f4..aafdbea7605 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/session/RequestProcessorIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/session/RequestProcessorIT.java @@ -26,6 +26,8 @@ import com.datastax.oss.driver.api.core.cql.PreparedStatement; import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; import com.datastax.oss.driver.example.guava.api.GuavaSession; @@ -64,6 +66,7 @@ * simplifies a certain query down to 1 parameter. */ @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class RequestProcessorIT { private static final CcmRule CCM_RULE = CcmRule.getInstance(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/type/codec/registry/CodecRegistryIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/type/codec/registry/CodecRegistryIT.java index 74472e8bab9..4c746a2d649 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/type/codec/registry/CodecRegistryIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/type/codec/registry/CodecRegistryIT.java @@ -39,6 +39,8 @@ import com.datastax.oss.driver.api.core.type.reflect.GenericType; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; import com.datastax.oss.driver.categories.ParallelizableTests; @@ -66,6 +68,7 @@ import org.junit.rules.TestRule; @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class CodecRegistryIT { private static final CcmRule CCM_RULE = CcmRule.getInstance(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/mapper/DefaultKeyspaceIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/mapper/DefaultKeyspaceIT.java index 30a808e87a9..08eff73d861 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/mapper/DefaultKeyspaceIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/mapper/DefaultKeyspaceIT.java @@ -42,6 +42,8 @@ import com.datastax.oss.driver.api.mapper.annotations.Update; import com.datastax.oss.driver.api.mapper.entity.saving.NullSavingStrategy; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; import java.util.Objects; @@ -54,6 +56,7 @@ import org.junit.rules.TestRule; @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class DefaultKeyspaceIT { private static final String DEFAULT_KEYSPACE = "default_keyspace"; private static final CcmRule CCM_RULE = CcmRule.getInstance(); diff --git a/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiCustomLoadBalancingPolicyIT.java b/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiCustomLoadBalancingPolicyIT.java index 99bd7294934..982b013bad2 100644 --- a/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiCustomLoadBalancingPolicyIT.java +++ b/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiCustomLoadBalancingPolicyIT.java @@ -19,6 +19,8 @@ import com.datastax.oss.driver.api.osgi.service.MailboxService; import com.datastax.oss.driver.api.testinfra.loadbalancing.SortingLoadBalancingPolicy; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.internal.osgi.checks.DefaultServiceChecks; import com.datastax.oss.driver.internal.osgi.support.BundleOptions; import com.datastax.oss.driver.internal.osgi.support.CcmExamReactorFactory; @@ -38,6 +40,7 @@ */ @RunWith(CcmPaxExam.class) @ExamReactorStrategy(CcmExamReactorFactory.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class OsgiCustomLoadBalancingPolicyIT { @Inject MailboxService service; diff --git a/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiDefaultIT.java b/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiDefaultIT.java index a4dec25d96f..26b76c1bbeb 100644 --- a/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiDefaultIT.java +++ b/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiDefaultIT.java @@ -18,6 +18,8 @@ package com.datastax.oss.driver.internal.osgi; import com.datastax.oss.driver.api.osgi.service.MailboxService; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.internal.osgi.checks.DefaultServiceChecks; import com.datastax.oss.driver.internal.osgi.support.BundleOptions; import com.datastax.oss.driver.internal.osgi.support.CcmExamReactorFactory; @@ -32,6 +34,7 @@ @RunWith(CcmPaxExam.class) @ExamReactorStrategy(CcmExamReactorFactory.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class OsgiDefaultIT { @Inject MailboxService service; diff --git a/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiLz4IT.java b/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiLz4IT.java index e8f470d3fdc..f97289443f7 100644 --- a/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiLz4IT.java +++ b/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiLz4IT.java @@ -18,6 +18,8 @@ package com.datastax.oss.driver.internal.osgi; import com.datastax.oss.driver.api.osgi.service.MailboxService; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.internal.osgi.checks.DefaultServiceChecks; import com.datastax.oss.driver.internal.osgi.support.BundleOptions; import com.datastax.oss.driver.internal.osgi.support.CcmExamReactorFactory; @@ -32,6 +34,7 @@ @RunWith(CcmPaxExam.class) @ExamReactorStrategy(CcmExamReactorFactory.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class OsgiLz4IT { @Inject MailboxService service; diff --git a/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiReactiveIT.java b/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiReactiveIT.java index 1710414b67d..51dda5adfd7 100644 --- a/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiReactiveIT.java +++ b/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiReactiveIT.java @@ -21,6 +21,8 @@ import com.datastax.oss.driver.api.osgi.service.MailboxService; import com.datastax.oss.driver.api.osgi.service.reactive.ReactiveMailboxService; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.internal.osgi.checks.DefaultServiceChecks; import com.datastax.oss.driver.internal.osgi.checks.ReactiveServiceChecks; import com.datastax.oss.driver.internal.osgi.support.BundleOptions; @@ -36,6 +38,7 @@ @RunWith(CcmPaxExam.class) @ExamReactorStrategy(CcmExamReactorFactory.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class OsgiReactiveIT { @Inject MailboxService service; diff --git a/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiShadedIT.java b/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiShadedIT.java index 780ed30874d..36754f6808c 100644 --- a/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiShadedIT.java +++ b/osgi-tests/src/test/java/com/datastax/oss/driver/internal/osgi/OsgiShadedIT.java @@ -18,6 +18,8 @@ package com.datastax.oss.driver.internal.osgi; import com.datastax.oss.driver.api.osgi.service.MailboxService; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.internal.osgi.checks.DefaultServiceChecks; import com.datastax.oss.driver.internal.osgi.support.BundleOptions; import com.datastax.oss.driver.internal.osgi.support.CcmExamReactorFactory; @@ -32,6 +34,7 @@ @RunWith(CcmPaxExam.class) @ExamReactorStrategy(CcmExamReactorFactory.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class OsgiShadedIT { @Inject MailboxService service; diff --git a/test-infra/pom.xml b/test-infra/pom.xml index ebe64306b8c..5bf2d07f652 100644 --- a/test-infra/pom.xml +++ b/test-infra/pom.xml @@ -70,12 +70,6 @@ commons-exec true
- - - com.fasterxml.jackson.core - jackson-databind - true - org.awaitility awaitility diff --git a/test-infra/revapi.json b/test-infra/revapi.json index 5284a6e30e1..293d9f4d142 100644 --- a/test-infra/revapi.json +++ b/test-infra/revapi.json @@ -190,80 +190,6 @@ "code": "java.method.removed", "old": "method java.util.Optional com.datastax.oss.driver.api.testinfra.ccm.CcmBridge::getDseVersion()", "justification": "Method has been replaced with more generic isDistributionOf(BackendType) and getDistributionVersion()" - }, - { - "code": "java.method.abstractMethodAdded", - "new": "method com.datastax.oss.driver.api.core.Version com.datastax.oss.driver.api.testinfra.CassandraResourceRule::getCassandraVersion()", - "justification": "Adding methods to support polymorphic backend selection for Astra and CCM tests" - }, - { - "code": "java.method.abstractMethodAdded", - "new": "method com.datastax.oss.driver.api.testinfra.requirement.BackendType com.datastax.oss.driver.api.testinfra.CassandraResourceRule::getDistribution()", - "justification": "Adding methods to support polymorphic backend selection for Astra and CCM tests" - }, - { - "code": "java.method.abstractMethodAdded", - "new": "method com.datastax.oss.driver.api.core.Version com.datastax.oss.driver.api.testinfra.CassandraResourceRule::getDistributionVersion()", - "justification": "Adding methods to support polymorphic backend selection for Astra and CCM tests" - }, - { - "code": "java.method.abstractMethodAdded", - "new": "method boolean com.datastax.oss.driver.api.testinfra.CassandraResourceRule::isDistributionOf(com.datastax.oss.driver.api.testinfra.requirement.BackendType)", - "justification": "Adding methods to support polymorphic backend selection for Astra and CCM tests" - }, - { - "ignore": true, - "code": "java.method.visibilityIncreased", - "old": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmRule::()", - "new": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmRule::()", - "oldVisibility": "private", - "newVisibility": "protected", - "justification": "So that AstraRule can extend CcmRule" - }, - { - "ignore": true, - "code": "java.method.visibilityIncreased", - "old": "method void com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule::(com.datastax.oss.driver.api.testinfra.ccm.CcmBridge)", - "new": "method void com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule::(com.datastax.oss.driver.api.testinfra.CassandraBridge)", - "oldVisibility": "package", - "newVisibility": "protected", - "justification": "So that AstraRule can extend CcmRule and pass AstraBridge" - }, - { - "ignore": true, - "code": "java.field.typeChanged", - "old": "field com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule.ccmBridge", - "new": "field com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule.ccmBridge", - "oldType": "com.datastax.oss.driver.api.testinfra.ccm.CcmBridge", - "newType": "com.datastax.oss.driver.api.testinfra.CassandraBridge", - "justification": "So that AstraRule can extend CcmRule and use AstraBridge" - }, - { - "ignore": true, - "code": "java.method.visibilityIncreased", - "old": "method void com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule::(com.datastax.oss.driver.api.testinfra.ccm.CcmBridge)", - "new": "method void com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule::(com.datastax.oss.driver.api.testinfra.ccm.CcmBridge)", - "oldVisibility": "package", - "newVisibility": "protected", - "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" - }, - { - "ignore": true, - "code": "java.method.visibilityIncreased", - "old": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.Builder::()", - "new": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.Builder::()", - "oldVisibility": "private", - "newVisibility": "protected", - "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" - }, - { - "ignore": true, - "code": "java.method.visibilityIncreased", - "old": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge::(java.nio.file.Path, int[], java.lang.String, java.util.Map, java.util.Map, java.util.List, java.util.List, java.util.Collection, java.util.List)", - "new": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge::(java.nio.file.Path, int[], java.lang.String, java.util.Map, java.util.Map, java.util.List, java.util.List, java.util.Collection, java.util.List)", - "oldVisibility": "private", - "newVisibility": "protected", - "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" } ] } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 2a4aee9effc..8db318c7323 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -17,9 +17,15 @@ */ package com.datastax.oss.driver.api.testinfra.astra; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.CqlSession; import com.datastax.oss.driver.api.core.Version; +import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; +import com.datastax.oss.driver.api.core.type.UserDefinedType; import com.datastax.oss.driver.api.testinfra.ccm.CcmBridge; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; +import com.datastax.oss.driver.shaded.guava.common.base.Splitter; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -28,8 +34,12 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; @@ -82,12 +92,12 @@ private AstraBridge( configDirectory, new int[] {1}, "127.0.0", - java.util.Collections.emptyMap(), - java.util.Collections.emptyMap(), - java.util.Collections.emptyList(), - java.util.Collections.emptyList(), - java.util.Collections.emptyList(), - java.util.Collections.emptyList()); + Collections.emptyMap(), + Collections.emptyMap(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList()); this.configDirectory = configDirectory; this.keyspace = keyspace; this.cloudProvider = cloudProvider; @@ -333,9 +343,21 @@ private String extractDatabaseNameFromCsv(String output) { if (trimmed.startsWith("[")) { continue; } - // Look for the line that starts with "Name," - if (trimmed.startsWith("Name,")) { - // Extract the value after the comma + // Skip the CSV header line + if (trimmed.startsWith("code,message,attribute,value")) { + continue; + } + // Look for the line with "Name" in the attribute column + // Format: OK,,Name, or Name, (for older CLI versions) + if (trimmed.contains(",Name,")) { + // Split by comma and get the last part (the value) + List parts = Splitter.on(',').splitToList(trimmed); + if (parts.size() >= 2) { + // Return the last part which is the database name + return parts.get(parts.size() - 1).trim(); + } + } else if (trimmed.startsWith("Name,")) { + // Handle older CSV format: Name, String[] parts = trimmed.split(",", 2); if (parts.length == 2) { return parts[1].trim(); @@ -546,4 +568,79 @@ public void createKeyspace(String keyspaceName) { "Failed to create keyspace '" + keyspaceName + "' in database '" + databaseName + "'", e); } } + + /** + * Drops all user-created tables in the specified keyspace. This is used to clean up after test + * suites when running against Astra, where creating/dropping keyspaces is expensive. + * + *

System tables (those starting with "system") are not dropped. + * + * @param keyspaceName the name of the keyspace to clean + * @param session the CQL session to use for dropping tables + */ + public void dropAllTablesInKeyspace(String keyspaceName, CqlSession session) { + if (databaseName == null) { + throw new IllegalStateException( + "Cannot drop tables: Astra database has not been created yet"); + } + + try { + LOG.info( + "Dropping all tables in keyspace '{}' of Astra database '{}'", + keyspaceName, + databaseName); + + // Get keyspace metadata from the driver + Optional keyspaceMetadata = session.getMetadata().getKeyspace(keyspaceName); + + if (!keyspaceMetadata.isPresent()) { + LOG.error("Keyspace '{}' not found in metadata", keyspaceName); + return; + } + + // Get all tables from the keyspace metadata + Map tables = keyspaceMetadata.get().getTables(); + Map udts = keyspaceMetadata.get().getUserDefinedTypes(); + + AtomicInteger droppedCount = new AtomicInteger(); + for (TableMetadata tableMetadata : tables.values()) { + String tableName = tableMetadata.getName().asInternal(); + if (!tableName.startsWith("system")) { + session + .executeAsync(String.format("DROP TABLE IF EXISTS %s.%s", keyspaceName, tableName)) + .whenComplete( + (rs, err) -> { + if (err != null) { + LOG.info( + "Failed to drop table '{}.{}': {}", + keyspaceName, + tableName, + err.getMessage()); + } else { + droppedCount.getAndIncrement(); + LOG.info("Dropped table '{}.{}'", keyspaceName, tableName); + } + }); + } + } + + for (UserDefinedType udt : udts.values()) { + String udtName = udt.getName().asInternal(); + session + .executeAsync(String.format("DROP TYPE IF EXISTS %s.%s", keyspaceName, udtName)) + .whenComplete( + (rs, err) -> { + if (err != null) { + LOG.info( + "Failed to drop type '{}.{}': {}", keyspaceName, udtName, err.getMessage()); + } else { + LOG.info("Dropped type '{}.{}'", keyspaceName, udtName); + } + }); + } + } catch (Exception e) { + LOG.error("Failed to drop tables in keyspace '{}': {}", keyspaceName, e.getMessage(), e); + // Don't throw - this is cleanup, we don't want to fail tests because of cleanup issues + } + } } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java index 7934cd1c281..bf247a018cf 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java @@ -17,6 +17,7 @@ */ package com.datastax.oss.driver.api.testinfra.astra; +import com.datastax.oss.driver.api.core.CqlSession; import com.datastax.oss.driver.api.core.DefaultProtocolVersion; import com.datastax.oss.driver.api.core.ProtocolVersion; import com.datastax.oss.driver.api.core.Version; @@ -30,11 +31,21 @@ import org.junit.AssumptionViolatedException; import org.junit.runner.Description; import org.junit.runners.model.Statement; +import org.slf4j.LoggerFactory; public abstract class BaseAstraRule extends CcmRule { protected final AstraBridge astraBridge; + // Reusable session for table cleanup operations + private volatile CqlSession cleanupSession; + + // Track the current test class and method count for @Rule cleanup + private volatile Class currentTestClass; + private volatile int totalTestMethods; + private volatile int completedTestMethods; + private final Object testTrackingLock = new Object(); + BaseAstraRule(AstraBridge astraBridge) { super(); this.astraBridge = astraBridge; @@ -43,6 +54,7 @@ public abstract class BaseAstraRule extends CcmRule { new Thread( () -> { try { + closeCleanupSession(); astraBridge.close(); } catch (Exception e) { // silently remove as may have already been removed. @@ -58,13 +70,86 @@ protected synchronized void before() { @Override protected void after() { - astraBridge.close(); + // Check if we need to drop tables after the last test method in a @Rule scenario + synchronized (testTrackingLock) { + completedTestMethods++; + LoggerFactory.getLogger(BaseAstraRule.class) + .error( + "Test method completed: {}/{} in class {}", + completedTestMethods, + totalTestMethods, + currentTestClass != null ? currentTestClass.getSimpleName() : "null"); + + if (currentTestClass != null && completedTestMethods >= totalTestMethods) { + // Last test method in the class has completed, drop all tables + LoggerFactory.getLogger(BaseAstraRule.class) + .error( + "Last test method completed in class {}, dropping all tables in keyspace '{}'", + currentTestClass.getSimpleName(), + astraBridge.getKeyspace()); + try { + dropAllTablesInKeyspace(); + } finally { + // Reset tracking for the next test class + currentTestClass = null; + totalTestMethods = 0; + completedTestMethods = 0; + } + } + } + + // Note: We don't call closeCleanupSession() or astraBridge.close() here + // because the rule is reused across test classes (singleton pattern) } @Override public Statement apply(Statement base, Description description) { if (BackendRequirementRule.meetsDescriptionRequirements(description)) { - return super.apply(base, description); + // Determine if this is a class-level rule (@ClassRule) or method-level rule (@Rule) + boolean isClassRule = description.isTest() == false; + + if (isClassRule) { + // @ClassRule: Wrap the base statement to drop all tables after test suite execution + Statement wrappedStatement = + new Statement() { + @Override + public void evaluate() throws Throwable { + try { + base.evaluate(); + } finally { + // Drop all tables in the keyspace after the test suite completes + dropAllTablesInKeyspace(); + } + } + }; + return super.apply(wrappedStatement, description); + } else { + // @Rule: Track test class and method count for cleanup after last test method + Class testClass = description.getTestClass(); + synchronized (testTrackingLock) { + if (currentTestClass != testClass) { + // New test class, reset tracking + currentTestClass = testClass; + completedTestMethods = 0; + // Count total test methods in this class + totalTestMethods = (int) description.getTestClass().getMethods().length; + // More accurate: count only @Test methods + totalTestMethods = 0; + for (java.lang.reflect.Method method : testClass.getMethods()) { + if (method.isAnnotationPresent(org.junit.Test.class)) { + totalTestMethods++; + } + } + LoggerFactory.getLogger(BaseAstraRule.class) + .error( + "Starting new test class {} with {} test methods (using @Rule)", + testClass.getSimpleName(), + totalTestMethods); + } + } + // Don't drop tables after each test method - cleanup happens in after() + return super.apply(base, description); + } } else { // requirements not met, throw reasoning assumption to skip test return new Statement() { @@ -77,6 +162,70 @@ public void evaluate() { } } + /** + * Gets or creates a reusable CQL session for cleanup operations. This session is created lazily + * and reused across all table cleanup operations to avoid the overhead of creating/closing + * sessions after each test suite. + * + * @return a CQL session connected to the Astra database + */ + private synchronized CqlSession getOrCreateCleanupSession() { + if (cleanupSession == null || cleanupSession.isClosed()) { + String keyspaceName = astraBridge.getKeyspace(); + cleanupSession = + CqlSession.builder() + .withCloudSecureConnectBundle(astraBridge.getSecureConnectBundle().toPath()) + .withAuthCredentials("token", astraBridge.getToken()) + .withKeyspace(keyspaceName) + .build(); + LoggerFactory.getLogger(BaseAstraRule.class) + .error("Created reusable cleanup session for keyspace '{}'", keyspaceName); + } + return cleanupSession; + } + + /** + * Closes the cleanup session if it exists. This is called when the rule is done (either in + * after() or in the shutdown hook). + */ + private synchronized void closeCleanupSession() { + if (cleanupSession != null && !cleanupSession.isClosed()) { + try { + LoggerFactory.getLogger(BaseAstraRule.class) + .error("Closing cleanup session for keyspace '{}'", astraBridge.getKeyspace()); + cleanupSession.close(); + } catch (Exception e) { + LoggerFactory.getLogger(BaseAstraRule.class) + .error("Failed to close cleanup session: {}", e.getMessage(), e); + } + cleanupSession = null; + } + } + + /** + * Drops all user-created tables in the keyspace after a test suite completes. This is called + * automatically after each test class when running against Astra to avoid the expensive operation + * of creating/dropping keyspaces. + * + *

This method uses a reusable session that is created once and reused across all cleanup + * operations. + */ + protected void dropAllTablesInKeyspace() { + String keyspaceName = astraBridge.getKeyspace(); + if (keyspaceName == null || keyspaceName.isEmpty()) { + return; // No keyspace to clean + } + + try { + CqlSession session = getOrCreateCleanupSession(); + astraBridge.dropAllTablesInKeyspace(keyspaceName, session); + } catch (Exception e) { + // Log but don't fail - this is cleanup + LoggerFactory.getLogger(BaseAstraRule.class) + .error("Failed to drop tables in keyspace '{}': {}", keyspaceName, e.getMessage(), e); + } + } + @Override public BackendType getDistribution() { return AstraBridge.DISTRIBUTION; diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/VersionRequirement.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/VersionRequirement.java index 7105537a48f..cde9cba75d2 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/VersionRequirement.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/requirement/VersionRequirement.java @@ -120,7 +120,8 @@ public static VersionRequirement fromDseRequirement(DseRequirement requirement) } public static Collection fromAnnotations(Description description) { - // collect all requirement annotation types + // collect all requirement annotation types from both the method and the class + // (description.getAnnotation() only checks the method when using @Rule) CassandraRequirement cassandraRequirement = description.getAnnotation(CassandraRequirement.class); DseRequirement dseRequirement = description.getAnnotation(DseRequirement.class); @@ -129,6 +130,23 @@ public static Collection fromAnnotations(Description descrip // matches methods/classes with two or more @BackendRequirement annotations BackendRequirements backendRequirements = description.getAnnotation(BackendRequirements.class); + // Also check the test class for annotations (needed when using @Rule instead of @ClassRule) + Class testClass = description.getTestClass(); + if (testClass != null) { + if (cassandraRequirement == null) { + cassandraRequirement = testClass.getAnnotation(CassandraRequirement.class); + } + if (dseRequirement == null) { + dseRequirement = testClass.getAnnotation(DseRequirement.class); + } + if (backendRequirement == null) { + backendRequirement = testClass.getAnnotation(BackendRequirement.class); + } + if (backendRequirements == null) { + backendRequirements = testClass.getAnnotation(BackendRequirements.class); + } + } + // build list of required versions Collection requirements = new ArrayList<>(); if (cassandraRequirement != null) { diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java index 0c25142c227..c95389daaba 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/session/SessionRule.java @@ -104,12 +104,22 @@ public SessionRule( this.cassandraResource = cassandraResource; this.nodeStateListener = nodeStateListener; this.schemaChangeListener = schemaChangeListener; - // Generate a unique keyspace for all backends except Simulacron (when createKeyspace is true) - // For Simulacron or when createKeyspace is false, don't generate a keyspace - this.keyspace = - (cassandraResource instanceof SimulacronRule || !createKeyspace) - ? null - : SessionUtils.uniqueKeyspaceId(); + // Determine keyspace based on backend type: + // - Simulacron: no keyspace (null) + // - Astra: use shared keyspace from AstraBridge (when createKeyspace is true) + // - CCM/other: generate unique keyspace (when createKeyspace is true) + // - When createKeyspace is false: no keyspace (null) + if (!createKeyspace || cassandraResource instanceof SimulacronRule) { + this.keyspace = null; + } else if (cassandraResource instanceof BaseAstraRule) { + // For Astra, use the shared keyspace from AstraBridge + BaseAstraRule astraRule = (BaseAstraRule) cassandraResource; + String sharedKeyspace = astraRule.getAstraBridge().getKeyspace(); + this.keyspace = sharedKeyspace != null ? CqlIdentifier.fromCql(sharedKeyspace) : null; + } else { + // For CCM and other backends, generate a unique keyspace + this.keyspace = SessionUtils.uniqueKeyspaceId(); + } this.configLoader = configLoader; this.graphName = graphName; this.isCoreGraph = isCoreGraph; @@ -159,16 +169,20 @@ protected void before() { // Create keyspace if needed if (keyspace != null) { - LOG.warn( - "Creating keyspace: {} with CassandraResource: {}", - keyspace, - cassandraResource.getClass().getSimpleName()); if (cassandraResource instanceof BaseAstraRule) { - // For Astra, create keyspace using Astra CLI + // For Astra, the shared keyspace already exists - just switch to it BaseAstraRule astraRule = (BaseAstraRule) cassandraResource; - astraRule.getAstraBridge().createKeyspace(keyspace.asInternal()); + String sharedKeyspace = astraRule.getAstraBridge().getKeyspace(); + LOG.warn( + "Using shared Astra keyspace: {} with CassandraResource: {}", + sharedKeyspace, + cassandraResource.getClass().getSimpleName()); } else { - // For CCM and other backends, create keyspace using CQL + // For CCM and other backends, create a unique keyspace using CQL + LOG.warn( + "Creating keyspace: {} with CassandraResource: {}", + keyspace, + cassandraResource.getClass().getSimpleName()); SessionUtils.createKeyspace(session, keyspace, slowProfile); } // Switch to the keyspace From 75514708c7de67d2db8a74aba95b38d26766adc4 Mon Sep 17 00:00:00 2001 From: janehe Date: Thu, 22 Jan 2026 20:19:23 -0800 Subject: [PATCH 15/27] run parallelable tests sequentially when running against Astra --- integration-tests/pom.xml | 28 +++++- .../oss/driver/core/metadata/SchemaIT.java | 1 + .../core/tracker/RequestIdGeneratorIT.java | 3 + .../mapper/QueryKeyspaceAndTableIT.java | 3 + test-infra/revapi.json | 60 ++++++++++++ .../api/testinfra/astra/AstraBridge.java | 56 +++++------ .../api/testinfra/astra/BaseAstraRule.java | 95 +++---------------- 7 files changed, 134 insertions(+), 112 deletions(-) diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index e302e12077f..d500c6e7296 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -33,6 +33,9 @@ ${skipITs} ${skipITs} ${skipITs} + + classes + 8 @@ -238,8 +241,10 @@ ${testing.jvm}/bin/java com.datastax.oss.driver.categories.ParallelizableTests - classes - 8 + + ${astra.parallel.mode} + ${astra.thread.count} ${project.build.directory}/failsafe-reports/failsafe-summary-parallelized.xml ${skipParallelizableITs} ${blockhound.argline} @@ -335,4 +340,23 @@ + + + + astra-sequential + + + ccm.distribution + Astra + + + + + none + 1 + + + diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/SchemaIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/SchemaIT.java index 79897664205..0cc1debf8ec 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/SchemaIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/metadata/SchemaIT.java @@ -85,6 +85,7 @@ public void should_expose_test_keyspace() { } @Test + @BackendRequirement(type = BackendType.ASTRA, include = false) public void should_filter_by_keyspaces() { DriverConfigLoader loader = SessionUtils.configLoaderBuilder() diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/core/tracker/RequestIdGeneratorIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/core/tracker/RequestIdGeneratorIT.java index 516a62bb1f7..6946e858ac6 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/core/tracker/RequestIdGeneratorIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/core/tracker/RequestIdGeneratorIT.java @@ -29,6 +29,8 @@ import com.datastax.oss.driver.api.core.session.Request; import com.datastax.oss.driver.api.core.tracker.RequestIdGenerator; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionUtils; import com.datastax.oss.driver.categories.ParallelizableTests; import com.datastax.oss.protocol.internal.util.collection.NullAllowingImmutableMap; @@ -43,6 +45,7 @@ import org.junit.rules.TestRule; @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) // TODO: Run with Astra public class RequestIdGeneratorIT { private CcmRule ccmRule = CcmRule.getInstance(); diff --git a/integration-tests/src/test/java/com/datastax/oss/driver/mapper/QueryKeyspaceAndTableIT.java b/integration-tests/src/test/java/com/datastax/oss/driver/mapper/QueryKeyspaceAndTableIT.java index 9391c0363f8..7a5a598d796 100644 --- a/integration-tests/src/test/java/com/datastax/oss/driver/mapper/QueryKeyspaceAndTableIT.java +++ b/integration-tests/src/test/java/com/datastax/oss/driver/mapper/QueryKeyspaceAndTableIT.java @@ -33,6 +33,8 @@ import com.datastax.oss.driver.api.mapper.annotations.Query; import com.datastax.oss.driver.api.mapper.entity.saving.NullSavingStrategy; import com.datastax.oss.driver.api.testinfra.ccm.CcmRule; +import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; +import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.api.testinfra.session.SessionRule; import com.datastax.oss.driver.categories.ParallelizableTests; import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; @@ -45,6 +47,7 @@ /** Covers the keyspace and table placeholders in {@link Query} methods. */ @Category(ParallelizableTests.class) +@BackendRequirement(type = BackendType.ASTRA, include = false) public class QueryKeyspaceAndTableIT { private static final CcmRule CCM_RULE = CcmRule.getInstance(); diff --git a/test-infra/revapi.json b/test-infra/revapi.json index 293d9f4d142..bf1e9a66ff4 100644 --- a/test-infra/revapi.json +++ b/test-infra/revapi.json @@ -190,6 +190,66 @@ "code": "java.method.removed", "old": "method java.util.Optional com.datastax.oss.driver.api.testinfra.ccm.CcmBridge::getDseVersion()", "justification": "Method has been replaced with more generic isDistributionOf(BackendType) and getDistributionVersion()" + }, + { + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method com.datastax.oss.driver.api.core.Version com.datastax.oss.driver.api.testinfra.CassandraResourceRule::getCassandraVersion()", + "justification": " CASSJAVA-120: ADD ASTRA INTEGRATION TESTS INFRASTRUCTURE" + }, + { + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method com.datastax.oss.driver.api.testinfra.requirement.BackendType com.datastax.oss.driver.api.testinfra.CassandraResourceRule::getDistribution()", + "justification": " CASSJAVA-120: ADD ASTRA INTEGRATION TESTS INFRASTRUCTURE" + }, + { + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method com.datastax.oss.driver.api.core.Version com.datastax.oss.driver.api.testinfra.CassandraResourceRule::getDistributionVersion()", + "justification": " CASSJAVA-120: ADD ASTRA INTEGRATION TESTS INFRASTRUCTURE" + }, + { + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method boolean com.datastax.oss.driver.api.testinfra.CassandraResourceRule::isDistributionOf(com.datastax.oss.driver.api.testinfra.requirement.BackendType)", + "justification": " CASSJAVA-120: ADD ASTRA INTEGRATION TESTS INFRASTRUCTURE" + }, + { + "ignore": true, + "code": "java.method.visibilityIncreased", + "old": "method void com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule::(com.datastax.oss.driver.api.testinfra.ccm.CcmBridge)", + "new": "method void com.datastax.oss.driver.api.testinfra.ccm.BaseCcmRule::(com.datastax.oss.driver.api.testinfra.ccm.CcmBridge)", + "oldVisibility": "package", + "newVisibility": "protected", + "justification": " CASSJAVA-120: ADD ASTRA INTEGRATION TESTS INFRASTRUCTURE" + }, + { + "ignore": true, + "code": "java.method.visibilityIncreased", + "old": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.Builder::()", + "new": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge.Builder::()", + "oldVisibility": "private", + "newVisibility": "protected", + "justification": " CASSJAVA-120: ADD ASTRA INTEGRATION TESTS INFRASTRUCTURE" + }, + { + "ignore": true, + "code": "java.method.visibilityIncreased", + "old": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge::(java.nio.file.Path, int[], java.lang.String, java.util.Map, java.util.Map, java.util.List, java.util.List, java.util.Collection, java.util.List)", + "new": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmBridge::(java.nio.file.Path, int[], java.lang.String, java.util.Map, java.util.Map, java.util.List, java.util.List, java.util.Collection, java.util.List)", + "oldVisibility": "private", + "newVisibility": "protected", + "justification": " CASSJAVA-120: ADD ASTRA INTEGRATION TESTS INFRASTRUCTURE" + }, + { + "ignore": true, + "code": "java.method.visibilityIncreased", + "old": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmRule::()", + "new": "method void com.datastax.oss.driver.api.testinfra.ccm.CcmRule::()", + "oldVisibility": "private", + "newVisibility": "protected", + "justification": " CASSJAVA-120: ADD ASTRA INTEGRATION TESTS INFRASTRUCTURE" } ] } diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 8db318c7323..7e6ead6d194 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -19,7 +19,9 @@ import com.datastax.oss.driver.api.core.CqlIdentifier; import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.DriverException; import com.datastax.oss.driver.api.core.Version; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; import com.datastax.oss.driver.api.core.type.UserDefinedType; @@ -33,6 +35,8 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -598,45 +602,43 @@ public void dropAllTablesInKeyspace(String keyspaceName, CqlSession session) { return; } + // Tried the fan out pattern to drop tables and types concurrently, + // It didn't work well because the drop table queries easily time out + // Get all tables from the keyspace metadata Map tables = keyspaceMetadata.get().getTables(); - Map udts = keyspaceMetadata.get().getUserDefinedTypes(); AtomicInteger droppedCount = new AtomicInteger(); for (TableMetadata tableMetadata : tables.values()) { String tableName = tableMetadata.getName().asInternal(); if (!tableName.startsWith("system")) { - session - .executeAsync(String.format("DROP TABLE IF EXISTS %s.%s", keyspaceName, tableName)) - .whenComplete( - (rs, err) -> { - if (err != null) { - LOG.info( - "Failed to drop table '{}.{}': {}", - keyspaceName, - tableName, - err.getMessage()); - } else { - droppedCount.getAndIncrement(); - LOG.info("Dropped table '{}.{}'", keyspaceName, tableName); - } - }); + try { + session.execute( + SimpleStatement.newInstance( + String.format("DROP TABLE IF EXISTS %s.%s", keyspaceName, tableName)) + .setTimeout(Duration.of(20, ChronoUnit.SECONDS))); + droppedCount.getAndIncrement(); + LOG.info("Dropped table '{}.{}'", keyspaceName, tableName); + } catch (DriverException e) { + LOG.error("Failed to drop table '{}.{}': {}", keyspaceName, tableName, e.getMessage()); + } + session.execute( + SimpleStatement.newInstance( + String.format("DROP TABLE IF EXISTS %s.%s", keyspaceName, tableName)) + .setTimeout(Duration.of(20, ChronoUnit.SECONDS))); } } + LOG.info("Dropped {} tables in keyspace '{}'", droppedCount.get(), keyspaceName); + + Map udts = keyspaceMetadata.get().getUserDefinedTypes(); for (UserDefinedType udt : udts.values()) { String udtName = udt.getName().asInternal(); - session - .executeAsync(String.format("DROP TYPE IF EXISTS %s.%s", keyspaceName, udtName)) - .whenComplete( - (rs, err) -> { - if (err != null) { - LOG.info( - "Failed to drop type '{}.{}': {}", keyspaceName, udtName, err.getMessage()); - } else { - LOG.info("Dropped type '{}.{}'", keyspaceName, udtName); - } - }); + try { + session.execute(String.format("DROP TYPE IF EXISTS %s.%s", keyspaceName, udtName)); + } catch (DriverException e) { + LOG.error("Failed to drop type '{}.{}': {}", keyspaceName, udtName, e.getMessage()); + } } } catch (Exception e) { LOG.error("Failed to drop tables in keyspace '{}': {}", keyspaceName, e.getMessage(), e); diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java index bf247a018cf..b49f7c3e6ca 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/BaseAstraRule.java @@ -40,12 +40,6 @@ public abstract class BaseAstraRule extends CcmRule { // Reusable session for table cleanup operations private volatile CqlSession cleanupSession; - // Track the current test class and method count for @Rule cleanup - private volatile Class currentTestClass; - private volatile int totalTestMethods; - private volatile int completedTestMethods; - private final Object testTrackingLock = new Object(); - BaseAstraRule(AstraBridge astraBridge) { super(); this.astraBridge = astraBridge; @@ -68,88 +62,23 @@ protected synchronized void before() { astraBridge.start(); } - @Override - protected void after() { - // Check if we need to drop tables after the last test method in a @Rule scenario - synchronized (testTrackingLock) { - completedTestMethods++; - LoggerFactory.getLogger(BaseAstraRule.class) - .error( - "Test method completed: {}/{} in class {}", - completedTestMethods, - totalTestMethods, - currentTestClass != null ? currentTestClass.getSimpleName() : "null"); - - if (currentTestClass != null && completedTestMethods >= totalTestMethods) { - // Last test method in the class has completed, drop all tables - LoggerFactory.getLogger(BaseAstraRule.class) - .error( - "Last test method completed in class {}, dropping all tables in keyspace '{}'", - currentTestClass.getSimpleName(), - astraBridge.getKeyspace()); - try { - dropAllTablesInKeyspace(); - } finally { - // Reset tracking for the next test class - currentTestClass = null; - totalTestMethods = 0; - completedTestMethods = 0; - } - } - } - - // Note: We don't call closeCleanupSession() or astraBridge.close() here - // because the rule is reused across test classes (singleton pattern) - } - @Override public Statement apply(Statement base, Description description) { if (BackendRequirementRule.meetsDescriptionRequirements(description)) { - // Determine if this is a class-level rule (@ClassRule) or method-level rule (@Rule) - boolean isClassRule = description.isTest() == false; - - if (isClassRule) { - // @ClassRule: Wrap the base statement to drop all tables after test suite execution - Statement wrappedStatement = - new Statement() { - @Override - public void evaluate() throws Throwable { - try { - base.evaluate(); - } finally { - // Drop all tables in the keyspace after the test suite completes - dropAllTablesInKeyspace(); - } - } - }; - return super.apply(wrappedStatement, description); - } else { - // @Rule: Track test class and method count for cleanup after last test method - Class testClass = description.getTestClass(); - synchronized (testTrackingLock) { - if (currentTestClass != testClass) { - // New test class, reset tracking - currentTestClass = testClass; - completedTestMethods = 0; - // Count total test methods in this class - totalTestMethods = (int) description.getTestClass().getMethods().length; - // More accurate: count only @Test methods - totalTestMethods = 0; - for (java.lang.reflect.Method method : testClass.getMethods()) { - if (method.isAnnotationPresent(org.junit.Test.class)) { - totalTestMethods++; + // @ClassRule: Wrap the base statement to drop all tables after test suite execution + Statement wrappedStatement = + new Statement() { + @Override + public void evaluate() throws Throwable { + try { + base.evaluate(); + } finally { + // Drop all tables in the keyspace after the test suite completes + dropAllTablesInKeyspace(); } } - LoggerFactory.getLogger(BaseAstraRule.class) - .error( - "Starting new test class {} with {} test methods (using @Rule)", - testClass.getSimpleName(), - totalTestMethods); - } - } - // Don't drop tables after each test method - cleanup happens in after() - return super.apply(base, description); - } + }; + return super.apply(wrappedStatement, description); } else { // requirements not met, throw reasoning assumption to skip test return new Statement() { From 5d0459e1d0464707580c94a9490d82266ba7e1ec Mon Sep 17 00:00:00 2001 From: janehe Date: Tue, 27 Jan 2026 14:48:03 -0800 Subject: [PATCH 16/27] green run --- .../api/testinfra/astra/AstraBridge.java | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 7e6ead6d194..a4b11637071 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -602,40 +602,51 @@ public void dropAllTablesInKeyspace(String keyspaceName, CqlSession session) { return; } - // Tried the fan out pattern to drop tables and types concurrently, - // It didn't work well because the drop table queries easily time out - - // Get all tables from the keyspace metadata + // Get all tables and UDTs from the keyspace metadata Map tables = keyspaceMetadata.get().getTables(); + Map udts = keyspaceMetadata.get().getUserDefinedTypes(); - AtomicInteger droppedCount = new AtomicInteger(); + // IMPORTANT: Drop tables FIRST, then UDTs + // UDTs cannot be dropped while tables are still using them + + // Step 1: Drop all tables + AtomicInteger droppedTableCount = new AtomicInteger(); for (TableMetadata tableMetadata : tables.values()) { - String tableName = tableMetadata.getName().asInternal(); + CqlIdentifier tableId = tableMetadata.getName(); + String tableName = tableId.asInternal(); if (!tableName.startsWith("system")) { try { + // IMPORTANT: Use the CqlIdentifier to properly quote the table name + // Tables created with quotes (e.g., "UPPER_CASE") need quotes to drop + String dropStatement = + String.format("DROP TABLE IF EXISTS %s.%s", keyspaceName, tableId.asCql(true)); session.execute( - SimpleStatement.newInstance( - String.format("DROP TABLE IF EXISTS %s.%s", keyspaceName, tableName)) + SimpleStatement.newInstance(dropStatement) .setTimeout(Duration.of(20, ChronoUnit.SECONDS))); - droppedCount.getAndIncrement(); - LOG.info("Dropped table '{}.{}'", keyspaceName, tableName); + droppedTableCount.getAndIncrement(); + LOG.info("Dropped table '{}.{}' using: {}", keyspaceName, tableName, dropStatement); } catch (DriverException e) { LOG.error("Failed to drop table '{}.{}': {}", keyspaceName, tableName, e.getMessage()); } - session.execute( - SimpleStatement.newInstance( - String.format("DROP TABLE IF EXISTS %s.%s", keyspaceName, tableName)) - .setTimeout(Duration.of(20, ChronoUnit.SECONDS))); } } - LOG.info("Dropped {} tables in keyspace '{}'", droppedCount.get(), keyspaceName); + LOG.info("Dropped {} tables in keyspace '{}'", droppedTableCount.get(), keyspaceName); - Map udts = keyspaceMetadata.get().getUserDefinedTypes(); + // Step 2: Drop all UDTs (after tables are dropped) + AtomicInteger droppedUdtCount = new AtomicInteger(); for (UserDefinedType udt : udts.values()) { - String udtName = udt.getName().asInternal(); + CqlIdentifier udtId = udt.getName(); + String udtName = udtId.asInternal(); try { - session.execute(String.format("DROP TYPE IF EXISTS %s.%s", keyspaceName, udtName)); + // IMPORTANT: Use the CqlIdentifier to properly quote the UDT name + String dropStatement = + String.format("DROP TYPE IF EXISTS %s.%s", keyspaceName, udtId.asCql(true)); + session.execute( + SimpleStatement.newInstance(dropStatement) + .setTimeout(Duration.of(20, ChronoUnit.SECONDS))); + droppedUdtCount.getAndIncrement(); + LOG.info("Dropped type '{}.{}' using: {}", keyspaceName, udtName, dropStatement); } catch (DriverException e) { LOG.error("Failed to drop type '{}.{}': {}", keyspaceName, udtName, e.getMessage()); } From 05e14bc3bd8f8305ea7d4ec78101c649ad0e63f1 Mon Sep 17 00:00:00 2001 From: janehe Date: Thu, 29 Jan 2026 16:14:43 -0800 Subject: [PATCH 17/27] Jenkinsfile-astra --- Jenkinsfile-astra | 191 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 Jenkinsfile-astra diff --git a/Jenkinsfile-astra b/Jenkinsfile-astra new file mode 100644 index 00000000000..8054e99a3a6 --- /dev/null +++ b/Jenkinsfile-astra @@ -0,0 +1,191 @@ +#!groovy +/* + * 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. + */ + +def initializeEnvironment() { + env.DRIVER_DISPLAY_NAME = 'Java Driver for Apache Cassandra® - Astra Tests' + env.DRIVER_METRIC_TYPE = 'oss' + + env.GIT_SHA = "${env.GIT_COMMIT.take(7)}" + env.GITHUB_PROJECT_URL = "https://${GIT_URL.replaceFirst(/(git@|http:\/\/|https:\/\/)/, '').replace(':', '/').replace('.git', '')}" + env.GITHUB_BRANCH_URL = "${GITHUB_PROJECT_URL}/tree/${env.BRANCH_NAME}" + env.GITHUB_COMMIT_URL = "${GITHUB_PROJECT_URL}/commit/${env.GIT_COMMIT}" + + env.MAVEN_HOME = "${env.HOME}/.mvn/apache-maven-3.8.8" + env.PATH = "${env.MAVEN_HOME}/bin:${env.PATH}" + + env.JAVA_HOME = sh(label: 'Get JAVA_HOME',script: '''#!/bin/bash -le + . ${JABBA_SHELL} + jabba which 1.8''', returnStdout: true).trim() + + sh label: 'Display Java and environment information',script: '''#!/bin/bash -le + . ${JABBA_SHELL} + jabba use 1.8 + + java -version + mvn -v + printenv | sort + ''' +} + +def buildDriver() { + sh label: 'Build driver', script: '''#!/bin/bash -le + . ${JABBA_SHELL} + jabba use 1.8 + + echo "Building with Java version 1.8" + + mvn -B -V install -DskipTests -Dmaven.javadoc.skip=true + ''' +} + +def executeAstraTests() { + withCredentials([ + string(credentialsId: 'astra-database-id', variable: 'ASTRA_DB_ID'), + string(credentialsId: 'astra-token', variable: 'ASTRA_TOKEN') + ]) { + sh label: 'Execute Astra integration tests', script: '''#!/bin/bash -le + . ${JABBA_SHELL} + jabba use 1.8 + + printenv | sort + + mvn -B -V verify \ + -DfailIfNoTests=false \ + -Dmaven.test.failure.ignore=true \ + -Dmaven.javadoc.skip=true \ + -Dccm.distribution=Astra \ + -Dastra.database.id=${ASTRA_DB_ID} \ + -Dastra.token=${ASTRA_TOKEN} + ''' + } +} + +def notifySlack(status = 'started') { + def color = 'good' // Green + if (status.equalsIgnoreCase('aborted')) { + color = '808080' // Grey + } else if (status.equalsIgnoreCase('unstable')) { + color = 'warning' // Orange + } else if (status.equalsIgnoreCase('failed')) { + color = 'danger' // Red + } + + def message = """Build ${status} for ${env.DRIVER_DISPLAY_NAME} +<${env.GITHUB_BRANCH_URL}|${env.BRANCH_NAME}> - <${env.RUN_DISPLAY_URL}|#${env.BUILD_NUMBER}> - <${env.GITHUB_COMMIT_URL}|${env.GIT_SHA}>""" + if (!status.equalsIgnoreCase('Started')) { + message += """ +${status} after ${currentBuild.durationString - ' and counting'}""" + } + + slackSend color: "${color}", + channel: "#java-driver-dev-bots", + message: "${message}" +} + +// branch pattern for cron +// should match 3.x, 4.x, 4.5.x, etc +def branchPatternCron() { + ~"((\\d+(\\.[\\dx]+)+))" +} + +pipeline { + agent { + label 'ubuntu/focal64/java-driver' + } + + // Global pipeline timeout + options { + timeout(time: 2, unit: 'HOURS') + buildDiscarder(logRotator(artifactNumToKeepStr: '10', // Keep only the last 10 artifacts + numToKeepStr: '50')) // Keep only the last 50 build records + } + + triggers { + // Run Astra tests weekly on release branches + cron(branchPatternCron().matcher(env.BRANCH_NAME).matches() ? '@weekly' : '') + } + + environment { + JABBA_SHELL = '/usr/lib/jabba/jabba.sh' + SERIAL_ITS_ARGUMENT = "-DskipSerialITs=${params.SKIP_SERIAL_ITS}" + ISOLATED_ITS_ARGUMENT = "-DskipIsolatedITs=${params.SKIP_ISOLATED_ITS}" + PARALLELIZABLE_ITS_ARGUMENT = "-DskipParallelizableITs=${params.SKIP_PARALLELIZABLE_ITS}" + INTEGRATION_TESTS_FILTER = "${params.INTEGRATION_TESTS_FILTER}" + } + + stages { + stage('Initialize-Environment') { + steps { + initializeEnvironment() + notifySlack() + } + } + + stage('Describe-Build') { + steps { + script { + currentBuild.displayName = "Astra Integration Tests" + currentBuild.description = "Running integration tests against DataStax Astra" + } + } + } + + stage('Build-Driver') { + steps { + buildDriver() + } + } + + stage('Execute-Astra-Tests') { + steps { + catchError { + executeAstraTests() + } + } + post { + always { + /* + * Empty results are possible + * + * - Build failures during mvn verify may exist so report may not be available + * - With boolean parameters to skip tests a failsafe report may not be available + */ + junit testResults: '**/target/surefire-reports/TEST-*.xml', allowEmptyResults: true + junit testResults: '**/target/failsafe-reports/TEST-*.xml', allowEmptyResults: true + } + } + } + } + + post { + aborted { + notifySlack('aborted') + } + success { + notifySlack('completed') + } + unstable { + notifySlack('unstable') + } + failure { + notifySlack('FAILED') + } + } +} From 57332034d1af340cccc948d8c56eaefff485c080 Mon Sep 17 00:00:00 2001 From: janehe Date: Thu, 29 Jan 2026 16:20:54 -0800 Subject: [PATCH 18/27] remove database ID --- Jenkinsfile-astra | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile-astra b/Jenkinsfile-astra index 8054e99a3a6..d91b1b77f5f 100644 --- a/Jenkinsfile-astra +++ b/Jenkinsfile-astra @@ -40,6 +40,7 @@ def initializeEnvironment() { java -version mvn -v + astra help printenv | sort ''' } @@ -57,8 +58,7 @@ def buildDriver() { def executeAstraTests() { withCredentials([ - string(credentialsId: 'astra-database-id', variable: 'ASTRA_DB_ID'), - string(credentialsId: 'astra-token', variable: 'ASTRA_TOKEN') + string(credentialsId: 'astra-integration-test-token', variable: 'ASTRA_TOKEN') ]) { sh label: 'Execute Astra integration tests', script: '''#!/bin/bash -le . ${JABBA_SHELL} @@ -71,7 +71,6 @@ def executeAstraTests() { -Dmaven.test.failure.ignore=true \ -Dmaven.javadoc.skip=true \ -Dccm.distribution=Astra \ - -Dastra.database.id=${ASTRA_DB_ID} \ -Dastra.token=${ASTRA_TOKEN} ''' } From a48421ccf66814539e8f4ac3b828bc7848ae0a07 Mon Sep 17 00:00:00 2001 From: janehe Date: Thu, 29 Jan 2026 16:23:58 -0800 Subject: [PATCH 19/27] remove cron --- Jenkinsfile-astra | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Jenkinsfile-astra b/Jenkinsfile-astra index d91b1b77f5f..17ceda511d6 100644 --- a/Jenkinsfile-astra +++ b/Jenkinsfile-astra @@ -98,12 +98,6 @@ ${status} after ${currentBuild.durationString - ' and counting'}""" message: "${message}" } -// branch pattern for cron -// should match 3.x, 4.x, 4.5.x, etc -def branchPatternCron() { - ~"((\\d+(\\.[\\dx]+)+))" -} - pipeline { agent { label 'ubuntu/focal64/java-driver' @@ -116,11 +110,6 @@ pipeline { numToKeepStr: '50')) // Keep only the last 50 build records } - triggers { - // Run Astra tests weekly on release branches - cron(branchPatternCron().matcher(env.BRANCH_NAME).matches() ? '@weekly' : '') - } - environment { JABBA_SHELL = '/usr/lib/jabba/jabba.sh' SERIAL_ITS_ARGUMENT = "-DskipSerialITs=${params.SKIP_SERIAL_ITS}" From cc6ceaa686f8db9b662486ac8e774ed88d66d15c Mon Sep 17 00:00:00 2001 From: "Siyao (Jane) He" Date: Tue, 24 Feb 2026 00:09:00 -0800 Subject: [PATCH 20/27] Update Jenkinsfile-astra --- Jenkinsfile-astra | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile-astra b/Jenkinsfile-astra index 17ceda511d6..01dc76df915 100644 --- a/Jenkinsfile-astra +++ b/Jenkinsfile-astra @@ -28,7 +28,7 @@ def initializeEnvironment() { env.GITHUB_COMMIT_URL = "${GITHUB_PROJECT_URL}/commit/${env.GIT_COMMIT}" env.MAVEN_HOME = "${env.HOME}/.mvn/apache-maven-3.8.8" - env.PATH = "${env.MAVEN_HOME}/bin:${env.PATH}" + env.PATH = "${env.MAVEN_HOME}/bin:~/.astra/cli:${env.PATH}" env.JAVA_HOME = sh(label: 'Get JAVA_HOME',script: '''#!/bin/bash -le . ${JABBA_SHELL} @@ -40,8 +40,8 @@ def initializeEnvironment() { java -version mvn -v - astra help printenv | sort + astra help ''' } From b9f5de79f29fc7f07e40e4e32d7a3074ff73ce09 Mon Sep 17 00:00:00 2001 From: janehe Date: Tue, 24 Feb 2026 12:54:57 -0800 Subject: [PATCH 21/27] add logging --- Jenkinsfile-astra | 2 +- .../datastax/oss/driver/api/testinfra/astra/AstraBridge.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile-astra b/Jenkinsfile-astra index 01dc76df915..a262da08aaa 100644 --- a/Jenkinsfile-astra +++ b/Jenkinsfile-astra @@ -60,7 +60,7 @@ def executeAstraTests() { withCredentials([ string(credentialsId: 'astra-integration-test-token', variable: 'ASTRA_TOKEN') ]) { - sh label: 'Execute Astra integration tests', script: '''#!/bin/bash -le + sh label: 'Execute Astra integration tests', script: '''#!/bin/bash -lex . ${JABBA_SHELL} jabba use 1.8 diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index a4b11637071..c8fcc8b2bc9 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -442,10 +442,9 @@ private String executeAstraCommand(String... args) throws IOException, Interrupt command.add(arg); } - LOG.info("Running Astra CLI command: {}", String.join(" ", command)); - ProcessBuilder pb = new ProcessBuilder(command); pb.redirectErrorStream(true); + LOG.info("Running Astra CLI command: {} with environment: {}", String.join(" ", command), pb.environment().toString()); Process process = pb.start(); StringBuilder output = new StringBuilder(); From f2493c3eb96ea5aeb93b16a3e8766ba0f2f77e56 Mon Sep 17 00:00:00 2001 From: janehe Date: Tue, 24 Feb 2026 15:31:14 -0800 Subject: [PATCH 22/27] fmt --- .../datastax/oss/driver/api/testinfra/astra/AstraBridge.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index c8fcc8b2bc9..2ccf1616262 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -444,7 +444,10 @@ private String executeAstraCommand(String... args) throws IOException, Interrupt ProcessBuilder pb = new ProcessBuilder(command); pb.redirectErrorStream(true); - LOG.info("Running Astra CLI command: {} with environment: {}", String.join(" ", command), pb.environment().toString()); + LOG.info( + "Running Astra CLI command: {} with environment: {}", + String.join(" ", command), + pb.environment().toString()); Process process = pb.start(); StringBuilder output = new StringBuilder(); From de3e6f56f678fa121e7fee28a009d97b253f9287 Mon Sep 17 00:00:00 2001 From: janehe Date: Tue, 24 Feb 2026 16:42:56 -0800 Subject: [PATCH 23/27] change to apache executor --- .../api/testinfra/astra/AstraBridge.java | 84 ++++++++++++------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 2ccf1616262..378fe7a269a 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -28,11 +28,8 @@ import com.datastax.oss.driver.api.testinfra.ccm.CcmBridge; import com.datastax.oss.driver.api.testinfra.requirement.BackendType; import com.datastax.oss.driver.shaded.guava.common.base.Splitter; -import java.io.BufferedReader; import java.io.File; import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; @@ -42,10 +39,18 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.exec.CommandLine; +import org.apache.commons.exec.DefaultExecutor; +import org.apache.commons.exec.ExecuteStreamHandler; +import org.apache.commons.exec.ExecuteWatchdog; +import org.apache.commons.exec.Executor; +import org.apache.commons.exec.LogOutputStream; +import org.apache.commons.exec.PumpStreamHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -436,40 +441,59 @@ private String runAstraCommand(String... args) throws IOException, InterruptedEx } private String executeAstraCommand(String... args) throws IOException, InterruptedException { - List command = new ArrayList<>(); - command.add("astra"); + // Build command line + CommandLine cli = new CommandLine("astra"); for (String arg : args) { - command.add(arg); + cli.addArgument(arg); } - ProcessBuilder pb = new ProcessBuilder(command); - pb.redirectErrorStream(true); - LOG.info( - "Running Astra CLI command: {} with environment: {}", - String.join(" ", command), - pb.environment().toString()); - Process process = pb.start(); + LOG.info("Running Astra CLI command: {}", cli.toString()); + // StringBuilder to collect output StringBuilder output = new StringBuilder(); - try (BufferedReader reader = - new BufferedReader( - new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { - String line; - while ((line = reader.readLine()) != null) { - output.append(line).append("\n"); - LOG.info("Astra CLI: {}", line); + + // Create watchdog with 10-minute timeout (same as CcmBridge) + ExecuteWatchdog watchDog = new ExecuteWatchdog(TimeUnit.MINUTES.toMillis(10)); + + try (LogOutputStream outStream = + new LogOutputStream() { + @Override + protected void processLine(String line, int logLevel) { + output.append(line).append("\n"); + LOG.debug("astraout> {}", line); + } + }; + LogOutputStream errStream = + new LogOutputStream() { + @Override + protected void processLine(String line, int logLevel) { + output.append(line).append("\n"); + LOG.error("astraerr> {}", line); + } + }) { + + Executor executor = new DefaultExecutor(); + ExecuteStreamHandler streamHandler = new PumpStreamHandler(outStream, errStream); + executor.setStreamHandler(streamHandler); + executor.setWatchdog(watchDog); + + int retValue = executor.execute(cli); + + if (retValue != 0) { + throw new IOException( + "Astra CLI command failed with exit code " + + retValue + + ": " + + cli.toString() + + "\nOutput: " + + output); } - } - int exitCode = process.waitFor(); - if (exitCode != 0) { - throw new IOException( - "Astra CLI command failed with exit code " - + exitCode - + ": " - + String.join(" ", command) - + "\nOutput: " - + output); + } catch (IOException e) { + if (watchDog.killedProcess()) { + throw new IOException("Astra CLI command timed out after 10 minutes: " + cli.toString(), e); + } + throw e; } return output.toString(); From fa48c84747dc1af0a3262f4f96b76547e2bd20d9 Mon Sep 17 00:00:00 2001 From: janehe Date: Tue, 24 Feb 2026 18:41:43 -0800 Subject: [PATCH 24/27] CommandLine("/home/jenkins/.astra/cli/astra"); --- .../datastax/oss/driver/api/testinfra/astra/AstraBridge.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 378fe7a269a..20c6218fb5c 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -442,7 +442,7 @@ private String runAstraCommand(String... args) throws IOException, InterruptedEx private String executeAstraCommand(String... args) throws IOException, InterruptedException { // Build command line - CommandLine cli = new CommandLine("astra"); + CommandLine cli = new CommandLine("/home/jenkins/.astra/cli/astra"); for (String arg : args) { cli.addArgument(arg); } From c8ae163df10aa43d80cab2902c658ef0ea11bfb7 Mon Sep 17 00:00:00 2001 From: janehe Date: Tue, 24 Feb 2026 19:19:07 -0800 Subject: [PATCH 25/27] back to astra --- Jenkinsfile-astra | 2 +- .../datastax/oss/driver/api/testinfra/astra/AstraBridge.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile-astra b/Jenkinsfile-astra index a262da08aaa..7499cd988e8 100644 --- a/Jenkinsfile-astra +++ b/Jenkinsfile-astra @@ -28,7 +28,7 @@ def initializeEnvironment() { env.GITHUB_COMMIT_URL = "${GITHUB_PROJECT_URL}/commit/${env.GIT_COMMIT}" env.MAVEN_HOME = "${env.HOME}/.mvn/apache-maven-3.8.8" - env.PATH = "${env.MAVEN_HOME}/bin:~/.astra/cli:${env.PATH}" + env.PATH = "${env.MAVEN_HOME}/bin:/home/jenkins/.astra/cli:${env.PATH}" env.JAVA_HOME = sh(label: 'Get JAVA_HOME',script: '''#!/bin/bash -le . ${JABBA_SHELL} diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 20c6218fb5c..378fe7a269a 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -442,7 +442,7 @@ private String runAstraCommand(String... args) throws IOException, InterruptedEx private String executeAstraCommand(String... args) throws IOException, InterruptedException { // Build command line - CommandLine cli = new CommandLine("/home/jenkins/.astra/cli/astra"); + CommandLine cli = new CommandLine("astra"); for (String arg : args) { cli.addArgument(arg); } From 286add311ed47038e313bba1cbce52a431bf9983 Mon Sep 17 00:00:00 2001 From: janehe Date: Tue, 24 Feb 2026 23:10:16 -0800 Subject: [PATCH 26/27] toURI --- Jenkinsfile-astra | 2 +- Jenkinsfile-datastax | 2 +- .../ssl/ReloadingKeyManagerFactoryTest.java | 23 ++++++++++++++----- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Jenkinsfile-astra b/Jenkinsfile-astra index 7499cd988e8..1156a10a0fb 100644 --- a/Jenkinsfile-astra +++ b/Jenkinsfile-astra @@ -100,7 +100,7 @@ ${status} after ${currentBuild.durationString - ' and counting'}""" pipeline { agent { - label 'ubuntu/focal64/java-driver' + label 'ubuntu/jammy64/java-driver' } // Global pipeline timeout diff --git a/Jenkinsfile-datastax b/Jenkinsfile-datastax index 602f33101ca..9a7b7923f32 100644 --- a/Jenkinsfile-datastax +++ b/Jenkinsfile-datastax @@ -402,7 +402,7 @@ pipeline { } environment { - OS_VERSION = 'ubuntu/focal64/java-driver' + OS_VERSION = 'ubuntu/jammy64/java-driver' JABBA_SHELL = '/usr/lib/jabba/jabba.sh' CCM_ENVIRONMENT_SHELL = '/usr/local/bin/ccm_environment.sh' SERIAL_ITS_ARGUMENT = "-DskipSerialITs=${params.SKIP_SERIAL_ITS}" diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/ssl/ReloadingKeyManagerFactoryTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/ssl/ReloadingKeyManagerFactoryTest.java index d07b45c21df..e0ba675b846 100644 --- a/core/src/test/java/com/datastax/oss/driver/internal/core/ssl/ReloadingKeyManagerFactoryTest.java +++ b/core/src/test/java/com/datastax/oss/driver/internal/core/ssl/ReloadingKeyManagerFactoryTest.java @@ -26,6 +26,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketException; +import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -55,12 +56,22 @@ public class ReloadingKeyManagerFactoryTest { private static final Logger logger = LoggerFactory.getLogger(ReloadingKeyManagerFactoryTest.class); - static final Path CERT_BASE = - Paths.get( - ReloadingKeyManagerFactoryTest.class - .getResource( - String.format("/%s/certs/", ReloadingKeyManagerFactoryTest.class.getSimpleName())) - .getPath()); + static final Path CERT_BASE; + + static { + try { + CERT_BASE = + Paths.get( + ReloadingKeyManagerFactoryTest.class + .getResource( + String.format( + "/%s/certs/", ReloadingKeyManagerFactoryTest.class.getSimpleName())) + .toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + static final Path SERVER_KEYSTORE_PATH = CERT_BASE.resolve("server.keystore"); static final Path SERVER_TRUSTSTORE_PATH = CERT_BASE.resolve("server.truststore"); From 5c22bfcb96c8b03c0cf0fe91f0f57eb8a77a58a9 Mon Sep 17 00:00:00 2001 From: janehe Date: Thu, 26 Feb 2026 16:35:00 -0800 Subject: [PATCH 27/27] db delete --yes --- .../src/test/resources/logback-test.xml | 21 ++++---- .../api/testinfra/astra/AstraBridge.java | 48 +------------------ 2 files changed, 12 insertions(+), 57 deletions(-) diff --git a/integration-tests/src/test/resources/logback-test.xml b/integration-tests/src/test/resources/logback-test.xml index 1dc34c20c6c..745d4003f84 100644 --- a/integration-tests/src/test/resources/logback-test.xml +++ b/integration-tests/src/test/resources/logback-test.xml @@ -24,14 +24,15 @@ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - + + + + + + + + + + + diff --git a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java index 378fe7a269a..c85b9288fff 100644 --- a/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java +++ b/test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/astra/AstraBridge.java @@ -522,7 +522,7 @@ public synchronized void stop() { LOG.info("Terminating Astra database: {} (ID: {})", databaseName, databaseId); // Delete the database asynchronously (don't wait for completion) - String deleteOutput = runAstraCommand("db", "delete", databaseName, "--async"); + String deleteOutput = runAstraCommand("db", "delete", databaseName, "--yes"); LOG.info("Database deletion initiated: {}", deleteOutput); LOG.info("Astra database {} (ID: {}) is being terminated", databaseName, databaseId); } catch (Exception e) { @@ -536,14 +536,6 @@ public void close() { stop(); } - public String getDatabaseName() { - return databaseName; - } - - public String getDatabaseId() { - return databaseId; - } - public String getKeyspace() { return keyspace; } @@ -561,44 +553,6 @@ public BackendType getDistribution() { return DISTRIBUTION; } - /** - * Creates a new keyspace in the Astra database using the Astra CLI. Retries are handled - * automatically by runAstraCommand() for transient errors. - * - * @param keyspaceName the name of the keyspace to create - * @throws RuntimeException if the keyspace creation fails - */ - public void createKeyspace(String keyspaceName) { - if (databaseName == null) { - throw new IllegalStateException( - "Cannot create keyspace: Astra database has not been created yet"); - } - - try { - LOG.info("Creating keyspace '{}' in Astra database '{}'", keyspaceName, databaseName); - - // Create keyspace using: astra db create-keyspace -k --if-not-exists - // runAstraCommand() will automatically retry on transient errors - String output = - runAstraCommand( - "db", - "create-keyspace", - databaseName, - "-k", - keyspaceName, - "--if-not-exists", - "--timeout", - "120"); - LOG.info("Keyspace creation output: {}", output); - LOG.info("Keyspace '{}' created successfully", keyspaceName); - - } catch (IOException | InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException( - "Failed to create keyspace '" + keyspaceName + "' in database '" + databaseName + "'", e); - } - } - /** * Drops all user-created tables in the specified keyspace. This is used to clean up after test * suites when running against Astra, where creating/dropping keyspaces is expensive.