Skip to content

Commit

Permalink
Missing UT Added (#37)
Browse files Browse the repository at this point in the history
* Added Dummy Test case

* Removed * from import

* removed unwanted

* Added Fixes

* Added test case and fixes

* Added Some more PR comments

* Added Test case for Cassandra Reader

* Added New testcase

* Added Dependecny
  • Loading branch information
pawankashyapollion authored and taherkl committed Jan 2, 2025
1 parent 3b6e39b commit cf0f5ac
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 11 deletions.
6 changes: 6 additions & 0 deletions v2/spanner-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@
<version>3.12.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.4</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private CassandraSourceMetadata(ResultSet resultSet, Schema schema) {
* @return A map where keys are table names and values are {@link SourceTable} objects containing
* schema details.
*/
public Map<String, SourceTable> generateSourceSchema() {
private Map<String, SourceTable> generateSourceSchema() {
Map<String, Map<String, SourceColumnDefinition>> colDefinitions = new HashMap<>();
Map<String, List<ColumnPK>> columnPKs = new HashMap<>();
Map<String, List<String>> columnIds = new HashMap<>();
Expand Down Expand Up @@ -151,9 +151,7 @@ private boolean isPrimaryKey(String kind) {
*/
private Map<String, NameAndCols> convertSourceToNameAndColsTable(Collection<SourceTable> tables) {
return tables.stream()
.collect(
Collectors.toMap(
SourceTable::getName, CassandraSourceMetadata::convertSourceTableToNameAndCols));
.collect(Collectors.toMap(SourceTable::getName, this::convertSourceTableToNameAndCols));
}

/**
Expand All @@ -162,7 +160,7 @@ private Map<String, NameAndCols> convertSourceToNameAndColsTable(Collection<Sour
* @param sourceTable The {@link SourceTable} to convert.
* @return A {@link NameAndCols} object containing the table name and column names.
*/
private static NameAndCols convertSourceTableToNameAndCols(SourceTable sourceTable) {
private NameAndCols convertSourceTableToNameAndCols(SourceTable sourceTable) {
Map<String, String> columnNames =
sourceTable.getColDefs().values().stream()
.collect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@ public String getUsername() {
return getOptionValue(TypedDriverOption.AUTH_PROVIDER_USER_NAME);
}

public String getPassword() {
return getOptionValue(TypedDriverOption.AUTH_PROVIDER_PASSWORD);
}

public OptionsMap getOptionsMap() {
return this.optionsMap;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (C) 2024 Google LLC
*
* Licensed 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.google.cloud.teleport.v2.spanner.migrations.metadata;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;

import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.google.cloud.teleport.v2.spanner.migrations.schema.Schema;
import java.util.Arrays;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

class CassandraSourceMetadataTest {

@Mock private ResultSet mockResultSet;
@Mock private Row mockRow1;
@Mock private Row mockRow2;
@Mock private Schema mockSchema;

private CassandraSourceMetadata.Builder builder;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
builder = new CassandraSourceMetadata.Builder();
}

@Test
void testBuilderSetSchemaAndResultSet() {
CassandraSourceMetadata metadata =
builder.setResultSet(mockResultSet).setSchema(mockSchema).build();
Assertions.assertNotNull(metadata, "CassandraSourceMetadata should not be null");
}

@Test
void testGenerateSourceSchema() {
doAnswer(
invocation -> {
Iterable<?> iterable = Arrays.asList(mockRow1, mockRow2);
iterable.forEach(invocation.getArgument(0));
return null;
})
.when(mockResultSet)
.forEach(any());

when(mockRow1.getString("table_name")).thenReturn("table1");
when(mockRow1.getString("column_name")).thenReturn("column1");
when(mockRow1.getString("type")).thenReturn("text");
when(mockRow1.getString("kind")).thenReturn("partition_key");

when(mockRow2.getString("table_name")).thenReturn("table1");
when(mockRow2.getString("column_name")).thenReturn("column2");
when(mockRow2.getString("type")).thenReturn("int");
when(mockRow2.getString("kind")).thenReturn("clustering");

CassandraSourceMetadata metadata =
builder.setResultSet(mockResultSet).setSchema(mockSchema).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (C) 2024 Google LLC
*
* Licensed 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.google.cloud.teleport.v2.spanner.migrations.shard;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.datastax.oss.driver.api.core.config.OptionsMap;
import com.datastax.oss.driver.api.core.config.TypedDriverOption;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

public class CassandraShardTest {

private OptionsMap optionsMap;
private List<String> contactPoints;

@BeforeEach
public void setUp() {
contactPoints = List.of("127.0.0.1:9042");
optionsMap = Mockito.mock(OptionsMap.class);
Mockito.when(optionsMap.get(TypedDriverOption.CONTACT_POINTS)).thenReturn(contactPoints);
Mockito.when(optionsMap.get(TypedDriverOption.SESSION_KEYSPACE)).thenReturn("test_keyspace");
}

@Test
public void testConstructor_Valid() {
CassandraShard shard = new CassandraShard(optionsMap);
assertNotNull(shard);
assertEquals("test_keyspace", shard.getKeySpaceName());
assertEquals(contactPoints, shard.getContactPoints());
}

@Test
public void testConstructor_InvalidContactPoints() {
Mockito.when(optionsMap.get(TypedDriverOption.CONTACT_POINTS)).thenReturn(null);
assertThrows(IllegalArgumentException.class, () -> new CassandraShard(optionsMap));
}

@Test
public void testConstructor_InvalidKeySpace() {
Mockito.when(optionsMap.get(TypedDriverOption.SESSION_KEYSPACE)).thenReturn(null);
assertThrows(IllegalArgumentException.class, () -> new CassandraShard(optionsMap));
}

@Test
public void testExtractAndSetHostAndPort_Valid() {
CassandraShard shard = new CassandraShard(optionsMap);
assertEquals("127.0.0.1", shard.getHost());
assertEquals("9042", shard.getPort());
}

@Test
public void testGetters() {
CassandraShard shard = new CassandraShard(optionsMap);
assertEquals(contactPoints, shard.getContactPoints());
assertEquals("test_keyspace", shard.getKeySpaceName());
}

@Test
public void testToString() {
CassandraShard shard = new CassandraShard(optionsMap);
String expected =
String.format(
"CassandraShard{logicalShardId='%s', contactPoints=%s, keyspace='%s', host='%s', port='%s'}",
shard.getLogicalShardId(), contactPoints, "test_keyspace", "127.0.0.1", "9042");
assertEquals(expected, shard.toString());
}

@Test
public void testEqualsAndHashCode_Equal() {
CassandraShard shard1 = new CassandraShard(optionsMap);
CassandraShard shard2 = new CassandraShard(optionsMap);
assertEquals(shard1, shard2);
assertEquals(shard1.hashCode(), shard2.hashCode());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright (C) 2024 Google LLC
*
* Licensed 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.google.cloud.teleport.v2.spanner.migrations.utils;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mockStatic;

import com.google.cloud.teleport.v2.spanner.migrations.shard.Shard;
import com.google.common.io.Resources;
import java.io.FileNotFoundException;
import java.net.URL;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class CassandraConfigFileReaderTest {
private CassandraConfigFileReader cassandraConfigFileReader;
private MockedStatic<JarFileReader> mockFileReader;

@BeforeEach
void setUp() {
cassandraConfigFileReader = new CassandraConfigFileReader();
mockFileReader = mockStatic(JarFileReader.class);
}

@AfterEach
void tearDown() {
if (mockFileReader != null) {
mockFileReader.close();
}
}

@Test
void testGetCassandraShardSuccess() {
String testGcsPath = "gs://smt-test-bucket/cassandraConfig.conf";
URL testUrl = Resources.getResource("test-cassandra-config.conf");
mockFileReader
.when(() -> JarFileReader.saveFilesLocally(testGcsPath))
.thenReturn(new URL[] {testUrl});

List<Shard> shards = cassandraConfigFileReader.getCassandraShard(testGcsPath);

assertNotNull(shards, "The shards list should not be null.");
assertEquals(1, shards.size(), "The shards list should contain one shard.");

Logger logger = LoggerFactory.getLogger(CassandraConfigFileReader.class);
assertNotNull(logger, "Logger should be initialized.");
}

@Test
void testGetCassandraShardFileNotFound() {
String testConfigPath = "gs://non-existent-bucket/non-existent-file.yaml";

try (MockedStatic<CassandraDriverConfigLoader> mockedConfigLoader =
mockStatic(CassandraDriverConfigLoader.class)) {
mockedConfigLoader
.when(() -> CassandraDriverConfigLoader.getOptionsMapFromFile(testConfigPath))
.thenThrow(new FileNotFoundException("File not found: " + testConfigPath));

IllegalArgumentException exception =
assertThrows(
IllegalArgumentException.class,
() -> cassandraConfigFileReader.getCassandraShard(testConfigPath),
"Expected an IllegalArgumentException for missing configuration file.");

assertTrue(
exception.getMessage().contains("Configuration file not found:"),
"Exception message should indicate the missing configuration file.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
# Required: no
# Modifiable at runtime: no
# Overridable in a profile: no
// basic.session-keyspace = my_keyspace
basic.session-keyspace = my_keyspace

# How often the driver tries to reload the configuration.
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ public static PipelineResult run(Options options) {
}
List<Shard> shards;
String shardingMode;
if ("mysql".equals(options.getSourceType())) {
if (MYSQL_SOURCE_TYPE.equals(options.getSourceType())) {
ShardFileReader shardFileReader = new ShardFileReader(new SecretManagerAccessorImpl());
shards = shardFileReader.getOrderedShardDetails(options.getSourceShardsFilePath());
shardingMode = Constants.SHARDING_MODE_MULTI_SHARD;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,27 @@ public void write(DMLGeneratorResponse dmlGeneratorResponse) throws Exception {
}
}

/**
* Reads metadata for the specified Cassandra keyspace.
*
* <p>This method retrieves metadata from the Cassandra system schema, including table names,
* column names, column types, and kinds, for the given keyspace. It uses a prepared statement for
* safe and efficient execution.
*
* @param keyspace The name of the Cassandra keyspace for which metadata is to be retrieved. Must
* not be {@code null} or empty.
* @return A {@link ResultSet} containing the metadata rows with columns:
* <ul>
* <li>{@code table_name} - The name of the table.
* <li>{@code column_name} - The name of the column.
* <li>{@code type} - The data type of the column.
* <li>{@code kind} - The column kind (e.g., partition_key, clustering).
* </ul>
*
* @throws IllegalArgumentException If the provided keyspace name is {@code null} or empty.
* @throws ConnectionException If a connection to the Cassandra database could not be established.
* @throws Exception If any other unexpected error occurs during the operation.
*/
public ResultSet readMetadata(String keyspace) throws Exception {
if (keyspace == null || keyspace.isEmpty()) {
throw new IllegalArgumentException("Keyspace name cannot be null or empty.");
Expand Down

0 comments on commit cf0f5ac

Please sign in to comment.