task-154: 临时目录磁盘容量告警(aiimage.storage.capacity-warn-bytes 阈值、告警含当前容量/阈值、10 分钟频率限制、测量失败容忍不阻塞)+ 8 条测试

This commit is contained in:
2026-09-02 06:40:20 +08:00
parent 57f75b8bc3
commit 87a6d20f88
3 changed files with 190 additions and 0 deletions
@@ -0,0 +1,110 @@
package com.nanri.aiimage.modules.file.service;
import com.nanri.aiimage.config.StorageProperties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.test.util.ReflectionTestUtils;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
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-154:临时目录磁盘容量告警契约(plan 09)。
* 容量超阈值记录告警(当前容量/阈值);未超不告警;告警频率受限(防刷屏);
* 测量失败容忍;不抛异常不阻塞业务。
*/
class TempDirCapacityMonitorTest {
@TempDir
Path tempDir;
private StorageProperties properties;
private TempDirCapacityMonitor monitor;
@BeforeEach
void setUp() {
properties = new StorageProperties();
properties.setLocalTempDir(tempDir.toString());
monitor = new TempDirCapacityMonitor(properties);
}
@Test
void usageMeasuredCorrectly() throws Exception {
Files.writeString(tempDir.resolve("a.tmp"), "0123456789", StandardCharsets.UTF_8);
Path sub = tempDir.resolve("sub");
Files.createDirectories(sub);
Files.writeString(sub.resolve("b.tmp"), "12345", StandardCharsets.UTF_8);
long usage = monitor.measureUsageBytes(tempDir.toFile());
assertEquals(15L, usage, "容量按字节递归求和");
}
@Test
void emptyDirUsageIsZero() {
assertEquals(0L, monitor.measureUsageBytes(tempDir.toFile()), "空目录容量为 0");
}
@Test
void warningTriggeredOverThreshold() throws Exception {
Files.writeString(tempDir.resolve("big.tmp"), "x".repeat(100), StandardCharsets.UTF_8);
properties.setCapacityWarnBytes(10);
ReflectionTestUtils.setField(monitor, "lastWarnAtMillis", 0L);
// 超阈值 → 告警路径(频率窗口内可告警)
monitor.warnWithRateLimit(tempDir.toFile(), 100L, 10L);
long lastWarn = (long) ReflectionTestUtils.getField(monitor, "lastWarnAtMillis");
assertTrue(lastWarn > 0L, "告警后记录时间戳(频率限制用)");
}
@Test
void underThresholdDoesNotWarn() throws Exception {
Files.writeString(tempDir.resolve("small.tmp"), "x", StandardCharsets.UTF_8);
properties.setCapacityWarnBytes(10_000);
monitor.checkTempDirCapacity();
long lastWarn = (long) ReflectionTestUtils.getField(monitor, "lastWarnAtMillis");
assertEquals(0L, lastWarn, "未超阈值不告警");
}
@Test
void warningRateLimited() throws Exception {
ReflectionTestUtils.setField(monitor, "lastWarnAtMillis", System.currentTimeMillis());
long before = (long) ReflectionTestUtils.getField(monitor, "lastWarnAtMillis");
monitor.warnWithRateLimit(tempDir.toFile(), 100L, 10L);
assertEquals(before, ReflectionTestUtils.getField(monitor, "lastWarnAtMillis"),
"频率窗口内重复告警被抑制");
}
@Test
void measureFailureIsGraceful() {
File missing = tempDir.resolve("nope").toFile();
assertEquals(-1L, monitor.measureUsageBytes(missing), "目录缺失测量返回 -1");
assertEquals(-1L, monitor.measureUsageBytes(null));
}
@Test
void checkDoesNotThrowOnMissingDir() {
properties.setLocalTempDir(tempDir.resolve("absent").toString());
monitor.checkTempDirCapacity();
}
@Test
void warningDoesNotBlockBusiness() {
// 告警路径不抛异常(业务不阻塞)
ReflectionTestUtils.setField(monitor, "lastWarnAtMillis", 0L);
monitor.warnWithRateLimit(tempDir.toFile(), 999L, 1L);
monitor.checkTempDirCapacity();
}
}