Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use commons module in server #272

Merged
merged 16 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
jsync-swirlds marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -38,104 +38,73 @@ public final class FileUtilities {
* Default permissions are set to: rw-r--r--
*/
private static final FileAttribute<Set<PosixFilePermission>> DEFAULT_FILE_PERMISSIONS =
PosixFilePermissions.asFileAttribute(
Set.of(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.GROUP_READ,
PosixFilePermission.OTHERS_READ));
PosixFilePermissions.asFileAttribute(Set.of(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.GROUP_READ,
PosixFilePermission.OTHERS_READ));

/**
* Default folder permissions for new folders.
* <p>
* Default permissions are set to: rwxr-xr-x
*/
private static final FileAttribute<Set<PosixFilePermission>> DEFAULT_FOLDER_PERMISSIONS =
PosixFilePermissions.asFileAttribute(
Set.of(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OWNER_EXECUTE,
PosixFilePermission.GROUP_READ,
PosixFilePermission.GROUP_EXECUTE,
PosixFilePermission.OTHERS_READ,
PosixFilePermission.OTHERS_EXECUTE));
PosixFilePermissions.asFileAttribute(Set.of(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OWNER_EXECUTE,
PosixFilePermission.GROUP_READ,
PosixFilePermission.GROUP_EXECUTE,
PosixFilePermission.OTHERS_READ,
PosixFilePermission.OTHERS_EXECUTE));

/**
* Log message template used when a path is not created because a file
* or folder already exists at the requested path.
*/
private static final String PRE_EXISTING_FOLDER_MESSAGE =
"Requested %s [%s] not created because %s already exists at %s";

/**
* Create a new path (folder or file) if it does not exist.
* Any folders or files created will use default permissions.
* Create a new path (folder) if it does not exist.
* Any folders created will use default permissions.
*
* @param toCreate valid, non-null instance of {@link Path} to be created
* @param logLevel valid, non-null instance of {@link System.Logger.Level} to use
* @param semanticPathName valid, non-blank {@link String} used for logging that represents the
* desired path semantically
* @param createDir {@link Boolean} value if we should create a directory or a file
* @throws IOException if the path cannot be created
*/
public static void createPathIfNotExists(
public static void createFolderPathIfNotExists(
@NonNull final Path toCreate,
@NonNull final System.Logger.Level logLevel,
@NonNull final String semanticPathName,
final boolean createDir)
@NonNull final String semanticPathName)
throws IOException {
createPathIfNotExists(
toCreate,
logLevel,
DEFAULT_FILE_PERMISSIONS,
DEFAULT_FOLDER_PERMISSIONS,
semanticPathName,
createDir);
createFolderPathIfNotExists(toCreate, logLevel, DEFAULT_FOLDER_PERMISSIONS, semanticPathName);
}

/**
* Create a new path (folder or file) if it does not exist.
* Create a new path (folder) if it does not exist.
*
* @param toCreate The path to be created.
* @param logLevel The logging level to use when logging this event.
* @param filePermissions Permissions to use when creating a new file.
* @param folderPermissions Permissions to use when creating a new folder.
* @param semanticPathName A name to represent the path in a logging
* @param permissions Permissions to use when creating the path.
* @param semanticPathName A name (non-blank) to represent the path in a logging
* statement.
* @param createDir A flag indicating we should create a directory
* (true) or a file (false)
* @throws IOException if the path cannot be created due to a filesystem
* error.
*/
public static void createPathIfNotExists(
public static void createFolderPathIfNotExists(
@NonNull final Path toCreate,
@NonNull final System.Logger.Level logLevel,
@NonNull final FileAttribute<Set<PosixFilePermission>> filePermissions,
@NonNull final FileAttribute<Set<PosixFilePermission>> folderPermissions,
@NonNull final String semanticPathName,
final boolean createDir)
@NonNull final FileAttribute<Set<PosixFilePermission>> permissions,
@NonNull final String semanticPathName)
throws IOException {
Objects.requireNonNull(toCreate);
Objects.requireNonNull(logLevel);
Objects.requireNonNull(filePermissions);
Objects.requireNonNull(folderPermissions);
Objects.requireNonNull(permissions);
StringUtilities.requireNotBlank(semanticPathName);
final String requestedType = createDir ? "directory" : "file";
if (Files.notExists(toCreate)) {
if (createDir) {
Files.createDirectories(toCreate, folderPermissions);
} else {
Files.createFile(toCreate, filePermissions);
}
final String logMessage =
"Created %s [%s] at %s".formatted(requestedType, semanticPathName, toCreate);
Files.createDirectories(toCreate, permissions);
final String logMessage = "Created [%s] at '%s'".formatted(semanticPathName, toCreate);
LOGGER.log(logLevel, logMessage);
} else {
final String actualType = Files.isDirectory(toCreate) ? "directory" : "file";
final String logMessage =
PRE_EXISTING_FOLDER_MESSAGE.formatted(
requestedType, semanticPathName, actualType, toCreate);
final String logMessage = "Requested [%s] not created because the directory already exists at '%s'"
.formatted(semanticPathName, toCreate);
LOGGER.log(logLevel, logMessage);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,29 @@
import java.lang.System.Logger.Level;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

/**
* Tests for {@link FileUtilities} functionality.
*/
class FileUtilitiesTest {
private static final String FILE_WITH_UNRECOGNIZED_EXTENSION = "src/test/resources/nonexistent.unrecognized";

/**
* This test aims to verify that a folder path will be created for a given
* path if it does not exist. First we assert that the path we want to make
* does not exist, then we run the actual method and assert that the path
* was created and is an empty folder.
*
* @param tempDir junit temp dir
* @throws IOException propagated from {@link FileUtilities#createFolderPathIfNotExists(Path, Level, String)}
*/
@Test
void test_createPathIfNotExists_CreatesDirIfDoesNotExist(@TempDir final Path tempDir) throws IOException {
void testCreateFolderPathIfNotExists(@TempDir final Path tempDir) throws IOException {
final String newDir = "newDir";
final Path toCreate = tempDir.resolve(newDir);

Expand All @@ -44,15 +55,24 @@ void test_createPathIfNotExists_CreatesDirIfDoesNotExist(@TempDir final Path tem
assertThat(toCreate).doesNotExist();

// run actual
FileUtilities.createPathIfNotExists(toCreate, Level.ERROR, "test dir 1", true);
FileUtilities.createFolderPathIfNotExists(toCreate, Level.ERROR, "test dir 1");

// assert
assertThat(toCreate).exists().isDirectory();
assertThat(tempDir.toFile().listFiles()).hasSize(1).contains(toCreate.toFile());
}

/**
* This test aims to verify that a folder path will not be created for a
* given path if it already exists. First we assert that the path we want to
* make already exists, then we run the actual method and assert that the
* path was unchanged and is an empty folder and nothing else was created.
*
* @param tempDir junit temp dir
* @throws IOException propagated from {@link FileUtilities#createFolderPathIfNotExists(Path, Level, String)}
*/
@Test
void test_createPathIfNotExists_DoesNotCreateDirIfExists(@TempDir final Path tempDir) throws IOException {
void testSkipFolderCreationIfPathExists(@TempDir final Path tempDir) throws IOException {
final String newDir = "newDir";
final Path toCreate = tempDir.resolve(newDir);

Expand All @@ -70,60 +90,24 @@ void test_createPathIfNotExists_DoesNotCreateDirIfExists(@TempDir final Path tem
assertThat(toCreate).exists().isDirectory();

// run actual
FileUtilities.createPathIfNotExists(toCreate, Level.ERROR, "test dir 1", true);
FileUtilities.createFolderPathIfNotExists(toCreate, Level.ERROR, "test dir 1");

// assert
assertThat(toCreate).exists().isDirectory();
assertThat(tempDirAsFile.listFiles()).hasSize(1).contains(toCreateAsFile);
}

@Test
void test_createPathIfNotExists_CreatesFileIfDoesNotExist(@TempDir final Path tempDir) throws IOException {
final String newFile = "newFile";
final Path toCreate = tempDir.resolve(newFile);

// ensure the temp directory is empty in the beginning
assertThat(tempDir).isEmptyDirectory();
assertThat(toCreate).doesNotExist();

// run actual
FileUtilities.createPathIfNotExists(toCreate, Level.ERROR, "test file 1", false);

// assert
assertThat(toCreate).exists().isEmptyFile();
assertThat(tempDir.toFile().listFiles()).hasSize(1).contains(toCreate.toFile());
}

@Test
void test_createPathIfNotExists_DoesNotCreateFileIfExists(@TempDir final Path tempDir) throws IOException {
final String newFile = "newFile";
final Path toCreate = tempDir.resolve(newFile);

// ensure the temp directory is empty in the beginning
assertThat(tempDir).isEmptyDirectory();
assertThat(toCreate).doesNotExist();

// create 'newFile'
Files.createFile(toCreate);

// ensure the temp directory contains only 'newFile' before running actual
final File tempDirAsFile = tempDir.toFile();
final File toCreateAsFile = toCreate.toFile();
assertThat(tempDirAsFile.listFiles()).hasSize(1).contains(toCreateAsFile);
assertThat(toCreate).exists().isEmptyFile();

// run actual
FileUtilities.createPathIfNotExists(toCreate, Level.ERROR, "test file 1", false);

// assert
assertThat(toCreate).exists().isEmptyFile();
assertThat(tempDirAsFile.listFiles()).hasSize(1).contains(toCreateAsFile);
}

/**
* This test aims to verify that reading a gzip file that exists and is
* valid, will return a byte array with the expected content.
*
* @param filePath parameterized, to read gzip files from
* @param expectedContent parameterized, expected content after reading the file
* @throws IOException propagated from {@link FileUtilities#readGzipFileUnsafe(Path)}
*/
@ParameterizedTest
@MethodSource("validGzipFiles")
void test_readGzipFileUnsafe_ReturnsByteArrayWithValidContentForValidGzipFile(
final Path filePath, final String expectedContent) throws IOException {
void testReadGzipFileUnsafe(final Path filePath, final String expectedContent) throws IOException {
final byte[] actualContent = FileUtilities.readGzipFileUnsafe(filePath);
assertThat(actualContent)
.isNotNull()
Expand All @@ -134,63 +118,77 @@ void test_readGzipFileUnsafe_ReturnsByteArrayWithValidContentForValidGzipFile(
.isEqualTo(expectedContent);
}

/**
* This test aims to verify that reading an invalid gzip file throws an
* {@link IOException}.
*
* @param filePath parameterized, to read gzip files from
*/
@ParameterizedTest
@MethodSource("invalidFiles")
void test_readGzipFileUnsafe_ThrowsIOExceptionForInvalidGzipFile(final Path filePath) {
void testReadGzipFileUnsafeThrows(final Path filePath) {
assertThatIOException().isThrownBy(() -> FileUtilities.readGzipFileUnsafe(filePath));
}

/**
* This test aims to verify that reading a file that exists and is valid and
* is found by the extension parameter, will return a byte array with the
* expected content.
*
* @param filePath parameterized, to read files from
* @param expectedContent parameterized, expected content after reading the file
* @throws IOException propagated from {@link FileUtilities#readFileBytesUnsafe(Path)}
* and {@link FileUtilities#readFileBytesUnsafe(Path, String, String)}
*/
@ParameterizedTest
@MethodSource({"validGzipFiles", "validBlkFiles"})
void test_readFileBytesUnsafe_ReturnsByteArrayWithValidContentForValidFile(
final Path filePath, final String expectedContent) throws IOException {
final byte[] actualContent = FileUtilities.readFileBytesUnsafe(filePath);
assertThat(actualContent)
.isNotNull()
.isNotEmpty()
.asString()
.isNotNull()
.isNotBlank()
.isEqualTo(expectedContent);
}

@Test
void test_readFileBytesUnsafe_ReturnsNullByteArrayWhenExtensionIsNotRecognized() throws IOException {
final byte[] actualContent = FileUtilities.readFileBytesUnsafe(Path.of(FILE_WITH_UNRECOGNIZED_EXTENSION));
assertThat(actualContent).isNull();
}

@ParameterizedTest
@MethodSource("invalidFiles")
void test_readFileBytesUnsafe_ThrowsIOExceptionForInvalidGzipFile(final Path filePath) {
assertThatIOException().isThrownBy(() -> FileUtilities.readFileBytesUnsafe(filePath));
}
void testReadFileBytesUnsafe(final Path filePath, final String expectedContent) throws IOException {
final Consumer<byte[]> asserts = actual -> {
assertThat(actual)
.isNotNull()
.isNotEmpty()
.asString()
.isNotNull()
.isNotBlank()
.isEqualTo(expectedContent);
};

@ParameterizedTest
@MethodSource({"validGzipFiles", "validBlkFiles"})
void test_readFileBytesUnsafe_ReturnsByteArrayWithValidContentForValidFileWithGivenExtension(
final Path filePath, final String expectedContent) throws IOException {
final byte[] actualContent = FileUtilities.readFileBytesUnsafe(filePath, ".blk", ".gz");
assertThat(actualContent)
.isNotNull()
.isNotEmpty()
.asString()
.isNotNull()
.isNotBlank()
.isEqualTo(expectedContent);
assertThat(actualContent).satisfies(asserts);

// overloaded has same extensions as above
final byte[] actualContentOverloaded = FileUtilities.readFileBytesUnsafe(filePath);
assertThat(actualContentOverloaded).satisfies(asserts);
}

/**
* This test aims to verify that reading a file that is not recognized by
* the block file extension we provide, will return null.
*
* @throws IOException propagated from {@link FileUtilities#readFileBytesUnsafe(Path)}
* and {@link FileUtilities#readFileBytesUnsafe(Path, String, String)}
*/
@Test
void test_readFileBytesUnsafe_ReturnsNullByteArrayWhenExtensionIsNotRecognizedWithGivenExtension()
throws IOException {
final byte[] actualContent =
FileUtilities.readFileBytesUnsafe(Path.of(FILE_WITH_UNRECOGNIZED_EXTENSION), ".blk", ".gz");
void testReadFileBytesUnsafeReturnsNull() throws IOException {
final Path path = Path.of("src/test/resources/nonexistent.unrecognized");

final byte[] actualContent = FileUtilities.readFileBytesUnsafe(path, ".blk", ".gz");
assertThat(actualContent).isNull();

final byte[] actualContentOverloaded = FileUtilities.readFileBytesUnsafe(path);
assertThat(actualContentOverloaded).isNull();
}

/**
* This test aims to verify that reading an invalid file, be that it is
* in some way corrupted or nonexistent, will throw an {@link IOException}.
*
* @param filePath parameterized, to read block files from
*/
@ParameterizedTest
@MethodSource("invalidFiles")
void test_readFileBytesUnsafe_ThrowsIOExceptionForInvalidGzipFileWithGivenExtension(final Path filePath) {
void testReadFileBytesUnsafeThrows(final Path filePath) {
assertThatIOException().isThrownBy(() -> FileUtilities.readFileBytesUnsafe(filePath));
assertThatIOException().isThrownBy(() -> FileUtilities.readFileBytesUnsafe(filePath, ".blk", ".gz"));
}

Expand All @@ -208,6 +206,8 @@ private static Stream<Arguments> validBlkFiles() {

private static Stream<Arguments> invalidFiles() {
return Stream.of(
Arguments.of("src/test/resources/invalid1.gz"), Arguments.of("src/test/resources/nonexistent.gz"));
Arguments.of("src/test/resources/invalid1.gz"),
Arguments.of("src/test/resources/nonexistent.gz"),
Arguments.of("src/test/resources/nonexistent.blk"));
}
}
Loading
Loading