diff --git a/.github/workflows/dev_build.yml b/.github/workflows/dev_build.yml
index dfc187c..e514591 100644
--- a/.github/workflows/dev_build.yml
+++ b/.github/workflows/dev_build.yml
@@ -11,8 +11,6 @@ name: Util Development Build
on:
push:
branches: [ "dev" ]
- pull_request:
- branches-ignore: ["main"]
jobs:
dev-build:
diff --git a/.github/workflows/dev_pr_build.yml b/.github/workflows/dev_pr_build.yml
new file mode 100644
index 0000000..083b448
--- /dev/null
+++ b/.github/workflows/dev_pr_build.yml
@@ -0,0 +1,34 @@
+# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
+
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+
+name: Util Development PR Build
+
+on:
+ pull_request:
+ branches-ignore: ["main"]
+
+jobs:
+ dev-build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Util
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+ cache: maven
+
+ - name: Build Util
+ run: mvn clean install --file pom.xml
+
+ # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
+ # - name: Update dependency graph
+ # uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
diff --git a/pom.xml b/pom.xml
index 483fd27..75c618a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
+ * The issue is that the relative path to the resource folder is dependant on + * what folder you are executing the test from. This can vary from IDE to IDE + * and even from where you run the maven command if using maven to run the unit + * tests. + *
+ * This solves the issues by obtaining an absolute reference to the resource + * directory by using the class loader and an empty resource. + *
+ * In addition to the above, this class also provides convience methods to + * validate file paths and directory paths, and create directories and files. + */ public class ResourceHelper { - private ResourceHelper() { - } - + /** + * Given a class ref, uses the class loader from the classref and an empty + * resource name to obtain a URI and then creates and returns a Path from that + * URI. This path will be an absolute path and not a relative path. Because it + * uses the classloader, it no longer matters from where this method gets called + * with respect to the java command used to call it. + *
+ * This solves the problem of unit tests that use files from the + * src/test/resources sometimes failing because of the directory from which the + * test was executed. + * + * @throws RuntimeException if the url provided by the classloader cannot be + * converted to a valid URI. Note that the + * RuntimeException wraps the thrown + * {@link URISyntaxException} + */ public static Path getResourceDir(Class> classRef) { return Path.of(getURI(classRef.getClassLoader().getResource(""))); } - protected static URI getURI(URL url) { - try { - return url.toURI(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); + /** + * Given a path, creates the directory. This internally calls + * dirPath.toFile().mkdirs(). + */ + public static Path createDirectory(Path dirPath) { + if (dirPath.toFile().exists()) { + return dirPath; } - } - public static Path makeOutputDir(Path dirPath) { dirPath.toFile().mkdirs(); return dirPath; } - public static Path makeOutputDir(Path basepath, String subDir) { - Path dirPath = basepath.resolve(subDir); + /** + * Given a string that is a valid path, creates the directory. + *
+ * calls {@link ResourceHelper#createDirectory(Path)} + */ + public static Path createDirectory(String directory) { + Path dirPath = Path.of(directory); + + return createDirectory(dirPath); + } + + /** + * Given a base directory path and a sub directory, resolves the sub directory + * against the base path and calls {@link ResourceHelper#createDirectory(Path)} + *
+ * returns the resolved path + */ + public static Path createDirectory(Path baseDirPath, String subDir) { + Path dirPath = baseDirPath.resolve(subDir); + + return createDirectory(dirPath); + } + + /** + * Given a base directory and a sub directory, resolves the sub directory + * against the base path and calls {@link ResourceHelper#createDirectory(Path)} + *
+ * returns the resolved path + */ + public static Path createDirectory(String baseDir, String subDir) { + Path dirPath = Path.of(baseDir, subDir); + + return createDirectory(dirPath); + } + + /** + * Given a directory path and a file name, creates a file with the given name in + * the given directory. + * + * @throws RuntimeException if the file cannot be created. Note that the + * RuntimeException wraps the thrown + * {@link IOException} + */ + public static void createFile(Path directory, String fileName) { + + File file = directory.resolve(fileName).toFile(); + + if (file.exists()) { + return; + } + + _createFile(file); + } + + /** + * Given a directory path and a file name, creates a file with the given name in + * the given directory. + *
+ * Deletes the file if it exists before creating the file. + * + * @throws RuntimeException if the file cannot be created. Note that the + * RuntimeException wraps the thrown + * {@link IOException} + */ + public static void createNewFile(Path directory, String fileName) { + + File file = directory.resolve(fileName).toFile(); + + if (file.exists()) { + file.delete(); + } + + _createFile(file); + } + + /** + * Given a file path, validates that the file exists. + *
+ * calls {@link ResourceHelper#validateFilePath(Path)} + * + * @throws ContractException + *
+ * calls {@link ResourceHelper#validateFilePath(Path)} + * + * @throws ContractException {@link ResourceError#FILE_PATH_IS_DIRECTORY} if the + * file path refers to a directory + */ + public static Path validateFilePath(String file) { + Path filePath = Path.of(file); + + validateFilePath(filePath); + + return filePath; } - public static Path validatePath(String path, boolean isOutput) { - Path maybePath = Path.of(path); - File maybeFile = maybePath.toFile(); + /** + * Given a file path, validates that the file path exists. + * + * @throws ContractException {@link ResourceError#FILE_PATH_IS_DIRECTORY} if the + * file path refers to a directory + */ + public static Path validateFilePath(Path filePath) { + File file = filePath.toFile(); - boolean isDirectory = maybeFile.isDirectory(); - boolean isFile = maybeFile.isFile(); + if (file.isDirectory()) { + throw new ContractException(ResourceError.FILE_PATH_IS_DIRECTORY); + } - // if the given string corresponds to a file that exists, return path - if (isFile) { - return maybePath; + if (!file.exists()) { + validateDirectoryPath(filePath.getParent()); } - // if file does not exist, ensure the path is not a directory and that the - // parent directory of the outputFile exists. - if (isOutput && !isDirectory) { - Path parentPath = maybePath.getParent(); + return filePath; + } + + /** + * Given a directory path, validates that the directory path exists. + *
+ * calls {@link ResourceHelper#validateDirectoryPath(Path)} + * + * @throws ContractException {@link ResourceError#DIRECTORY_PATH_IS_FILE} if the + * directory path refers to a file + */ + public static Path validateDirectoryPath(String directory) { + Path maybePath = Path.of(directory); + + validateDirectoryPath(maybePath); + + return maybePath; + } + + /** + * Given a directory path, validates that the directory path exists. + *
+ * If it does not exists, attempts to create it + * + * @throws ContractException {@link ResourceError#DIRECTORY_PATH_IS_FILE} if the + * directory path refers to a file + */ + public static Path validateDirectoryPath(Path directoryPath) { + File maybeFile = directoryPath.toFile(); + + if (maybeFile.isFile()) { + throw new ContractException(ResourceError.DIRECTORY_PATH_IS_FILE); + } - if (Files.exists(parentPath)) { - return maybePath; - } + if (!maybeFile.exists()) { + createDirectory(directoryPath); } - // otherwise throw an exception - throw new ContractException(ResourceError.UNKNOWN_FILE, path); + return directoryPath; + } + + /** + * Given a url, converts it to a URI + */ + protected static URI getURI(URL url) { + try { + return url.toURI(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } } - protected static void createFile(File file) { + /** + * Given a file, attempts to create the file package access for testing + */ + static void _createFile(File file) { try { file.createNewFile(); } catch (IOException e) { @@ -84,4 +278,7 @@ protected static void createFile(File file) { } } + private ResourceHelper() { + } + } diff --git a/src/test/java/gov/hhs/aspr/ms/util/resourcehelper/AT_ResourceHelper.java b/src/test/java/gov/hhs/aspr/ms/util/resourcehelper/AT_ResourceHelper.java index 5354b7f..88899bd 100644 --- a/src/test/java/gov/hhs/aspr/ms/util/resourcehelper/AT_ResourceHelper.java +++ b/src/test/java/gov/hhs/aspr/ms/util/resourcehelper/AT_ResourceHelper.java @@ -1,6 +1,7 @@ package gov.hhs.aspr.ms.util.resourcehelper; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -19,6 +20,29 @@ public class AT_ResourceHelper { + private class IOExceptionFile extends File { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public IOExceptionFile(String pathname) { + super(pathname); + } + + public IOExceptionFile(File file) { + super(file, pathSeparator); + } + + @Override + public boolean createNewFile() throws IOException { + + throw new IOException(); + } + + } + @Test @UnitTestMethod(target = ResourceHelper.class, name = "getResourceDir", args = { Class.class }) public void testGetResourceDir() throws MalformedURLException { @@ -40,105 +64,296 @@ public void testGetResourceDir() throws MalformedURLException { } @Test - @UnitTestMethod(target = ResourceHelper.class, name = "makeOutputDir", args = { Path.class }) - public void testMakeTestOutputDir() { + @UnitTestMethod(target = ResourceHelper.class, name = "createDirectory", args = { Path.class }) + public void testCreateDirectory() { Path path = ResourceHelper.getResourceDir(this.getClass()).resolve("additional-folder"); - Path newPath = ResourceHelper.makeOutputDir(path); + Path newPath = ResourceHelper.createDirectory(path); - assertNotNull(newPath); assertTrue(newPath.toFile().exists()); } @Test - @UnitTestMethod(target = ResourceHelper.class, name = "makeOutputDir", args = { Path.class, String.class }) - public void testMakeTestOutputDir_SubDir() { + @UnitTestMethod(target = ResourceHelper.class, name = "createDirectory", args = { String.class }) + public void testCreateDirectory_String() { Path path = ResourceHelper.getResourceDir(this.getClass()).resolve("additional-folder"); - Path newPath = ResourceHelper.makeOutputDir(path, "subFolder"); + Path newPath = ResourceHelper.createDirectory(path.toString()); - assertNotNull(newPath); assertTrue(newPath.toFile().exists()); } - private class IOExceptionFile extends File { - - /** - * - */ - private static final long serialVersionUID = 1L; + @Test + @UnitTestMethod(target = ResourceHelper.class, name = "createDirectory", args = { Path.class, String.class }) + public void testCreateDirectory_Path_String() { + Path path = ResourceHelper.getResourceDir(this.getClass()); - public IOExceptionFile(String pathname) { - super(pathname); - } + Path newPath = ResourceHelper.createDirectory(path, "additional-folder"); - public IOExceptionFile(File file) { - super(file, pathSeparator); - } + assertTrue(newPath.toFile().exists()); + } - @Override - public boolean createNewFile() throws IOException { + @Test + @UnitTestMethod(target = ResourceHelper.class, name = "createDirectory", args = { String.class, String.class }) + public void testCreateDirectory_String_String() { + Path path = ResourceHelper.getResourceDir(this.getClass()); - throw new IOException(); - } + Path newPath = ResourceHelper.createDirectory(path.toString(), "additional-folder"); + assertTrue(newPath.toFile().exists()); } @Test - @UnitTestMethod(target = ResourceHelper.class, name = "createOutputFile", args = { Path.class, - String.class }) - public void testCreateTestOutputFile() throws IOException { + @UnitTestMethod(target = ResourceHelper.class, name = "createFile", args = { Path.class, String.class }) + public void testCreateFile() throws IOException { Path path = ResourceHelper.getResourceDir(this.getClass()); - Path newPath = ResourceHelper.makeOutputDir(path); + Path newPath = ResourceHelper.createDirectory(path); String fileName = "foo.txt"; - ResourceHelper.createOutputFile(newPath, fileName); + ResourceHelper.createFile(newPath, fileName); assertTrue(newPath.resolve(fileName).toFile().exists()); - // test for delete/recreat file if it exists - ResourceHelper.createOutputFile(newPath, fileName); + // preconditions + // the file cannot be created + // force an IOException + RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> { + IOExceptionFile testFile = new IOExceptionFile(newPath.resolve(fileName).toFile()); + ResourceHelper._createFile(testFile); + }); + + assertTrue(runtimeException.getCause() instanceof IOException); + + newPath.resolve(fileName).toFile().delete(); + } + + @Test + @UnitTestMethod(target = ResourceHelper.class, name = "createNewFile", args = { Path.class, String.class }) + public void testCreateNewFile() throws IOException { + Path path = ResourceHelper.getResourceDir(this.getClass()); + + Path newPath = ResourceHelper.createDirectory(path); + + String fileName = "foo.txt"; + ResourceHelper.createNewFile(newPath, fileName); + + assertTrue(newPath.resolve(fileName).toFile().exists()); // preconditions + // the file cannot be created // force an IOException RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> { IOExceptionFile testFile = new IOExceptionFile(newPath.resolve(fileName).toFile()); - ResourceHelper.createFile(testFile); + ResourceHelper._createFile(testFile); }); assertTrue(runtimeException.getCause() instanceof IOException); + + newPath.resolve(fileName).toFile().delete(); } @Test - @UnitTestMethod(target = ResourceHelper.class, name = "validatePath", args = { String.class, boolean.class }) - public void testValidatePath() { + @UnitTestMethod(target = ResourceHelper.class, name = "validateFile", args = { String.class }) + public void testValidateFile() { Path resourceDir = ResourceHelper.getResourceDir(getClass()); - // path is a file that is not output and exists - String path = resourceDir.resolve("resourceHelper.json").toAbsolutePath().toString(); - Path expectedPath = resourceDir.resolve("resourceHelper.json").toAbsolutePath(); - assertEquals(expectedPath, ResourceHelper.validatePath(path, false)); - - // file is an output file that does not exist, but the directory the file - // resides in exists - ResourceHelper.makeOutputDir(resourceDir, "testOutput"); - path = resourceDir.resolve("testOutput").resolve("resourceHelper.json").toAbsolutePath().toString(); - expectedPath = resourceDir.resolve("testOutput").resolve("resourceHelper.json").toAbsolutePath(); - assertEquals(expectedPath, ResourceHelper.validatePath(path, true)); + Path dirPath = resourceDir; + String fileName = "validateFile.json"; + + ResourceHelper.createFile(dirPath, fileName); + + String filePath = dirPath.resolve(fileName).toAbsolutePath().toString(); + assertTrue(ResourceHelper.validateFile(filePath).toFile().exists()); // preconditions - // File does not exist and it is not an outputFile + // file path points to a directory ContractException contractException = assertThrows(ContractException.class, () -> { - ResourceHelper.validatePath("testOutput", false); + ResourceHelper.validateFile(resourceDir.toString()); + }); + + assertEquals(ResourceError.FILE_PATH_IS_DIRECTORY, contractException.getErrorType()); + + // file does not exist + contractException = assertThrows(ContractException.class, () -> { + ResourceHelper.validateFile(dirPath.resolve("unknwonfile.txt")); }); assertEquals(ResourceError.UNKNOWN_FILE, contractException.getErrorType()); - // is output but parent directory does not exist + dirPath.resolve(fileName).toFile().delete(); + } + + @Test + @UnitTestMethod(target = ResourceHelper.class, name = "validateFile", args = { Path.class }) + public void testValidateFile_Path() { + Path resourceDir = ResourceHelper.getResourceDir(getClass()); + Path dirPath = resourceDir; + String fileName = "validateFile.json"; + + ResourceHelper.createFile(dirPath, fileName); + + Path filePath = dirPath.resolve(fileName).toAbsolutePath(); + assertTrue(ResourceHelper.validateFile(filePath).toFile().exists()); + + // preconditions + // file path points to a directory + ContractException contractException = assertThrows(ContractException.class, () -> { + ResourceHelper.validateFile(resourceDir.toString()); + }); + + assertEquals(ResourceError.FILE_PATH_IS_DIRECTORY, contractException.getErrorType()); + + // file does not exist contractException = assertThrows(ContractException.class, () -> { - ResourceHelper.validatePath(resourceDir.resolve("nonExistantDir").resolve("file.json").toString(), true); + ResourceHelper.validateFile(dirPath.resolve("unknwonfile.txt")); }); assertEquals(ResourceError.UNKNOWN_FILE, contractException.getErrorType()); + + dirPath.resolve(fileName).toFile().delete(); + } + + @Test + @UnitTestMethod(target = ResourceHelper.class, name = "validateFilePath", args = { String.class }) + public void testValidateFilePath() { + Path resourceDir = ResourceHelper.getResourceDir(getClass()); + Path dirPath = resourceDir; + String fileName = "validateFilePath.json"; + String subDirName = "temp"; + + // delete any old test files + dirPath.resolve(fileName).toFile().delete(); + + // path is a file that does not exist but directory exists + String filePath = dirPath.resolve(fileName).toAbsolutePath().toString(); + Path path = ResourceHelper.validateFilePath(filePath); + assertFalse(path.toFile().exists()); + + // create file + ResourceHelper.createFile(dirPath, fileName); + + // path is a file that does exist + path = ResourceHelper.validateFilePath(filePath); + assertTrue(path.toFile().exists()); + + // path is a file that does not exist and the directory does not exist + dirPath = resourceDir.resolve(subDirName); + filePath = dirPath.resolve(fileName).toAbsolutePath().toString(); + path = ResourceHelper.validateFilePath(filePath); + // file does not exist + assertFalse(path.toFile().exists()); + // directory exists because it was created + assertTrue(dirPath.toFile().exists()); + + // preconditions + // File path points to a directory + ContractException contractException = assertThrows(ContractException.class, () -> { + ResourceHelper.validateFilePath(resourceDir); + }); + + assertEquals(ResourceError.FILE_PATH_IS_DIRECTORY, contractException.getErrorType()); + + resourceDir.resolve(fileName).toFile().delete(); + dirPath.resolve(fileName).toFile().delete(); + } + + @Test + @UnitTestMethod(target = ResourceHelper.class, name = "validateFilePath", args = { Path.class }) + public void testValidateFilePath_Path() { + Path resourceDir = ResourceHelper.getResourceDir(getClass()); + Path dirPath = resourceDir; + String fileName = "validateFilePath.json"; + String subDirName = "temp"; + + // delete any old test files + dirPath.resolve(fileName).toFile().delete(); + + // path is a file that does not exist but directory exists + Path filePath = dirPath.resolve(fileName).toAbsolutePath(); + Path path = ResourceHelper.validateFilePath(filePath); + assertFalse(path.toFile().exists()); + + // create file + ResourceHelper.createFile(dirPath, fileName); + + // path is a file that does exist + path = ResourceHelper.validateFilePath(filePath); + assertTrue(path.toFile().exists()); + + // path is a file that does not exist and the directory does not exist + dirPath = resourceDir.resolve(subDirName); + filePath = dirPath.resolve(fileName).toAbsolutePath(); + path = ResourceHelper.validateFilePath(filePath); + // file does not exist + assertFalse(path.toFile().exists()); + // directory exists because it was created + assertTrue(dirPath.toFile().exists()); + + // preconditions + // File path points to a directory + ContractException contractException = assertThrows(ContractException.class, () -> { + ResourceHelper.validateFilePath(resourceDir); + }); + + assertEquals(ResourceError.FILE_PATH_IS_DIRECTORY, contractException.getErrorType()); + + resourceDir.resolve(fileName).toFile().delete(); + dirPath.resolve(fileName).toFile().delete(); + } + + @Test + @UnitTestMethod(target = ResourceHelper.class, name = "validateDirectoryPath", args = { String.class }) + public void testValidateDirectoryPath() { + Path resourceDir = ResourceHelper.getResourceDir(getClass()); + Path dirPath = resourceDir; + String subDirName = "temp"; + + // delete any old test files + dirPath.resolve(subDirName).toFile().delete(); + + // path is a file that does not exist but directory exists + String filePath = dirPath.resolve(subDirName).toAbsolutePath().toString(); + Path path = ResourceHelper.validateDirectoryPath(filePath); + assertTrue(path.toFile().exists()); + + // preconditions + // directory path points to a file + String fileName = "validateDirectoryPath.json"; + ContractException contractException = assertThrows(ContractException.class, () -> { + ResourceHelper.createFile(dirPath, fileName); + ResourceHelper.validateDirectoryPath(dirPath.resolve(fileName).toString()); + }); + + assertEquals(ResourceError.DIRECTORY_PATH_IS_FILE, contractException.getErrorType()); + + dirPath.resolve(fileName).toFile().delete(); + } + + @Test + @UnitTestMethod(target = ResourceHelper.class, name = "validateDirectoryPath", args = { Path.class }) + public void testValidateDirectoryPath_Path() { + Path resourceDir = ResourceHelper.getResourceDir(getClass()); + Path dirPath = resourceDir; + String subDirName = "temp"; + + // delete any old test files + dirPath.resolve(subDirName).toFile().delete(); + + // path is a file that does not exist but directory exists + Path filePath = dirPath.resolve(subDirName).toAbsolutePath(); + Path path = ResourceHelper.validateDirectoryPath(filePath); + assertTrue(path.toFile().exists()); + + // preconditions + // directory path points to a file + String fileName = "validateDirectoryPath.json"; + ContractException contractException = assertThrows(ContractException.class, () -> { + ResourceHelper.createFile(dirPath, fileName); + ResourceHelper.validateDirectoryPath(dirPath.resolve(fileName)); + }); + + assertEquals(ResourceError.DIRECTORY_PATH_IS_FILE, contractException.getErrorType()); + + dirPath.resolve(fileName).toFile().delete(); } }