details;
+ boolean enabled;
+}
diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeLocationFilter.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeLocationFilter.java
new file mode 100644
index 00000000..9e27babb
--- /dev/null
+++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeLocationFilter.java
@@ -0,0 +1,15 @@
+package com.dtsx.astra.sdk.pcu.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PCUTypeLocationFilter {
+ String provider;
+ String region;
+}
diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeResolver.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeResolver.java
new file mode 100644
index 00000000..e393c61e
--- /dev/null
+++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeResolver.java
@@ -0,0 +1,58 @@
+package com.dtsx.astra.sdk.pcu.domain;
+
+import java.util.List;
+
+/**
+ * Utility class for resolving default PCU types based on availability and configuration.
+ */
+public class PCUTypeResolver {
+
+ /**
+ * Resolves the default PCU type based on available types and mini PCU configuration.
+ *
+ * Selection priority when mini PCU is enabled:
+ * SMALL > MEDIUM > GENERAL_PURPOSE > CACHE_OPTIMIZED
+ *
+ *
Selection priority when mini PCU is disabled:
+ * GENERAL_PURPOSE > CACHE_OPTIMIZED (SMALL and MEDIUM are ignored)
+ *
+ * @param availableTypes list of available PCU family types
+ * @param miniPcuEnabled whether mini PCU is enabled
+ * @return the selected PCU family, or null if no suitable type is available
+ */
+ public static PCUInstanceType resolveDefaultPcuType(List availableTypes, boolean miniPcuEnabled) {
+ if (availableTypes == null || availableTypes.isEmpty()) {
+ return null;
+ }
+
+ if (miniPcuEnabled) {
+ // Priority: SMALL > MEDIUM > GENERAL_PURPOSE > CACHE_OPTIMIZED
+ if (availableTypes.contains(PCUInstanceType.small)) {
+ return PCUInstanceType.small;
+ }
+ if (availableTypes.contains(PCUInstanceType.medium)) {
+ return PCUInstanceType.medium;
+ }
+ if (availableTypes.contains(PCUInstanceType.generalPurpose)) {
+ return PCUInstanceType.generalPurpose;
+ }
+ if (availableTypes.contains(PCUInstanceType.cacheOptimized)) {
+ return PCUInstanceType.cacheOptimized;
+ }
+ } else {
+ // Priority: GENERAL_PURPOSE > CACHE_OPTIMIZED (ignore SMALL and MEDIUM)
+ if (availableTypes.contains(PCUInstanceType.generalPurpose)) {
+ return PCUInstanceType.generalPurpose;
+ }
+ if (availableTypes.contains(PCUInstanceType.cacheOptimized)) {
+ return PCUInstanceType.cacheOptimized;
+ }
+ }
+
+ return null;
+ }
+
+ private PCUTypeResolver() {
+ // Utility class - prevent instantiation
+ }
+}
diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroup.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroup.java
deleted file mode 100644
index 0e6d47e8..00000000
--- a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroup.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.dtsx.astra.sdk.pcu.domain;
-
-import com.dtsx.astra.sdk.db.domain.CloudProviderType;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-/**
- * Represents a PCU (Processing Capacity Units) Group in Astra.
- * A PCU group manages compute resources for databases across cloud providers and regions.
- */
-@Data
-@NoArgsConstructor
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class PcuGroup {
- /**
- * Unique identifier for the PCU group.
- */
- @JsonProperty("uuid")
- private String id;
-
- /**
- * Organization identifier.
- */
- private String orgId;
-
- /**
- * Human-readable title for the PCU group.
- */
- private String title;
-
- /**
- * Description of the PCU group.
- */
- private String description;
-
- /**
- * Cloud provider where the PCU group is deployed.
- */
- private CloudProviderType cloudProvider;
-
- /**
- * Cloud region where the PCU group is deployed.
- */
- private String region;
-
- /**
- * Instance type for the PCU group.
- */
- private String instanceType;
-
- /**
- * Provisioning type (e.g., on-demand, reserved).
- */
- private PcuProvisionType provisionType;
-
- /**
- * Minimum number of PCUs.
- */
- private int min;
-
- /**
- * Maximum number of PCUs.
- */
- private int max;
-
- /**
- * Number of reserved PCUs.
- */
- private int reserved;
-
- /**
- * Timestamp when the PCU group was created.
- */
- private String createdAt;
-
- /**
- * Timestamp when the PCU group was last updated.
- */
- private String updatedAt;
-
- /**
- * User who created the PCU group.
- */
- private String createdBy;
-
- /**
- * User who last updated the PCU group.
- */
- private String updatedBy;
-
- /**
- * Current status of the PCU group.
- */
- private PcuGroupStatusType status;
-}
diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupDbAssociationNotFound.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupDbAssociationNotFound.java
index 8f0fbfb4..ee1833b4 100644
--- a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupDbAssociationNotFound.java
+++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupDbAssociationNotFound.java
@@ -2,6 +2,8 @@
import lombok.Getter;
+import java.util.UUID;
+
/**
* Exception thrown when a datacenter association with a PCU (Processing Capacity Units) Group cannot be found.
* This occurs when attempting to access or modify an association that doesn't exist.
@@ -11,7 +13,7 @@ public class PcuGroupDbAssociationNotFound extends RuntimeException {
* PCU group unique identifier.
*/
@Getter
- private final String pcuGroupId;
+ private final UUID pcuGroupId;
/**
* Datacenter unique identifier.
@@ -27,7 +29,7 @@ public class PcuGroupDbAssociationNotFound extends RuntimeException {
* @param datacenterId
* the datacenter ID
*/
- public PcuGroupDbAssociationNotFound(String pcuGroupId, String datacenterId) {
+ public PcuGroupDbAssociationNotFound(UUID pcuGroupId, String datacenterId) {
super("Association not found for pcu group '" + pcuGroupId + "' and datacenter '" + datacenterId + "'");
this.pcuGroupId = pcuGroupId;
this.datacenterId = datacenterId;
diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupNotFoundException.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupNotFoundException.java
index 920d3e4c..04ccb451 100644
--- a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupNotFoundException.java
+++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupNotFoundException.java
@@ -3,6 +3,7 @@
import lombok.Getter;
import java.util.Optional;
+import java.util.UUID;
/**
* Exception thrown when a PCU (Processing Capacity Units) Group cannot be found.
@@ -20,7 +21,7 @@ public class PcuGroupNotFoundException extends RuntimeException {
* Optional ID of the PCU group that was not found.
*/
@Getter
- private final Optional id;
+ private final Optional id;
/**
* Private constructor for creating the exception.
@@ -30,12 +31,28 @@ public class PcuGroupNotFoundException extends RuntimeException {
* @param id
* optional ID of the PCU group
*/
- private PcuGroupNotFoundException(Optional title, Optional id) {
- super("PCU group " + title.or(() -> id).map(s -> "'" + s + "' ").orElse("") + "has not been found.");
+ private PcuGroupNotFoundException(Optional title, Optional id) {
+ super(buildErrorMessage(title, id));
this.title = title;
this.id = id;
}
+ /**
+ * Build Error Message.
+ * @param title
+ * title
+ * @param id
+ * PUC group identifier
+ * @return
+ * exception
+ */
+ private static String buildErrorMessage(Optional title, Optional id) {
+ String identifier = title
+ .map(t -> "'" + t + "'")
+ .orElseGet(() -> id.map(uuid -> "'" + uuid + "'").orElse(""));
+ return "PCU group " + identifier + " has not been found.";
+ }
+
/**
* Creates an exception for a PCU group not found by title.
*
@@ -56,7 +73,7 @@ public static PcuGroupNotFoundException forTitle(String title) {
* @return
* exception instance
*/
- public static PcuGroupNotFoundException forId(String id) {
+ public static PcuGroupNotFoundException forId(UUID id) {
return new PcuGroupNotFoundException(Optional.empty(), Optional.of(id));
}
}
diff --git a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/AbstractDevopsApiTest.java b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/AbstractDevopsApiTest.java
index 64607136..4dcccfd7 100644
--- a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/AbstractDevopsApiTest.java
+++ b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/AbstractDevopsApiTest.java
@@ -4,6 +4,7 @@
import com.dtsx.astra.sdk.db.AstraDBOpsClient;
import com.dtsx.astra.sdk.db.domain.DatabaseCreationRequest;
import com.dtsx.astra.sdk.streaming.AstraStreamingClient;
+import com.dtsx.astra.sdk.utils.AstraEnvironment;
import com.dtsx.astra.sdk.utils.AstraRc;
import com.dtsx.astra.sdk.utils.Utils;
import org.junit.jupiter.api.Assertions;
@@ -33,25 +34,30 @@ public abstract class AbstractDevopsApiTest {
*/
private static String token;
+ /**
+ * Hold reference to token
+ */
+ private static AstraEnvironment env;
+
/**
* Reference to Databases Client.
*/
- private static AstraDBOpsClient databasesClient;
+ protected static AstraDBOpsClient databasesClient;
/**
* Reference to organization client.
*/
- private static AstraOpsClient apiDevopsClient;
+ protected static AstraOpsClient apiDevopsClient;
/**
* Working db.
*/
- private static DbOpsClient dbClient;
+ protected static DbOpsClient dbClient;
/**
* Reference to Databases Client.
*/
- private static AstraStreamingClient streamingClient;
+ protected static AstraStreamingClient streamingClient;
/**
* Access DB client.
@@ -61,7 +67,7 @@ public abstract class AbstractDevopsApiTest {
*/
protected AstraOpsClient getApiDevopsClient() {
if (apiDevopsClient == null) {
- apiDevopsClient = new AstraOpsClient(getToken());
+ apiDevopsClient = new AstraOpsClient(getToken(), getEnvironment());
}
return apiDevopsClient;
}
@@ -74,7 +80,7 @@ protected AstraOpsClient getApiDevopsClient() {
*/
protected AstraDBOpsClient getDatabasesClient() {
if (databasesClient == null) {
- databasesClient = new AstraDBOpsClient(getToken());
+ databasesClient = new AstraDBOpsClient(getToken(), getEnvironment());
}
return databasesClient;
}
@@ -87,7 +93,7 @@ protected AstraDBOpsClient getDatabasesClient() {
*/
protected AstraStreamingClient getStreamingClient() {
if (streamingClient == null) {
- streamingClient = new AstraStreamingClient(getToken());
+ streamingClient = new AstraStreamingClient(getToken(), getEnvironment());
}
return streamingClient;
}
@@ -110,6 +116,21 @@ protected String getToken() {
return token;
}
+ protected AstraEnvironment getEnvironment() {
+ String envStr = null;
+ if (env == null) {
+ if (AstraRc.isDefaultConfigFileExists()) {
+ envStr = new AstraRc()
+ .getSectionKey(AstraRc.ASTRARC_DEFAULT, AstraRc.ASTRA_ENV)
+ .orElse(null);
+ env = AstraEnvironment.valueOf(envStr);
+ }
+ envStr = Utils.readEnvVariable(AstraRc.ASTRA_ENV).orElse(envStr);
+ env = AstraEnvironment.valueOf(envStr);
+ }
+ return env;
+ }
+
/**
* Create DB if not exist
*
diff --git a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUGroupClientDevTest.java b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUGroupClientDevTest.java
new file mode 100644
index 00000000..9e9ddcf1
--- /dev/null
+++ b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUGroupClientDevTest.java
@@ -0,0 +1,187 @@
+package com.dtsx.astra.sdk.pcu;
+
+import com.dtsx.astra.sdk.AbstractDevopsApiTest;
+import com.dtsx.astra.sdk.AstraOpsClient;
+import com.dtsx.astra.sdk.db.domain.CloudProviderType;
+import com.dtsx.astra.sdk.db.domain.DatabaseCreationRequest;
+import com.dtsx.astra.sdk.db.domain.DatabaseStatusType;
+import com.dtsx.astra.sdk.org.domain.Organization;
+import com.dtsx.astra.sdk.pcu.domain.PCUCapacityWorkloadType;
+import com.dtsx.astra.sdk.pcu.domain.PCUGroup;
+import com.dtsx.astra.sdk.pcu.domain.PCUGroupCreationRequest;
+import com.dtsx.astra.sdk.pcu.domain.PCUInstanceType;
+import com.dtsx.astra.sdk.pcu.domain.PCUProvisionType;
+import com.dtsx.astra.sdk.pcu.domain.PCUType;
+import com.dtsx.astra.sdk.pcu.domain.PCUTypeLocationFilter;
+import com.dtsx.astra.sdk.utils.AstraEnvironment;
+import com.dtsx.astra.sdk.utils.JsonUtils;
+import com.dtsx.astra.sdk.utils.TestUtils;
+import com.dtsx.astra.sdk.utils.Utils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+// Unpredictable so re;oving fro; automated tests
+//@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class PCUGroupClientDevTest extends AbstractDevopsApiTest {
+
+ public static final String DEV_REGION = "us-west-2";
+ public static final CloudProviderType DEV_PROVIDER = CloudProviderType.AWS;
+
+ public static final String DEV_TOKEN = Utils
+ .readEnvVariable("ASTRA_DB_APPLICATION_TOKEN_DEV")
+ .orElseThrow(() -> new IllegalStateException("Please set env var 'ASTRA_DB_APPLICATION_TOKEN_DEV' with a dev token"));
+
+ protected static PCUGroupsOpsClient PCUGroupsOpsClient;
+
+ @BeforeAll
+ public static void beforeAll() {
+ apiDevopsClient = new AstraOpsClient(DEV_TOKEN, AstraEnvironment.DEV);
+ PCUGroupsOpsClient = apiDevopsClient.pcus();
+ }
+
+ @Test
+ @Order(1)
+ public void shouldAccessOrganizationInDev() {
+ Organization org = getApiDevopsClient().getOrganization();
+ Assertions.assertNotNull(org);
+ Assertions.assertNotNull(org.getId());
+ Assertions.assertNotNull(org.getName());
+ System.out.println("[test-pcu] - You are connected to organization: " + org.getName() + "(" + org.getId() + ")");
+ }
+
+ @Test
+ @Order(2)
+ public void shouldListPcuGroupsType() {
+ // Full List
+ System.out.println("[test-pcu] - Listing PCU Types (no filters)");
+ Stream types = PCUGroupsOpsClient.listPcuTypes();
+ System.out.println("[test-pcu] - Items " + types.toList().size());
+
+ // Filtered by DC
+ PCUTypeLocationFilter location = new PCUTypeLocationFilter(DEV_PROVIDER.getCode().toLowerCase(), DEV_REGION);
+ System.out.println("[test-pcu] - Listing PCU Types on " + JsonUtils.marshall(location));
+ List filtered = getApiDevopsClient().pcus().listPcuTypes(location).toList();
+ Assertions.assertNotNull(filtered);
+ System.out.println("[test-pcu] - Items " + filtered.size());
+ System.out.println("[test-pcu] - Items " + JsonUtils.marshall(filtered));
+ Assertions.assertTrue(filtered.stream().anyMatch(pcu -> pcu.getType().equals("small")));
+ }
+
+ @Test
+ @Order(2)
+ public void shouldListPcuGroups() {
+ List allGroups = PCUGroupsOpsClient.findAll().toList();
+ System.out.println("[test-pcu] - Listing pcu groups " + JsonUtils.marshall(allGroups));
+ System.out.println("[test-pcu] - Items " + allGroups.size());
+ }
+
+ @Test
+ @Order(3)
+ public void shouldCreatePcuGroupStandard() {
+
+ UUID pcuGroupStandardId = null;
+ try {
+ PCUGroupCreationRequest createPcu = PCUGroupCreationRequest
+ .builder()
+ .title("pcu_group_from_java")
+ .description("my first PCU group")
+ .instanceType("standard")
+ .cloudProvider(CloudProviderType.AWS)
+ .region(DEV_REGION)
+ .workloadType(PCUCapacityWorkloadType.flexible.name())
+ .provisionType(PCUProvisionType.shared.name())
+ .reserved(1)
+ .min(1)
+ .max(1)
+ .build();
+
+ // Creating standard pcu Group
+ PCUGroup group = PCUGroupsOpsClient.create(createPcu);
+ Assertions.assertNotNull(group);
+ pcuGroupStandardId = pcuGroupStandardId = group.getId();
+ System.out.println("[test-pcu] - Created PCU group: " + group.getId());
+ Assertions.assertTrue(getApiDevopsClient().pcus().findById(group.getId()).isPresent());
+
+ // Create DB
+ DatabaseCreationRequest dbCreation = DatabaseCreationRequest
+ .builder()
+ .name("vector_db_in_standard_pcu")
+ .keyspace(SDK_TEST_KEYSPACE)
+ .cloudProvider(CloudProviderType.AWS)
+ .cloudRegion(DEV_REGION)
+ .withVector()
+ .assignToPCUGroup(pcuGroupStandardId)
+ .build();
+
+ String dbId = getApiDevopsClient().db().create(dbCreation);
+ TestUtils.waitForDbStatus(getDatabasesClient().database(dbId), DatabaseStatusType.ACTIVE, 500);
+ Assertions.assertTrue(getDatabasesClient().findById(dbId).isPresent());
+ Assertions.assertNotNull(getDatabasesClient().database(dbId).get());
+ Assertions.assertTrue(getDatabasesClient().findByName("vector_db_in_standard_pcu").findAny().isPresent());
+
+ } catch (RuntimeException e) {
+
+ } finally {
+ if (pcuGroupStandardId != null) {
+ PCUGroupsOpsClient.group(pcuGroupStandardId).delete();
+ }
+ }
+ }
+
+ @Test
+ @Order(2)
+ public void shouldCreateMiniPcu() {
+ PCUGroupCreationRequest createPcu = PCUGroupCreationRequest
+ .builder()
+ .title("java_client_mini_pcu")
+ .description("java_client_mini_pcu")
+ .instanceType(PCUInstanceType.small.name())
+ .cloudProvider(CloudProviderType.AWS)
+ .region(DEV_REGION)
+ .workloadType(PCUCapacityWorkloadType.flexible.name())
+ .provisionType(PCUProvisionType.shared.name())
+ .reserved(1)
+ .min(1)
+ .max(1)
+ .build();
+ PCUGroup group = getApiDevopsClient().pcus().create(createPcu);
+ System.out.println(group);
+ }
+
+ @Test
+ @Order(2)
+ public void shouldDeletePcuGroup() {
+ PCUGroupsOpsClient.group(UUID.fromString("0c1f67a8-1fd6-4f52-ae2c-ea1483718a11")).delete();
+ }
+
+ @Test
+ @Order(3)
+ public void should_create_db_and_assign_pcu() throws InterruptedException {
+ UUID miniPcuUUID = UUID.fromString("57dde257-86c6-4646-9247-670cd8a4d360");
+ DatabaseCreationRequest dbCreation = DatabaseCreationRequest
+ .builder()
+ .name("vector_db_in_mini_pcu")
+ .keyspace(SDK_TEST_KEYSPACE)
+ .cloudProvider(CloudProviderType.AWS)
+ .cloudRegion(DEV_REGION)
+ .withVector()
+ .assignToPCUGroup(miniPcuUUID)
+ .build();
+ String dbId = getApiDevopsClient().db().create(dbCreation);
+ Thread.sleep(10000);
+ Assertions.assertTrue(getDatabasesClient().findById(dbId).isPresent());
+ Assertions.assertNotNull(getDatabasesClient().database(dbId).get());
+ Assertions.assertTrue(getDatabasesClient().findByName("vector_db_in_mini_pcu").count() > 0);
+ // When
+ TestUtils.waitForDbStatus(getDatabasesClient().database(dbId), DatabaseStatusType.ACTIVE, 500);
+ }
+
+}
diff --git a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUTypeResolverTest.java b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUTypeResolverTest.java
new file mode 100644
index 00000000..ab38ecc9
--- /dev/null
+++ b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUTypeResolverTest.java
@@ -0,0 +1,132 @@
+package com.dtsx.astra.sdk.pcu;
+
+import com.dtsx.astra.sdk.pcu.domain.PCUInstanceType;
+import com.dtsx.astra.sdk.pcu.domain.PCUTypeResolver;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * Test class for PcuTypeResolver.
+ */
+@DisplayName("PcuTypeResolver Tests")
+class PCUTypeResolverTest {
+
+ @Nested
+ @DisplayName("Mini PCU Enabled")
+ class MiniPcuEnabled {
+
+ @Test
+ @DisplayName("Selects SMALL when SMALL is available")
+ void selectsSmallWhenSmallIsAvailable() {
+ List availableTypes = Arrays.asList(
+ PCUInstanceType.small,
+ PCUInstanceType.medium,
+ PCUInstanceType.generalPurpose,
+ PCUInstanceType.cacheOptimized
+ );
+
+ assertEquals(
+ PCUInstanceType.small,
+ PCUTypeResolver.resolveDefaultPcuType(availableTypes, true)
+ );
+ }
+
+ @Test
+ @DisplayName("Selects MEDIUM when SMALL is unavailable but MEDIUM exists")
+ void selectsMediumWhenSmallUnavailableButMediumExists() {
+ List availableTypes = Arrays.asList(
+ PCUInstanceType.medium,
+ PCUInstanceType.generalPurpose
+ );
+
+ assertEquals(
+ PCUInstanceType.medium,
+ PCUTypeResolver.resolveDefaultPcuType(availableTypes, true)
+ );
+ }
+
+ @Test
+ @DisplayName("Selects GENERAL_PURPOSE when only GENERAL_PURPOSE and CACHE_OPTIMIZED exist")
+ void selectsGeneralPurposeWhenOnlyGeneralPurposeAndCacheOptimizedExist() {
+ List availableTypes = Arrays.asList(
+ PCUInstanceType.generalPurpose,
+ PCUInstanceType.cacheOptimized
+ );
+
+ assertEquals(
+ PCUInstanceType.generalPurpose,
+ PCUTypeResolver.resolveDefaultPcuType(availableTypes, true)
+ );
+ }
+
+ @Test
+ @DisplayName("Selects CACHE_OPTIMIZED when it is the only available type")
+ void selectsCacheOptimizedWhenItIsTheOnlyAvailableType() {
+ List availableTypes = Collections.singletonList(
+ PCUInstanceType.cacheOptimized
+ );
+
+ assertEquals(
+ PCUInstanceType.cacheOptimized,
+ PCUTypeResolver.resolveDefaultPcuType(availableTypes, true)
+ );
+ }
+
+ @Test
+ @DisplayName("Returns null when no types are available")
+ void returnsNullWhenNoTypesAreAvailable() {
+ assertNull(PCUTypeResolver.resolveDefaultPcuType(Collections.emptyList(), true));
+ }
+ }
+
+ @Nested
+ @DisplayName("Mini PCU Disabled")
+ class MiniPcuDisabled {
+
+ @Test
+ @DisplayName("Selects GENERAL_PURPOSE when GENERAL_PURPOSE is available")
+ void selectsGeneralPurposeWhenGeneralPurposeIsAvailable() {
+ List availableTypes = Arrays.asList(
+ PCUInstanceType.generalPurpose,
+ PCUInstanceType.cacheOptimized
+ );
+
+ assertEquals(
+ PCUInstanceType.generalPurpose,
+ PCUTypeResolver.resolveDefaultPcuType(availableTypes, false)
+ );
+ }
+
+ @Test
+ @DisplayName("Selects CACHE_OPTIMIZED when it is the only available type")
+ void selectsCacheOptimizedWhenItIsTheOnlyAvailableType() {
+ List availableTypes = Collections.singletonList(
+ PCUInstanceType.cacheOptimized
+ );
+
+ assertEquals(
+ PCUInstanceType.cacheOptimized,
+ PCUTypeResolver.resolveDefaultPcuType(availableTypes, false)
+ );
+ }
+
+ @Test
+ @DisplayName("Returns null when only SMALL or MEDIUM are available")
+ void returnsNullWhenOnlySmallOrMediumAreAvailable() {
+ List availableTypes = Arrays.asList(
+ PCUInstanceType.small,
+ PCUInstanceType.medium
+ );
+
+ assertNull(PCUTypeResolver.resolveDefaultPcuType(availableTypes, false));
+ }
+ }
+}
diff --git a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/ParkingTest.java b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/ParkingTest.java
deleted file mode 100644
index 0d1b53e4..00000000
--- a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/ParkingTest.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.dtsx.astra.sdk.pcu;
-
-import com.dtsx.astra.sdk.AstraOpsClient;
-import com.dtsx.astra.sdk.utils.AstraEnvironment;
-import lombok.val;
-import org.junit.jupiter.api.Test;
-
-public class ParkingTest {
-
-}
diff --git a/integrations/skills/spring-boot-data-api/README.md b/integrations/skills/spring-boot-data-api/README.md
index a290b4a0..04fc6257 100644
--- a/integrations/skills/spring-boot-data-api/README.md
+++ b/integrations/skills/spring-boot-data-api/README.md
@@ -5,8 +5,8 @@ A comprehensive AI-powered skill for building production-ready Spring Boot appli
## Quick Links
- π **[Main Skill Document](SKILL.md)** - Complete step-by-step guide
-- π **[Code Templates](templates/)** - Reusable code templates
-- π‘ **[Basic Example](examples/basic/)** - Minimal working example
+- π **[Code Templates](assets/templates/)** - Reusable code templates
+- π‘ **[Basic Example](assets/examples/basic/)** - Minimal working example
## What You'll Learn
@@ -29,6 +29,26 @@ A comprehensive AI-powered skill for building production-ready Spring Boot appli
30-45 minutes
+## Folder Structure
+
+```
+spring-boot-data-api/
+βββ README.md # This file
+βββ SKILL.md # Main skill guide
+βββ references/ # Additional reference docs (future)
+βββ scripts/ # Automation scripts (future)
+βββ assets/ # Reusable assets
+ βββ templates/ # Code templates
+ β βββ Document.java.template
+ β βββ Repository.java.template
+ β βββ Service.java.template
+ β βββ Controller.java.template
+ β βββ application.yml.template
+ βββ examples/ # Working examples
+ βββ basic/ # Basic example
+ βββ README.md
+```
+
## How to Use This Skill
### With Claude
@@ -70,7 +90,7 @@ integrations/skills/spring-boot-data-api/SKILL.md
```
2. **Use templates to generate code:**
- - Copy templates from `templates/` directory
+ - Copy templates from `assets/templates/` directory
- Replace placeholders ({{CLASS_NAME}}, {{COLLECTION_NAME}}, etc.)
- Customize for your use case
diff --git a/integrations/skills/spring-boot-data-api/examples/basic/README.md b/integrations/skills/spring-boot-data-api/assets/examples/basic/README.md
similarity index 100%
rename from integrations/skills/spring-boot-data-api/examples/basic/README.md
rename to integrations/skills/spring-boot-data-api/assets/examples/basic/README.md
diff --git a/integrations/skills/spring-boot-data-api/templates/Controller.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Controller.java.template
similarity index 100%
rename from integrations/skills/spring-boot-data-api/templates/Controller.java.template
rename to integrations/skills/spring-boot-data-api/assets/templates/Controller.java.template
diff --git a/integrations/skills/spring-boot-data-api/templates/Document.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Document.java.template
similarity index 100%
rename from integrations/skills/spring-boot-data-api/templates/Document.java.template
rename to integrations/skills/spring-boot-data-api/assets/templates/Document.java.template
diff --git a/integrations/skills/spring-boot-data-api/templates/Repository.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Repository.java.template
similarity index 100%
rename from integrations/skills/spring-boot-data-api/templates/Repository.java.template
rename to integrations/skills/spring-boot-data-api/assets/templates/Repository.java.template
diff --git a/integrations/skills/spring-boot-data-api/templates/Service.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Service.java.template
similarity index 100%
rename from integrations/skills/spring-boot-data-api/templates/Service.java.template
rename to integrations/skills/spring-boot-data-api/assets/templates/Service.java.template
diff --git a/integrations/skills/spring-boot-data-api/templates/application.yml.template b/integrations/skills/spring-boot-data-api/assets/templates/application.yml.template
similarity index 100%
rename from integrations/skills/spring-boot-data-api/templates/application.yml.template
rename to integrations/skills/spring-boot-data-api/assets/templates/application.yml.template
diff --git a/tools/data-api-tools/src/test/java/com/datastax/astra/tool/copy/README.md b/tools/data-api-tools/src/test/java/com/datastax/astra/tool/copy/README.md
new file mode 100644
index 00000000..a6b24de8
--- /dev/null
+++ b/tools/data-api-tools/src/test/java/com/datastax/astra/tool/copy/README.md
@@ -0,0 +1,86 @@
+# CollectionCloner Integration Tests
+
+This directory contains integration tests for the `CollectionCloner` utility.
+
+## Test Coverage
+
+The `CollectionClonerIT` test suite covers:
+
+1. **Basic Cloning** - Small collection (50 documents) with default settings
+2. **Large Collection Cloning** - 2500+ documents with parallel reading (tests estimatedDocumentCount)
+3. **Document Transformation** - Using DocumentMapper to transform documents during cloning
+4. **Custom Thread Pools** - Testing different read/insert thread pool configurations
+5. **Empty Collection** - Handling edge case of empty source collection
+6. **Duplicate Prevention** - Verifying no duplicates are created on repeated cloning
+
+## Running the Tests
+
+### Prerequisites
+
+- Local HCD/DSE instance running on `http://localhost:8181` (default)
+- OR Astra database with proper credentials configured
+
+### Run Tests Locally (HCD/DSE)
+
+```bash
+# From project root
+mvn test -pl tools/data-api-tools
+
+# Or from tools/data-api-tools directory
+mvn test
+```
+
+### Run Tests Against Astra
+
+```bash
+# Set environment variables
+export ASTRA_DB_APPLICATION_TOKEN=
+export ASTRA_DB_API_ENDPOINT=
+
+# Run tests
+mvn test -pl tools/data-api-tools -Dtest.environment=astra_prod
+```
+
+## Test Configuration
+
+Tests use configuration from:
+- `src/test/resources/test-config.properties` - Default local settings
+- Environment variables can override config file settings
+- Inherits from `AbstractDataAPITest` in astra-db-java module
+
+## Performance Expectations
+
+With default settings (5 read threads, 10 insert threads):
+- Small collections (< 100 docs): < 5 seconds
+- Medium collections (500 docs): < 15 seconds
+- Large collections (2500+ docs): < 60 seconds
+
+Actual performance depends on:
+- Network latency
+- Database load
+- Document size and complexity
+- Available system resources
+
+## Troubleshooting
+
+### Test Failures
+
+1. **Connection refused**: Ensure HCD/DSE is running on localhost:8181
+2. **Timeout errors**: Increase timeout in test configuration
+3. **Duplicate key errors**: Expected behavior when cloning to non-empty target
+
+### Logging
+
+Adjust log levels in `src/test/resources/logback-test.xml`:
+```xml
+
+```
+
+## Adding New Tests
+
+When adding new test cases:
+1. Extend `CollectionClonerIT` class
+2. Use `@Order` annotation to control execution sequence
+3. Clean up collections in `@BeforeAll` and `@AfterAll`
+4. Use descriptive test method names: `should__()`
+5. Add assertions to verify expected behavior
diff --git a/tools/data-api-tools/src/test/resources/logback-test.xml b/tools/data-api-tools/src/test/resources/logback-test.xml
new file mode 100644
index 00000000..5cf5eec7
--- /dev/null
+++ b/tools/data-api-tools/src/test/resources/logback-test.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/data-api-tools/src/test/resources/test-config.properties b/tools/data-api-tools/src/test/resources/test-config.properties
new file mode 100644
index 00000000..252cf7cf
--- /dev/null
+++ b/tools/data-api-tools/src/test/resources/test-config.properties
@@ -0,0 +1,49 @@
+# Test Configuration for Data API Tools Integration Tests
+# This file is used by CollectionClonerIT and other integration tests
+
+# Default test environment - Astra Production
+test.environment=astra_prod
+
+# ========================================
+# Astra Configuration
+# ========================================
+# You can configure Astra connection in two ways:
+#
+# Option 1: Environment Variables (recommended for CI/CD)
+# ASTRA_DB_APPLICATION_TOKEN - Your Astra DB token
+#
+# Option 2: Properties file (convenient for local development)
+# Uncomment and set the property below:
+# astra.token=AstraCS:...your-token-here...
+
+# Astra settings
+astra.keyspace=default_keyspace
+astra.cloud.provider=AWS
+astra.cloud.region=us-east-2
+
+# Test settings
+test.timeout.seconds=300
+test.log.progress=true
+test.vectorize=true
+test.reranking=true
+
+# ========================================
+# Local HCD/DSE Configuration (commented out)
+# Uncomment these lines and set test.environment=local to run against local HCD/DSE
+# ========================================
+# test.environment=local
+# local.endpoint=http://localhost:8181
+# local.keyspace=default_keyspace
+# local.username=cassandra
+# local.password=cassandra
+# test.vectorize=false
+# test.reranking=false
+
+# ========================================
+# Notes:
+# ========================================
+# - For Astra tests, the framework automatically creates/finds a database
+# based on cloud provider and region settings above
+# - You don't need to specify ASTRA_DB_API_ENDPOINT - it's derived from
+# the database name, cloud provider, and region
+# - Priority: Environment Variables > System Properties > Config File