diff --git a/core/src/main/java/com/scalar/db/common/error/CoreError.java b/core/src/main/java/com/scalar/db/common/error/CoreError.java index ddbf1cf9b8..111411a43c 100644 --- a/core/src/main/java/com/scalar/db/common/error/CoreError.java +++ b/core/src/main/java/com/scalar/db/common/error/CoreError.java @@ -676,6 +676,8 @@ public enum CoreError implements ScalarDbError { "Invalid number specified for column %s in table %s in namespace %s", "", ""), + DATA_LOADER_ERROR_METHOD_NULL_ARGUMENT( + Category.USER_ERROR, "0151", "Method null argument not allowed", "", ""), // // Errors for the concurrency error category diff --git a/data-loader/build.gradle b/data-loader/build.gradle index f633095150..87a057933b 100644 --- a/data-loader/build.gradle +++ b/data-loader/build.gradle @@ -1,4 +1,7 @@ subprojects { + ext { + jacksonVersion = '2.17.0' + } group = "scalardb.dataloader" dependencies { // AssertJ @@ -13,6 +16,7 @@ subprojects { // Apache Commons implementation("org.apache.commons:commons-lang3:${commonsLangVersion}") implementation("commons-io:commons-io:${commonsIoVersion}") + implementation("org.slf4j:slf4j-simple:${slf4jVersion}") // Mockito testImplementation "org.mockito:mockito-core:${mockitoVersion}" @@ -24,5 +28,11 @@ subprojects { annotationProcessor "org.projectlombok:lombok:${lombokVersion}" testCompileOnly "org.projectlombok:lombok:${lombokVersion}" testAnnotationProcessor "org.projectlombok:lombok:${lombokVersion}" + + // Jackson + implementation("com.fasterxml.jackson.core:jackson-core:${jacksonVersion}") + implementation("com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jacksonVersion}") + } } diff --git a/data-loader/core/src/main/java/com/scalar/db/dataloader/core/exception/Base64Exception.java b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/exception/Base64Exception.java new file mode 100644 index 0000000000..9cf94854c0 --- /dev/null +++ b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/exception/Base64Exception.java @@ -0,0 +1,14 @@ +package com.scalar.db.dataloader.core.exception; + +/** Exception thrown when an error occurs while trying to encode or decode base64 values. */ +public class Base64Exception extends Exception { + + /** + * Class constructor + * + * @param message Exception message + */ + public Base64Exception(String message) { + super(message); + } +} diff --git a/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/CollectionUtil.java b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/CollectionUtil.java new file mode 100644 index 0000000000..e98f4beef7 --- /dev/null +++ b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/CollectionUtil.java @@ -0,0 +1,23 @@ +package com.scalar.db.dataloader.core.util; + +import java.util.Collection; + +/** Utils for collection classes */ +public class CollectionUtil { + + /** + * Check if lists are of same length + * + * @param collections List of collections + * @return collections are same length or not + */ + public static boolean areSameLength(Collection... collections) { + int n = collections[0].size(); + for (Collection c : collections) { + if (c.size() != n) { + return false; + } + } + return true; + } +} diff --git a/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/CsvUtil.java b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/CsvUtil.java new file mode 100644 index 0000000000..9979ce58ca --- /dev/null +++ b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/CsvUtil.java @@ -0,0 +1,17 @@ +package com.scalar.db.dataloader.core.util; + +/** Utils for csv data manipulation */ +public class CsvUtil { + + /** + * Remove the last character in the string builder if it's a delimiter + * + * @param stringBuilder String builder instance + * @param delimiter Delimiter character used in the CSV content + */ + public static void removeTrailingDelimiter(StringBuilder stringBuilder, String delimiter) { + if (stringBuilder.substring(stringBuilder.length() - 1).equals(delimiter)) { + stringBuilder.setLength(stringBuilder.length() - 1); + } + } +} diff --git a/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/DebugUtil.java b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/DebugUtil.java new file mode 100644 index 0000000000..a16e2fae02 --- /dev/null +++ b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/DebugUtil.java @@ -0,0 +1,30 @@ +package com.scalar.db.dataloader.core.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DebugUtil { + + private static final Logger logger = LoggerFactory.getLogger(DebugUtil.class); + + /** + * log memory usage + * + * @param stage stage of process + */ + public static void logMemoryUsage(String stage) { + Runtime runtime = Runtime.getRuntime(); + long usedMemory = runtime.totalMemory() - runtime.freeMemory(); + long maxMemory = runtime.maxMemory(); + + logger.info( + "Memory usage at {}: Used Memory = {} MB, Max Memory = {} MB", + stage, + formatMemorySize(usedMemory), + formatMemorySize(maxMemory)); + } + + private static String formatMemorySize(long size) { + return String.format("%.2f", size / (1024.0 * 1024.0)); + } +} diff --git a/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/DecimalUtil.java b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/DecimalUtil.java new file mode 100644 index 0000000000..8372dc8aac --- /dev/null +++ b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/DecimalUtil.java @@ -0,0 +1,40 @@ +package com.scalar.db.dataloader.core.util; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; + +/** Utils for decimal handling */ +public class DecimalUtil { + + /** + * Convert a Double to a non-scientific formatted string + * + * @param doubleValue Double value + * @return formatted double as a string + */ + public static String convertToNonScientific(Double doubleValue) { + return createFormatter().format(doubleValue); + } + + /** + * Convert a Float to a non-scientific formatted string + * + * @param floatValue Float value + * @return formatted float as a string + */ + public static String convertToNonScientific(Float floatValue) { + return createFormatter().format(floatValue); + } + + /** + * Create a Decimal formatter + * + * @return decimal formatter instance + */ + private static DecimalFormat createFormatter() { + DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + df.setMaximumFractionDigits(340); // 340 = DecimalFormat.DOUBLE_FRACTION_DIGITS + return df; + } +} diff --git a/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/PathUtil.java b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/PathUtil.java new file mode 100644 index 0000000000..c307ea961f --- /dev/null +++ b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/PathUtil.java @@ -0,0 +1,24 @@ +package com.scalar.db.dataloader.core.util; + +import java.io.File; + +public class PathUtil { + + /** + * Ensures the specified path has a trailing path separator. + * + * @param path the path + * @return the path with a trailing path separator. + */ + public static String ensureTrailingSeparator(String path) { + if (path == null || path.isEmpty()) { + return ""; + } + + if (!path.endsWith(File.separator)) { + return path + File.separator; + } + + return path; + } +} diff --git a/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/RuntimeUtil.java b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/RuntimeUtil.java new file mode 100644 index 0000000000..870e70285a --- /dev/null +++ b/data-loader/core/src/main/java/com/scalar/db/dataloader/core/util/RuntimeUtil.java @@ -0,0 +1,21 @@ +package com.scalar.db.dataloader.core.util; + +import static com.scalar.db.common.error.CoreError.DATA_LOADER_ERROR_METHOD_NULL_ARGUMENT; + +/** Utils for runtime checks */ +public class RuntimeUtil { + + /** + * Argument null check + * + * @param values List of arguments + * @throws NullPointerException when one of the arguments is null + */ + public static void checkNotNull(Object... values) { + for (Object value : values) { + if (value == null) { + throw new NullPointerException(DATA_LOADER_ERROR_METHOD_NULL_ARGUMENT.buildMessage()); + } + } + } +} diff --git a/data-loader/core/src/test/java/com/scalar/db/dataloader/core/UnitTestUtils.java b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/UnitTestUtils.java new file mode 100644 index 0000000000..bf4b4414af --- /dev/null +++ b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/UnitTestUtils.java @@ -0,0 +1,227 @@ +package com.scalar.db.dataloader.core; + +import static com.scalar.db.io.DataType.BIGINT; +import static com.scalar.db.io.DataType.BLOB; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.scalar.db.api.TableMetadata; +import com.scalar.db.dataloader.core.util.DecimalUtil; +import com.scalar.db.io.BigIntColumn; +import com.scalar.db.io.BlobColumn; +import com.scalar.db.io.BooleanColumn; +import com.scalar.db.io.Column; +import com.scalar.db.io.DataType; +import com.scalar.db.io.DoubleColumn; +import com.scalar.db.io.FloatColumn; +import com.scalar.db.io.IntColumn; +import com.scalar.db.io.TextColumn; +import com.scalar.db.transaction.consensuscommit.Attribute; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** Utils for the service unit tests */ +public class UnitTestUtils { + public static final String TEST_NAMESPACE = "namespace"; + public static final String TEST_TABLE_NAME = "table"; + public static final String TEST_COLUMN_1_PK = "col1"; + public static final String TEST_COLUMN_2_CK = "col2"; + public static final String TEST_COLUMN_3_CK = "col3"; + public static final String TEST_COLUMN_4 = "col4"; + public static final String TEST_COLUMN_5 = "col5"; + public static final String TEST_COLUMN_6 = "col6"; + public static final String TEST_COLUMN_7 = "col7"; + + public static final String TEST_VALUE_TEXT = "test value"; + + public static final String TEST_VALUE_BLOB_STRING = "blob test value"; + static final byte[] TEST_VALUE_BLOB = TEST_VALUE_BLOB_STRING.getBytes(StandardCharsets.UTF_8); + public static final String TEST_VALUE_BLOB_BASE64 = + new String(Base64.getEncoder().encode(TEST_VALUE_BLOB), StandardCharsets.UTF_8); + public static final String TEST_VALUE_TX_ID = "txt value 464654654"; + public static final Float TEST_VALUE_FLOAT = Float.MIN_VALUE; + public static final int TEST_VALUE_INT = Integer.MAX_VALUE; + public static final Long TEST_VALUE_LONG = BigIntColumn.MAX_VALUE; + public static final boolean TEST_VALUE_BOOLEAN = true; + public static final double TEST_VALUE_DOUBLE = Double.MIN_VALUE; + public static final String TEST_CSV_DELIMITER = ";"; + + public static TableMetadata createTestTableMetadata() { + return TableMetadata.newBuilder() + .addColumn(TEST_COLUMN_1_PK, BIGINT) + .addColumn(TEST_COLUMN_2_CK, DataType.INT) + .addColumn(TEST_COLUMN_3_CK, DataType.BOOLEAN) + .addColumn(TEST_COLUMN_4, DataType.FLOAT) + .addColumn(TEST_COLUMN_5, DataType.DOUBLE) + .addColumn(TEST_COLUMN_6, DataType.TEXT) + .addColumn(TEST_COLUMN_7, BLOB) + .addColumn(Attribute.BEFORE_PREFIX + TEST_COLUMN_4, DataType.FLOAT) + .addColumn(Attribute.BEFORE_PREFIX + TEST_COLUMN_5, DataType.DOUBLE) + .addColumn(Attribute.BEFORE_PREFIX + TEST_COLUMN_6, DataType.TEXT) + .addColumn(Attribute.BEFORE_PREFIX + TEST_COLUMN_7, BLOB) + .addColumn(Attribute.ID, DataType.TEXT) + .addColumn(Attribute.STATE, DataType.INT) + .addColumn(Attribute.VERSION, DataType.INT) + .addColumn(Attribute.PREPARED_AT, BIGINT) + .addColumn(Attribute.COMMITTED_AT, BIGINT) + .addColumn(Attribute.BEFORE_ID, DataType.TEXT) + .addColumn(Attribute.BEFORE_STATE, DataType.INT) + .addColumn(Attribute.BEFORE_VERSION, DataType.INT) + .addColumn(Attribute.BEFORE_PREPARED_AT, BIGINT) + .addColumn(Attribute.BEFORE_COMMITTED_AT, BIGINT) + .addPartitionKey(TEST_COLUMN_1_PK) + .addClusteringKey(TEST_COLUMN_2_CK) + .addClusteringKey(TEST_COLUMN_3_CK) + .build(); + } + + public static ObjectNode getOutputDataWithMetadata() { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + rootNode.put(TEST_COLUMN_1_PK, TEST_VALUE_LONG); + rootNode.put(TEST_COLUMN_2_CK, TEST_VALUE_INT); + rootNode.put(TEST_COLUMN_3_CK, TEST_VALUE_BOOLEAN); + rootNode.put(TEST_COLUMN_4, TEST_VALUE_FLOAT); + rootNode.put(TEST_COLUMN_5, TEST_VALUE_DOUBLE); + rootNode.put(TEST_COLUMN_6, TEST_VALUE_TEXT); + rootNode.put(TEST_COLUMN_7, TEST_VALUE_BLOB); + rootNode.put(Attribute.BEFORE_PREFIX + TEST_COLUMN_4, TEST_VALUE_FLOAT); + rootNode.put(Attribute.BEFORE_PREFIX + TEST_COLUMN_5, TEST_VALUE_DOUBLE); + rootNode.put(Attribute.BEFORE_PREFIX + TEST_COLUMN_6, TEST_VALUE_TEXT); + rootNode.put(Attribute.BEFORE_PREFIX + TEST_COLUMN_7, TEST_VALUE_BLOB); + rootNode.put(Attribute.ID, TEST_VALUE_TX_ID); + rootNode.put(Attribute.STATE, TEST_VALUE_INT); + rootNode.put(Attribute.VERSION, TEST_VALUE_INT); + rootNode.put(Attribute.PREPARED_AT, TEST_VALUE_LONG); + rootNode.put(Attribute.COMMITTED_AT, TEST_VALUE_LONG); + rootNode.put(Attribute.BEFORE_ID, TEST_VALUE_TEXT); + rootNode.put(Attribute.BEFORE_STATE, TEST_VALUE_INT); + rootNode.put(Attribute.BEFORE_VERSION, TEST_VALUE_INT); + rootNode.put(Attribute.BEFORE_PREPARED_AT, TEST_VALUE_LONG); + rootNode.put(Attribute.BEFORE_COMMITTED_AT, TEST_VALUE_LONG); + return rootNode; + } + + public static ObjectNode getOutputDataWithoutMetadata() { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + rootNode.put(TEST_COLUMN_1_PK, TEST_VALUE_LONG); + rootNode.put(TEST_COLUMN_2_CK, TEST_VALUE_INT); + rootNode.put(TEST_COLUMN_3_CK, TEST_VALUE_BOOLEAN); + rootNode.put(TEST_COLUMN_4, TEST_VALUE_FLOAT); + rootNode.put(TEST_COLUMN_5, TEST_VALUE_DOUBLE); + rootNode.put(TEST_COLUMN_6, TEST_VALUE_TEXT); + rootNode.put(TEST_COLUMN_7, TEST_VALUE_BLOB); + return rootNode; + } + + public static List getColumnsListOfMetadata() { + List projectedColumns = new ArrayList<>(); + projectedColumns.add(TEST_COLUMN_1_PK); + projectedColumns.add(TEST_COLUMN_2_CK); + projectedColumns.add(TEST_COLUMN_3_CK); + projectedColumns.add(TEST_COLUMN_4); + projectedColumns.add(TEST_COLUMN_5); + projectedColumns.add(TEST_COLUMN_6); + projectedColumns.add(TEST_COLUMN_7); + projectedColumns.add(Attribute.BEFORE_PREFIX + TEST_COLUMN_4); + projectedColumns.add(Attribute.BEFORE_PREFIX + TEST_COLUMN_5); + projectedColumns.add(Attribute.BEFORE_PREFIX + TEST_COLUMN_6); + projectedColumns.add(Attribute.BEFORE_PREFIX + TEST_COLUMN_7); + projectedColumns.add(Attribute.ID); + projectedColumns.add(Attribute.STATE); + projectedColumns.add(Attribute.VERSION); + projectedColumns.add(Attribute.PREPARED_AT); + projectedColumns.add(Attribute.COMMITTED_AT); + projectedColumns.add(Attribute.BEFORE_ID); + projectedColumns.add(Attribute.BEFORE_STATE); + projectedColumns.add(Attribute.BEFORE_VERSION); + projectedColumns.add(Attribute.BEFORE_PREPARED_AT); + projectedColumns.add(Attribute.BEFORE_COMMITTED_AT); + return projectedColumns; + } + + public static Map getColumnData() { + Map columnData = new HashMap<>(); + columnData.put(TEST_COLUMN_1_PK, BIGINT); + columnData.put(TEST_COLUMN_2_CK, DataType.INT); + columnData.put(TEST_COLUMN_3_CK, DataType.BOOLEAN); + columnData.put(TEST_COLUMN_4, DataType.FLOAT); + columnData.put(TEST_COLUMN_5, DataType.DOUBLE); + columnData.put(TEST_COLUMN_6, DataType.TEXT); + columnData.put(TEST_COLUMN_7, BLOB); + columnData.put(Attribute.BEFORE_PREFIX + TEST_COLUMN_4, DataType.FLOAT); + columnData.put(Attribute.BEFORE_PREFIX + TEST_COLUMN_5, DataType.DOUBLE); + columnData.put(Attribute.BEFORE_PREFIX + TEST_COLUMN_6, DataType.TEXT); + columnData.put(Attribute.BEFORE_PREFIX + TEST_COLUMN_7, BLOB); + columnData.put(Attribute.ID, DataType.TEXT); + columnData.put(Attribute.STATE, DataType.INT); + columnData.put(Attribute.VERSION, DataType.INT); + columnData.put(Attribute.PREPARED_AT, BIGINT); + columnData.put(Attribute.COMMITTED_AT, BIGINT); + columnData.put(Attribute.BEFORE_ID, DataType.TEXT); + columnData.put(Attribute.BEFORE_STATE, DataType.INT); + columnData.put(Attribute.BEFORE_VERSION, DataType.INT); + columnData.put(Attribute.BEFORE_PREPARED_AT, BIGINT); + columnData.put(Attribute.BEFORE_COMMITTED_AT, BIGINT); + return columnData; + } + + public static Map> createTestValues() { + Map> values = new HashMap<>(); + values.put(TEST_COLUMN_1_PK, BigIntColumn.of(TEST_COLUMN_1_PK, TEST_VALUE_LONG)); + values.put(TEST_COLUMN_2_CK, IntColumn.of(TEST_COLUMN_2_CK, TEST_VALUE_INT)); + values.put(TEST_COLUMN_3_CK, BooleanColumn.of(TEST_COLUMN_3_CK, TEST_VALUE_BOOLEAN)); + values.put(TEST_COLUMN_4, FloatColumn.of(TEST_COLUMN_4, TEST_VALUE_FLOAT)); + values.put(TEST_COLUMN_5, DoubleColumn.of(TEST_COLUMN_5, TEST_VALUE_DOUBLE)); + values.put(TEST_COLUMN_6, TextColumn.of(TEST_COLUMN_6, TEST_VALUE_TEXT)); + values.put(TEST_COLUMN_7, BlobColumn.of(TEST_COLUMN_7, TEST_VALUE_BLOB)); + values.put( + Attribute.BEFORE_PREFIX + TEST_COLUMN_4, + FloatColumn.of(Attribute.BEFORE_PREFIX + TEST_COLUMN_4, TEST_VALUE_FLOAT)); + values.put( + Attribute.BEFORE_PREFIX + TEST_COLUMN_5, + DoubleColumn.of(Attribute.BEFORE_PREFIX + TEST_COLUMN_5, TEST_VALUE_DOUBLE)); + values.put( + Attribute.BEFORE_PREFIX + TEST_COLUMN_6, + TextColumn.of(Attribute.BEFORE_PREFIX + TEST_COLUMN_6, TEST_VALUE_TEXT)); + values.put( + Attribute.BEFORE_PREFIX + TEST_COLUMN_7, + BlobColumn.of(Attribute.BEFORE_PREFIX + TEST_COLUMN_7, TEST_VALUE_BLOB)); + values.put(Attribute.ID, TextColumn.of(Attribute.ID, TEST_VALUE_TX_ID)); + values.put(Attribute.STATE, IntColumn.of(Attribute.STATE, TEST_VALUE_INT)); + values.put(Attribute.VERSION, IntColumn.of(Attribute.VERSION, TEST_VALUE_INT)); + values.put(Attribute.PREPARED_AT, BigIntColumn.of(Attribute.PREPARED_AT, TEST_VALUE_LONG)); + values.put(Attribute.COMMITTED_AT, BigIntColumn.of(Attribute.COMMITTED_AT, TEST_VALUE_LONG)); + values.put(Attribute.BEFORE_ID, TextColumn.of(Attribute.BEFORE_ID, TEST_VALUE_TEXT)); + values.put(Attribute.BEFORE_STATE, IntColumn.of(Attribute.BEFORE_STATE, TEST_VALUE_INT)); + values.put(Attribute.BEFORE_VERSION, IntColumn.of(Attribute.BEFORE_VERSION, TEST_VALUE_INT)); + values.put( + Attribute.BEFORE_PREPARED_AT, + BigIntColumn.of(Attribute.BEFORE_PREPARED_AT, TEST_VALUE_LONG)); + values.put( + Attribute.BEFORE_COMMITTED_AT, + BigIntColumn.of(Attribute.BEFORE_COMMITTED_AT, TEST_VALUE_LONG)); + return values; + } + + public static String getSourceTestValue(DataType dataType) { + switch (dataType) { + case INT: + return Integer.toString(TEST_VALUE_INT); + case BIGINT: + return Long.toString(TEST_VALUE_LONG); + case FLOAT: + return DecimalUtil.convertToNonScientific(TEST_VALUE_FLOAT); + case DOUBLE: + return DecimalUtil.convertToNonScientific(TEST_VALUE_DOUBLE); + case BLOB: + return TEST_VALUE_BLOB_BASE64; + case BOOLEAN: + return Boolean.toString(TEST_VALUE_BOOLEAN); + case TEXT: + default: + return TEST_VALUE_TEXT; + } + } +} diff --git a/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/CollectionUtilTest.java b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/CollectionUtilTest.java new file mode 100644 index 0000000000..b054a55cef --- /dev/null +++ b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/CollectionUtilTest.java @@ -0,0 +1,28 @@ +package com.scalar.db.dataloader.core.util; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** Unit tests for CollectionUtils */ +class CollectionUtilTest { + + @Test + void areSameLength_CollectionsAllSameLength_ShouldReturnTrue() { + List listOne = new ArrayList<>(); + List listTwo = new ArrayList<>(); + boolean actual = CollectionUtil.areSameLength(listOne, listTwo); + assertThat(actual).isTrue(); + } + + @Test + void areSameLength_CollectionsDifferentLength_ShouldReturnFalse() { + List listOne = new ArrayList<>(); + List listTwo = new ArrayList<>(); + listTwo.add(5); + boolean actual = CollectionUtil.areSameLength(listOne, listTwo); + assertThat(actual).isFalse(); + } +} diff --git a/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/CsvUtilTest.java b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/CsvUtilTest.java new file mode 100644 index 0000000000..2afcfbcbe8 --- /dev/null +++ b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/CsvUtilTest.java @@ -0,0 +1,25 @@ +package com.scalar.db.dataloader.core.util; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +/** Unit tests for CsvUtils */ +class CsvUtilTest { + + @Test + void removeTrailingDelimiter_HasTrailingDelimiter_ShouldRemoveDelimiter() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("testing;"); + CsvUtil.removeTrailingDelimiter(stringBuilder, ";"); + assertThat(stringBuilder.toString()).isEqualTo("testing"); + } + + @Test + void removeTrailingDelimiter_DoesNotHaveTrailingDelimiter_ShouldNotRemoveAnything() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("testing"); + CsvUtil.removeTrailingDelimiter(stringBuilder, ";"); + assertThat(stringBuilder.toString()).isEqualTo("testing"); + } +} diff --git a/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/DecimalUtilTest.java b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/DecimalUtilTest.java new file mode 100644 index 0000000000..c99ad0866d --- /dev/null +++ b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/DecimalUtilTest.java @@ -0,0 +1,20 @@ +package com.scalar.db.dataloader.core.util; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class DecimalUtilTest { + @Test + void convertToNonScientific_withValidDoubleValue_shouldReturnProperStringValue() { + String expectedValue = "340.55"; + Double value = 340.55; + Assertions.assertEquals(expectedValue, DecimalUtil.convertToNonScientific(value)); + } + + @Test + void convertToNonScientific_withValidFloatValue_shouldReturnProperStringValue() { + String expectedValue = "356"; + Float value = 356F; + Assertions.assertEquals(expectedValue, DecimalUtil.convertToNonScientific(value)); + } +} diff --git a/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/PathUtilTest.java b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/PathUtilTest.java new file mode 100644 index 0000000000..85d3ed1ce7 --- /dev/null +++ b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/PathUtilTest.java @@ -0,0 +1,35 @@ +package com.scalar.db.dataloader.core.util; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class PathUtilTest { + + @Test + void ensureTrailingSeparator_nullPath_returnsEmptyString() { + String result = PathUtil.ensureTrailingSeparator(null); + Assertions.assertEquals("", result); + } + + @Test + void ensureTrailingSeparator_emptyPath_returnsEmptyString() { + String result = PathUtil.ensureTrailingSeparator(""); + Assertions.assertEquals("", result); + } + + @Test + void ensureTrailingSlash_pathWithoutTrailingSlash_addsTrailingSeparator() { + String path = "/path/to/directory"; + String expectedResult = "/path/to/directory/"; + String result = PathUtil.ensureTrailingSeparator(path); + Assertions.assertEquals(expectedResult, result); + } + + @Test + void ensureTrailingSlash_pathWithTrailingSeparator_returnsOriginalPath() { + String path = "/path/to/directory/"; + String expectedResult = "/path/to/directory/"; + String result = PathUtil.ensureTrailingSeparator(path); + Assertions.assertEquals(expectedResult, result); + } +} diff --git a/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/RuntimeUtilTest.java b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/RuntimeUtilTest.java new file mode 100644 index 0000000000..8b03c0c0ab --- /dev/null +++ b/data-loader/core/src/test/java/com/scalar/db/dataloader/core/util/RuntimeUtilTest.java @@ -0,0 +1,24 @@ +package com.scalar.db.dataloader.core.util; + +import static com.scalar.db.common.error.CoreError.DATA_LOADER_ERROR_METHOD_NULL_ARGUMENT; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +/** RuntimeUtils unit tests */ +class RuntimeUtilTest { + + @Test + void checkNotNull_HasNullValues_ShouldThrowException() { + assertThatThrownBy(() -> RuntimeUtil.checkNotNull(null, null)) + .isExactlyInstanceOf(NullPointerException.class) + .hasMessage(DATA_LOADER_ERROR_METHOD_NULL_ARGUMENT.buildMessage()); + } + + @Test + void checkNotNull_HasNoNullValues_ShouldNotThrowException() { + String string = "1"; + Object object = new Object(); + RuntimeUtil.checkNotNull(string, object); + } +}