task-155: 孤立文件巡检报表(只读巡检超保留期且无引用文件,报表含路径/大小/时间,绝不删除,可重复)+ 8 条测试

This commit is contained in:
2026-09-02 06:47:03 +08:00
parent 87a6d20f88
commit d64ef5284a
2 changed files with 207 additions and 0 deletions
@@ -0,0 +1,121 @@
package com.nanri.aiimage.modules.file.service;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* task-155:孤立文件巡检报表契约(plan 09)。
* 只读巡检:超保留期且无引用的文件输出报表(路径/大小/时间);被引用不报;
* 不删除;批量/空报表/可重复。
*/
class TempOrphanInspectorTest {
@TempDir
Path tempDir;
private final TempOrphanInspector inspector = new TempOrphanInspector();
private Path writeFile(String name, String content) throws Exception {
Path path = tempDir.resolve(name);
Files.writeString(path, content, StandardCharsets.UTF_8);
return path;
}
private Instant hourAgo() {
return Instant.now().minus(1, ChronoUnit.HOURS);
}
private Instant hourLater() {
return Instant.now().plus(1, ChronoUnit.HOURS);
}
@Test
void orphanDetectedWhenExpiredAndUnreferenced() throws Exception {
Path file = writeFile("orphan.tmp", "x".repeat(10));
var report = inspector.inspectOrphanFiles(tempDir.toFile(), hourLater(), name -> false);
assertEquals(1, report.entries().size());
assertTrue(report.entries().getFirst().path().contains("orphan.tmp"));
}
@Test
void referencedFileNotReported() throws Exception {
writeFile("kept.tmp", "x");
var report = inspector.inspectOrphanFiles(tempDir.toFile(), hourLater(), name -> name.equals("kept.tmp"));
assertTrue(report.isEmpty(), "被引用文件不得进入报表");
}
@Test
void reportIncludesDetails() throws Exception {
Path file = writeFile("detail.tmp", "0123456789");
var report = inspector.inspectOrphanFiles(tempDir.toFile(), hourLater(), name -> false);
assertEquals(1, report.entries().size());
TempOrphanInspector.OrphanFileEntry entry = report.entries().getFirst();
assertTrue(entry.path().contains("detail.tmp"), "报表含路径");
assertEquals(10L, entry.sizeBytes(), "报表含大小");
assertTrue(entry.lastModifiedAt() != null && !entry.lastModifiedAt().isBlank(), "报表含修改时间");
}
@Test
void readOnlyDoesNotDelete() throws Exception {
Path file = writeFile("readonly.tmp", "keep-me");
inspector.inspectOrphanFiles(tempDir.toFile(), hourLater(), name -> false);
assertTrue(Files.exists(file), "巡检只读,不得删除文件");
}
@Test
void batchReportListsAllOrphans() throws Exception {
writeFile("a.tmp", "1");
writeFile("b.tmp", "22");
writeFile("c.tmp", "333");
var report = inspector.inspectOrphanFiles(tempDir.toFile(), hourLater(), name -> false);
assertEquals(3, report.entries().size());
assertEquals(6, report.totalBytes());
}
@Test
void emptyReportWhenNoOrphans() throws Exception {
writeFile("fresh.tmp", "new"); // 未超保留期
var report = inspector.inspectOrphanFiles(tempDir.toFile(), hourAgo(), name -> false);
assertTrue(report.isEmpty());
}
@Test
void rerunIsSafeAndStable() throws Exception {
writeFile("rerun.tmp", "x");
var first = inspector.inspectOrphanFiles(tempDir.toFile(), hourLater(), name -> false);
var second = inspector.inspectOrphanFiles(tempDir.toFile(), hourLater(), name -> false);
assertEquals(first.entries().size(), second.entries().size());
assertEquals(first.entries().getFirst().path(), second.entries().getFirst().path());
}
@Test
void invalidInputsAreGraceful() {
assertTrue(inspector.inspectOrphanFiles(null, hourAgo(), name -> false).isEmpty());
assertTrue(inspector.inspectOrphanFiles(tempDir.toFile(), null, name -> false).isEmpty());
assertTrue(inspector.inspectOrphanFiles(tempDir.resolve("absent").toFile(), hourAgo(), name -> false).isEmpty());
}
}