From b4b3f4e0bd556a4bf94a781828f830e640db5346 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:59:03 +0000 Subject: [PATCH 1/3] Initial plan From 28367a5156f8ebafc2709f2b10a299621ab8102f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 19:13:08 +0000 Subject: [PATCH 2/3] Add reviewUnapproved and approveAll tasks/goals to build plugins Co-authored-by: mkutz <3702442+mkutz@users.noreply.github.com> --- .../approve/ApprovedFileInventory.java | 109 +++++++++++++++++- .../approve/ApprovedFileInventoryTest.java | 87 ++++++++++++++ .../org/approvej/gradle/ApproveJPlugin.java | 26 +++++ .../approvej/gradle/ApproveJPluginTest.java | 42 ++++++- .../org/approvej/maven/ApproveAllMojo.java | 21 ++++ .../approvej/maven/ReviewUnapprovedMojo.java | 24 ++++ .../org/approvej/maven/MojoHelperTest.java | 2 +- 7 files changed, 306 insertions(+), 5 deletions(-) create mode 100644 plugins/approvej-maven-plugin/src/main/java/org/approvej/maven/ApproveAllMojo.java create mode 100644 plugins/approvej-maven-plugin/src/main/java/org/approvej/maven/ReviewUnapprovedMojo.java diff --git a/modules/core/src/main/java/org/approvej/approve/ApprovedFileInventory.java b/modules/core/src/main/java/org/approvej/approve/ApprovedFileInventory.java index eb3b23b9..8beccf79 100644 --- a/modules/core/src/main/java/org/approvej/approve/ApprovedFileInventory.java +++ b/modules/core/src/main/java/org/approvej/approve/ApprovedFileInventory.java @@ -1,5 +1,6 @@ package org.approvej.approve; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.util.Arrays.stream; import static java.util.stream.Collectors.joining; @@ -13,6 +14,8 @@ import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; +import org.approvej.configuration.Configuration; +import org.approvej.review.FileReviewer; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -28,6 +31,7 @@ public class ApprovedFileInventory { private static final Path DEFAULT_INVENTORY_FILE = Path.of(".approvej/inventory.properties"); + private static final Path DEFAULT_WORKING_DIRECTORY = Path.of("."); private static final String HEADER = "# ApproveJ Approved File Inventory (auto-generated, do not edit)"; @@ -39,6 +43,9 @@ public class ApprovedFileInventory { private static final AtomicReference inventoryFile = new AtomicReference<>(DEFAULT_INVENTORY_FILE); + private static final AtomicReference workingDirectory = + new AtomicReference<>(DEFAULT_WORKING_DIRECTORY); + private ApprovedFileInventory() {} /** @@ -193,6 +200,15 @@ static void reset(Path testInventoryFile) { } } inventoryFile.set(testInventoryFile); + workingDirectory.set(DEFAULT_WORKING_DIRECTORY); + } + + /** + * Resets static state and sets the inventory file path and working directory. For testing only. + */ + static void reset(Path testInventoryFile, Path testWorkingDirectory) { + reset(testInventoryFile); + workingDirectory.set(testWorkingDirectory); } /** Resets static state to defaults. For testing only. */ @@ -200,13 +216,90 @@ static void reset() { reset(DEFAULT_INVENTORY_FILE); } + /** + * Finds all received files in the working directory recursively. + * + * @return a sorted list of paths to received files + */ + static List findReceivedFiles() { + try (var stream = Files.walk(workingDirectory.get())) { + return stream + .filter( + path -> { + String filename = path.getFileName().toString(); + return filename.contains("-received.") || filename.endsWith("-received"); + }) + .map(Path::normalize) + .sorted() + .toList(); + } catch (IOException e) { + System.err.printf("Failed to search for received files: %s%n", e.getMessage()); + return List.of(); + } + } + + private static Path approvedPathFor(Path receivedPath) { + String filename = receivedPath.getFileName().toString(); + String approvedFilename; + if (filename.contains("-received.")) { + approvedFilename = filename.replace("-received.", "-approved."); + } else { + approvedFilename = + filename.substring(0, filename.length() - "-received".length()) + "-approved"; + } + return receivedPath.getParent().resolve(approvedFilename); + } + + /** + * Approves all unapproved files by moving each received file to its corresponding approved file. + * + * @return the list of approved file paths (the received files that were moved) + */ + static List approveAll() { + List receivedFiles = findReceivedFiles(); + List approved = new ArrayList<>(); + for (Path received : receivedFiles) { + Path approvedPath = approvedPathFor(received); + try { + Files.move(received, approvedPath, REPLACE_EXISTING); + approved.add(received); + } catch (IOException e) { + System.err.printf("Failed to approve %s: %s%n", received, e.getMessage()); + } + } + return approved; + } + + /** + * Reviews all unapproved files using the configured {@link FileReviewer}. + * + * @param reviewer the {@link FileReviewer} to use for reviewing each unapproved file + */ + static void reviewUnapproved(FileReviewer reviewer) { + List receivedFiles = findReceivedFiles(); + if (receivedFiles.isEmpty()) { + System.out.println("No unapproved files found."); + return; + } + System.out.println("Unapproved files:"); + receivedFiles.forEach( + received -> { + Path approvedPath = approvedPathFor(received); + System.out.printf(" %s%n", received.toUri()); + reviewer.apply(PathProviders.approvedPath(approvedPath)); + }); + } + /** * CLI entry point for build tool plugins. * - * @param args {@code --find} to list leftovers, {@code --remove} to delete them + * @param args {@code --find} to list leftovers, {@code --remove} to delete them, {@code + * --approve-all} to approve all unapproved files, {@code --review-unapproved} to review all + * unapproved files */ public static void main(String[] args) { - String usage = "Usage: ApprovedFileInventory --find | --remove"; + String usage = + "Usage: ApprovedFileInventory --find | --remove | --approve-all | --review-unapproved"; if (args.length == 0) { System.err.println(usage); System.exit(1); @@ -236,6 +329,18 @@ public static void main(String[] args) { removed.forEach(leftover -> System.out.printf(" %s%n", leftover.relativePath().toUri())); } } + case "--approve-all" -> { + List approved = approveAll(); + if (approved.isEmpty()) { + System.out.println("No unapproved files found."); + } else { + System.out.println("Approved files:"); + approved.forEach(path -> System.out.printf(" %s%n", path.toUri())); + } + } + case "--review-unapproved" -> { + reviewUnapproved(Configuration.configuration.defaultFileReviewer()); + } default -> { System.err.printf("Unknown command: %s%n", command); System.err.println(usage); diff --git a/modules/core/src/test/java/org/approvej/approve/ApprovedFileInventoryTest.java b/modules/core/src/test/java/org/approvej/approve/ApprovedFileInventoryTest.java index 9a17fb5f..994b91e4 100644 --- a/modules/core/src/test/java/org/approvej/approve/ApprovedFileInventoryTest.java +++ b/modules/core/src/test/java/org/approvej/approve/ApprovedFileInventoryTest.java @@ -4,10 +4,13 @@ import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.ArrayList; import java.util.List; import java.util.TreeMap; +import org.approvej.review.FileReviewResult; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -188,4 +191,88 @@ void removeLeftovers() throws IOException { TreeMap inventory = ApprovedFileInventory.loadInventory(); assertThat(inventory).doesNotContainKey(leftoverFile).containsKey(validFile); } + + @Test + void findReceivedFiles() throws IOException { + ApprovedFileInventory.reset(inventoryFile, tempDir); + Files.createFile(tempDir.resolve("MyTest-myMethod-received.txt")); + Files.createFile(tempDir.resolve("MyTest-myMethod-approved.txt")); + Files.createFile(tempDir.resolve("OtherTest-other-received.json")); + + List receivedFiles = ApprovedFileInventory.findReceivedFiles(); + + assertThat(receivedFiles) + .hasSize(2) + .anySatisfy( + p -> assertThat(p.getFileName().toString()).isEqualTo("MyTest-myMethod-received.txt")) + .anySatisfy( + p -> assertThat(p.getFileName().toString()).isEqualTo("OtherTest-other-received.json")); + } + + @Test + void findReceivedFiles_empty() { + ApprovedFileInventory.reset(inventoryFile, tempDir); + + List receivedFiles = ApprovedFileInventory.findReceivedFiles(); + + assertThat(receivedFiles).isEmpty(); + } + + @Test + void approveAll() throws IOException { + ApprovedFileInventory.reset(inventoryFile, tempDir); + Path receivedFile = tempDir.resolve("MyTest-myMethod-received.txt"); + writeString(receivedFile, "received content", StandardOpenOption.CREATE); + Path approvedFile = tempDir.resolve("MyTest-myMethod-approved.txt"); + writeString(approvedFile, "old approved content", StandardOpenOption.CREATE); + + List approved = ApprovedFileInventory.approveAll(); + + assertThat(approved).hasSize(1); + assertThat(receivedFile).doesNotExist(); + assertThat(approvedFile).exists().hasContent("received content"); + } + + @Test + void approveAll_no_received_files() { + ApprovedFileInventory.reset(inventoryFile, tempDir); + + List approved = ApprovedFileInventory.approveAll(); + + assertThat(approved).isEmpty(); + } + + @Test + void reviewUnapproved() throws IOException { + ApprovedFileInventory.reset(inventoryFile, tempDir); + Path receivedFile = tempDir.resolve("MyTest-myMethod-received.txt"); + writeString(receivedFile, "received content", StandardOpenOption.CREATE); + Path approvedFile = tempDir.resolve("MyTest-myMethod-approved.txt"); + writeString(approvedFile, "approved content", StandardOpenOption.CREATE); + + var reviewedProviders = new ArrayList(); + ApprovedFileInventory.reviewUnapproved( + pathProvider -> { + reviewedProviders.add(pathProvider); + return new FileReviewResult(false); + }); + + assertThat(reviewedProviders).hasSize(1); + assertThat(reviewedProviders.getFirst().receivedPath()).isEqualTo(receivedFile.normalize()); + assertThat(reviewedProviders.getFirst().approvedPath()).isEqualTo(approvedFile.normalize()); + } + + @Test + void reviewUnapproved_no_received_files() { + ApprovedFileInventory.reset(inventoryFile, tempDir); + + var reviewedProviders = new ArrayList(); + ApprovedFileInventory.reviewUnapproved( + pathProvider -> { + reviewedProviders.add(pathProvider); + return new FileReviewResult(false); + }); + + assertThat(reviewedProviders).isEmpty(); + } } diff --git a/plugins/approvej-gradle-plugin/src/main/java/org/approvej/gradle/ApproveJPlugin.java b/plugins/approvej-gradle-plugin/src/main/java/org/approvej/gradle/ApproveJPlugin.java index 60f5e56e..5a2dd01e 100644 --- a/plugins/approvej-gradle-plugin/src/main/java/org/approvej/gradle/ApproveJPlugin.java +++ b/plugins/approvej-gradle-plugin/src/main/java/org/approvej/gradle/ApproveJPlugin.java @@ -49,6 +49,32 @@ public void apply(Project project) { task.getMainClass().set("org.approvej.approve.ApprovedFileInventory"); task.args("--remove"); }); + + project + .getTasks() + .register( + "approvejApproveAll", + JavaExec.class, + task -> { + task.setGroup("verification"); + task.setDescription("Approve all unapproved files"); + task.setClasspath(testClasspath); + task.getMainClass().set("org.approvej.approve.ApprovedFileInventory"); + task.args("--approve-all"); + }); + + project + .getTasks() + .register( + "approvejReviewUnapproved", + JavaExec.class, + task -> { + task.setGroup("verification"); + task.setDescription("Review all unapproved files"); + task.setClasspath(testClasspath); + task.getMainClass().set("org.approvej.approve.ApprovedFileInventory"); + task.args("--review-unapproved"); + }); }); } } diff --git a/plugins/approvej-gradle-plugin/src/test/java/org/approvej/gradle/ApproveJPluginTest.java b/plugins/approvej-gradle-plugin/src/test/java/org/approvej/gradle/ApproveJPluginTest.java index be97f71e..ebb6a187 100644 --- a/plugins/approvej-gradle-plugin/src/test/java/org/approvej/gradle/ApproveJPluginTest.java +++ b/plugins/approvej-gradle-plugin/src/test/java/org/approvej/gradle/ApproveJPluginTest.java @@ -25,6 +25,8 @@ void apply() { assertThat(project.getTasks().findByName("approvejFindLeftovers")).isNotNull(); assertThat(project.getTasks().findByName("approvejCleanup")).isNotNull(); + assertThat(project.getTasks().findByName("approvejApproveAll")).isNotNull(); + assertThat(project.getTasks().findByName("approvejReviewUnapproved")).isNotNull(); } @Test @@ -35,6 +37,8 @@ void apply_without_java_plugin() { assertThat(project.getTasks().findByName("approvejFindLeftovers")).isNull(); assertThat(project.getTasks().findByName("approvejCleanup")).isNull(); + assertThat(project.getTasks().findByName("approvejApproveAll")).isNull(); + assertThat(project.getTasks().findByName("approvejReviewUnapproved")).isNull(); } @Test @@ -46,6 +50,8 @@ void apply_java_plugin_after_approvej() { assertThat(project.getTasks().findByName("approvejFindLeftovers")).isNotNull(); assertThat(project.getTasks().findByName("approvejCleanup")).isNotNull(); + assertThat(project.getTasks().findByName("approvejApproveAll")).isNotNull(); + assertThat(project.getTasks().findByName("approvejReviewUnapproved")).isNotNull(); } @Test @@ -76,6 +82,34 @@ void apply_cleanup_task_configuration() { assertThat(task.getArgs()).containsExactly("--remove"); } + @Test + void apply_approveAll_task_configuration() { + Project project = ProjectBuilder.builder().build(); + project.getPluginManager().apply("java"); + project.getPluginManager().apply(ApproveJPlugin.class); + + var task = (JavaExec) project.getTasks().getByName("approvejApproveAll"); + + assertThat(task.getGroup()).isEqualTo("verification"); + assertThat(task.getDescription()).isEqualTo("Approve all unapproved files"); + assertThat(task.getMainClass().get()).isEqualTo("org.approvej.approve.ApprovedFileInventory"); + assertThat(task.getArgs()).containsExactly("--approve-all"); + } + + @Test + void apply_reviewUnapproved_task_configuration() { + Project project = ProjectBuilder.builder().build(); + project.getPluginManager().apply("java"); + project.getPluginManager().apply(ApproveJPlugin.class); + + var task = (JavaExec) project.getTasks().getByName("approvejReviewUnapproved"); + + assertThat(task.getGroup()).isEqualTo("verification"); + assertThat(task.getDescription()).isEqualTo("Review all unapproved files"); + assertThat(task.getMainClass().get()).isEqualTo("org.approvej.approve.ApprovedFileInventory"); + assertThat(task.getArgs()).containsExactly("--review-unapproved"); + } + @Test void apply_functional() throws IOException { Files.writeString( @@ -96,7 +130,9 @@ void apply_functional() throws IOException { assertThat(result.getOutput()) .contains("approvejFindLeftovers - List leftover approved files") - .contains("approvejCleanup - Detect and remove leftover approved files"); + .contains("approvejCleanup - Detect and remove leftover approved files") + .contains("approvejApproveAll - Approve all unapproved files") + .contains("approvejReviewUnapproved - Review all unapproved files"); } @Test @@ -118,6 +154,8 @@ void apply_functional_without_java_plugin() throws IOException { assertThat(result.getOutput()) .doesNotContain("approvejFindLeftovers") - .doesNotContain("approvejCleanup"); + .doesNotContain("approvejCleanup") + .doesNotContain("approvejApproveAll") + .doesNotContain("approvejReviewUnapproved"); } } diff --git a/plugins/approvej-maven-plugin/src/main/java/org/approvej/maven/ApproveAllMojo.java b/plugins/approvej-maven-plugin/src/main/java/org/approvej/maven/ApproveAllMojo.java new file mode 100644 index 00000000..b7c3250e --- /dev/null +++ b/plugins/approvej-maven-plugin/src/main/java/org/approvej/maven/ApproveAllMojo.java @@ -0,0 +1,21 @@ +package org.approvej.maven; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; + +/** Approves all unapproved files by moving each received file to its approved counterpart. */ +@Mojo(name = "approve-all", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true) +public class ApproveAllMojo extends AbstractMojo { + + @Parameter(defaultValue = "${project}", readonly = true, required = true) + private MavenProject project; + + @Override + public void execute() throws MojoExecutionException { + MojoHelper.executeInventory(project, "--approve-all", getLog()); + } +} diff --git a/plugins/approvej-maven-plugin/src/main/java/org/approvej/maven/ReviewUnapprovedMojo.java b/plugins/approvej-maven-plugin/src/main/java/org/approvej/maven/ReviewUnapprovedMojo.java new file mode 100644 index 00000000..246af63c --- /dev/null +++ b/plugins/approvej-maven-plugin/src/main/java/org/approvej/maven/ReviewUnapprovedMojo.java @@ -0,0 +1,24 @@ +package org.approvej.maven; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; + +/** Reviews all unapproved files using the configured file reviewer. */ +@Mojo( + name = "review-unapproved", + requiresDependencyResolution = ResolutionScope.TEST, + threadSafe = true) +public class ReviewUnapprovedMojo extends AbstractMojo { + + @Parameter(defaultValue = "${project}", readonly = true, required = true) + private MavenProject project; + + @Override + public void execute() throws MojoExecutionException { + MojoHelper.executeInventory(project, "--review-unapproved", getLog()); + } +} diff --git a/plugins/approvej-maven-plugin/src/test/java/org/approvej/maven/MojoHelperTest.java b/plugins/approvej-maven-plugin/src/test/java/org/approvej/maven/MojoHelperTest.java index 6bd555f6..81432f6a 100644 --- a/plugins/approvej-maven-plugin/src/test/java/org/approvej/maven/MojoHelperTest.java +++ b/plugins/approvej-maven-plugin/src/test/java/org/approvej/maven/MojoHelperTest.java @@ -28,7 +28,7 @@ void executeInventory_nonzero_exit_code(@TempDir Path tempDir) { } @ParameterizedTest - @ValueSource(strings = {"--find", "--remove"}) + @ValueSource(strings = {"--find", "--remove", "--approve-all", "--review-unapproved"}) void buildCommand(String command) { var classpathElements = List.of("/lib/a.jar", "/lib/b.jar"); From e4914aa064b63f65a101cd63b9508b57ce1de2b0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 19:58:48 +0000 Subject: [PATCH 3/3] Use inventory for received-file discovery instead of filesystem walk Co-authored-by: mkutz <3702442+mkutz@users.noreply.github.com> --- .../approve/ApprovedFileInventory.java | 90 +++++++++---------- .../approve/ApprovedFileInventoryTest.java | 65 +++++++++++--- 2 files changed, 95 insertions(+), 60 deletions(-) diff --git a/modules/core/src/main/java/org/approvej/approve/ApprovedFileInventory.java b/modules/core/src/main/java/org/approvej/approve/ApprovedFileInventory.java index 8beccf79..eab5f98b 100644 --- a/modules/core/src/main/java/org/approvej/approve/ApprovedFileInventory.java +++ b/modules/core/src/main/java/org/approvej/approve/ApprovedFileInventory.java @@ -31,7 +31,6 @@ public class ApprovedFileInventory { private static final Path DEFAULT_INVENTORY_FILE = Path.of(".approvej/inventory.properties"); - private static final Path DEFAULT_WORKING_DIRECTORY = Path.of("."); private static final String HEADER = "# ApproveJ Approved File Inventory (auto-generated, do not edit)"; @@ -43,9 +42,6 @@ public class ApprovedFileInventory { private static final AtomicReference inventoryFile = new AtomicReference<>(DEFAULT_INVENTORY_FILE); - private static final AtomicReference workingDirectory = - new AtomicReference<>(DEFAULT_WORKING_DIRECTORY); - private ApprovedFileInventory() {} /** @@ -200,15 +196,6 @@ static void reset(Path testInventoryFile) { } } inventoryFile.set(testInventoryFile); - workingDirectory.set(DEFAULT_WORKING_DIRECTORY); - } - - /** - * Resets static state and sets the inventory file path and working directory. For testing only. - */ - static void reset(Path testInventoryFile, Path testWorkingDirectory) { - reset(testInventoryFile); - workingDirectory.set(testWorkingDirectory); } /** Resets static state to defaults. For testing only. */ @@ -217,49 +204,52 @@ static void reset() { } /** - * Finds all received files in the working directory recursively. + * Finds all received files corresponding to inventory entries. * - * @return a sorted list of paths to received files + *

For each approved file in the inventory, checks if a corresponding received file exists next + * to it. + * + * @return a sorted list of paths to received files that exist */ static List findReceivedFiles() { - try (var stream = Files.walk(workingDirectory.get())) { - return stream - .filter( - path -> { - String filename = path.getFileName().toString(); - return filename.contains("-received.") || filename.endsWith("-received"); - }) - .map(Path::normalize) - .sorted() - .toList(); - } catch (IOException e) { - System.err.printf("Failed to search for received files: %s%n", e.getMessage()); - return List.of(); - } + return loadInventory().keySet().stream() + .map(ApprovedFileInventory::receivedPathFor) + .filter(Files::exists) + .sorted() + .toList(); } - private static Path approvedPathFor(Path receivedPath) { - String filename = receivedPath.getFileName().toString(); - String approvedFilename; - if (filename.contains("-received.")) { - approvedFilename = filename.replace("-received.", "-approved."); - } else { - approvedFilename = - filename.substring(0, filename.length() - "-received".length()) + "-approved"; + private static Path receivedPathFor(Path approvedPath) { + String filename = approvedPath.getFileName().toString(); + int approvedWithExtIdx = filename.lastIndexOf("-approved."); + if (approvedWithExtIdx >= 0) { + return approvedPath + .getParent() + .resolve( + filename.substring(0, approvedWithExtIdx) + + "-received." + + filename.substring(approvedWithExtIdx + "-approved.".length())); } - return receivedPath.getParent().resolve(approvedFilename); + if (filename.endsWith("-approved")) { + return approvedPath + .getParent() + .resolve(filename.substring(0, filename.length() - "-approved".length()) + "-received"); + } + return approvedPath; } /** * Approves all unapproved files by moving each received file to its corresponding approved file. * - * @return the list of approved file paths (the received files that were moved) + * @return the list of received file paths that were approved */ static List approveAll() { - List receivedFiles = findReceivedFiles(); List approved = new ArrayList<>(); - for (Path received : receivedFiles) { - Path approvedPath = approvedPathFor(received); + for (Path approvedPath : loadInventory().keySet()) { + Path received = receivedPathFor(approvedPath); + if (!Files.exists(received)) { + continue; + } try { Files.move(received, approvedPath, REPLACE_EXISTING); approved.add(received); @@ -276,16 +266,20 @@ static List approveAll() { * @param reviewer the {@link FileReviewer} to use for reviewing each unapproved file */ static void reviewUnapproved(FileReviewer reviewer) { - List receivedFiles = findReceivedFiles(); - if (receivedFiles.isEmpty()) { + var unapprovedEntries = + loadInventory().keySet().stream() + .filter(approvedPath -> Files.exists(receivedPathFor(approvedPath))) + .sorted() + .toList(); + if (unapprovedEntries.isEmpty()) { System.out.println("No unapproved files found."); return; } System.out.println("Unapproved files:"); - receivedFiles.forEach( - received -> { - Path approvedPath = approvedPathFor(received); - System.out.printf(" %s%n", received.toUri()); + unapprovedEntries.forEach( + approvedPath -> { + Path receivedPath = receivedPathFor(approvedPath); + System.out.printf(" %s%n", receivedPath.toUri()); reviewer.apply(PathProviders.approvedPath(approvedPath)); }); } diff --git a/modules/core/src/test/java/org/approvej/approve/ApprovedFileInventoryTest.java b/modules/core/src/test/java/org/approvej/approve/ApprovedFileInventoryTest.java index 994b91e4..c02757a0 100644 --- a/modules/core/src/test/java/org/approvej/approve/ApprovedFileInventoryTest.java +++ b/modules/core/src/test/java/org/approvej/approve/ApprovedFileInventoryTest.java @@ -194,10 +194,22 @@ void removeLeftovers() throws IOException { @Test void findReceivedFiles() throws IOException { - ApprovedFileInventory.reset(inventoryFile, tempDir); - Files.createFile(tempDir.resolve("MyTest-myMethod-received.txt")); - Files.createFile(tempDir.resolve("MyTest-myMethod-approved.txt")); - Files.createFile(tempDir.resolve("OtherTest-other-received.json")); + Path approvedFile1 = tempDir.resolve("MyTest-myMethod-approved.txt"); + Files.createFile(approvedFile1); + Path receivedFile1 = tempDir.resolve("MyTest-myMethod-received.txt"); + Files.createFile(receivedFile1); + Path approvedFile2 = tempDir.resolve("OtherTest-other-approved.json"); + Files.createFile(approvedFile2); + Path receivedFile2 = tempDir.resolve("OtherTest-other-received.json"); + Files.createFile(receivedFile2); + writeString( + inventoryFile, + "# ApproveJ Approved File Inventory (auto-generated, do not edit)\n" + + approvedFile1 + + " = com.example.MyTest#myMethod\n" + + approvedFile2 + + " = com.example.OtherTest#other\n", + StandardOpenOption.CREATE); List receivedFiles = ApprovedFileInventory.findReceivedFiles(); @@ -210,21 +222,49 @@ void findReceivedFiles() throws IOException { } @Test - void findReceivedFiles_empty() { - ApprovedFileInventory.reset(inventoryFile, tempDir); + void findReceivedFiles_only_existing() throws IOException { + Path approvedFile = tempDir.resolve("MyTest-myMethod-approved.txt"); + Files.createFile(approvedFile); + Path receivedFile = tempDir.resolve("MyTest-myMethod-received.txt"); + Files.createFile(receivedFile); + Path approvedFileNoReceived = tempDir.resolve("OtherTest-other-approved.json"); + Files.createFile(approvedFileNoReceived); + writeString( + inventoryFile, + "# ApproveJ Approved File Inventory (auto-generated, do not edit)\n" + + approvedFile + + " = com.example.MyTest#myMethod\n" + + approvedFileNoReceived + + " = com.example.OtherTest#other\n", + StandardOpenOption.CREATE); List receivedFiles = ApprovedFileInventory.findReceivedFiles(); + assertThat(receivedFiles) + .hasSize(1) + .anySatisfy( + p -> assertThat(p.getFileName().toString()).isEqualTo("MyTest-myMethod-received.txt")); + } + + @Test + void findReceivedFiles_empty() { + List receivedFiles = ApprovedFileInventory.findReceivedFiles(); + assertThat(receivedFiles).isEmpty(); } @Test void approveAll() throws IOException { - ApprovedFileInventory.reset(inventoryFile, tempDir); Path receivedFile = tempDir.resolve("MyTest-myMethod-received.txt"); writeString(receivedFile, "received content", StandardOpenOption.CREATE); Path approvedFile = tempDir.resolve("MyTest-myMethod-approved.txt"); writeString(approvedFile, "old approved content", StandardOpenOption.CREATE); + writeString( + inventoryFile, + "# ApproveJ Approved File Inventory (auto-generated, do not edit)\n" + + approvedFile + + " = com.example.MyTest#myMethod\n", + StandardOpenOption.CREATE); List approved = ApprovedFileInventory.approveAll(); @@ -235,8 +275,6 @@ void approveAll() throws IOException { @Test void approveAll_no_received_files() { - ApprovedFileInventory.reset(inventoryFile, tempDir); - List approved = ApprovedFileInventory.approveAll(); assertThat(approved).isEmpty(); @@ -244,11 +282,16 @@ void approveAll_no_received_files() { @Test void reviewUnapproved() throws IOException { - ApprovedFileInventory.reset(inventoryFile, tempDir); Path receivedFile = tempDir.resolve("MyTest-myMethod-received.txt"); writeString(receivedFile, "received content", StandardOpenOption.CREATE); Path approvedFile = tempDir.resolve("MyTest-myMethod-approved.txt"); writeString(approvedFile, "approved content", StandardOpenOption.CREATE); + writeString( + inventoryFile, + "# ApproveJ Approved File Inventory (auto-generated, do not edit)\n" + + approvedFile + + " = com.example.MyTest#myMethod\n", + StandardOpenOption.CREATE); var reviewedProviders = new ArrayList(); ApprovedFileInventory.reviewUnapproved( @@ -264,8 +307,6 @@ void reviewUnapproved() throws IOException { @Test void reviewUnapproved_no_received_files() { - ApprovedFileInventory.reset(inventoryFile, tempDir); - var reviewedProviders = new ArrayList(); ApprovedFileInventory.reviewUnapproved( pathProvider -> {