task-189: 指标命名一致性契约(aiimage.* 前缀、全小写点分、全量名快照对齐 spec11 §1、无拼写漂移)+ 8 条测试

This commit is contained in:
2026-09-04 23:53:22 +08:00
parent 7967f454ec
commit 4179e0de23
@@ -0,0 +1,124 @@
package com.nanri.aiimage.metrics;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* task-189:指标命名一致性契约(spec 11 §1 标准名)。
* 全部指标名 aiimage.* 前缀、小写点分、无拼写漂移;recorder 全量指标名快照与 spec 对齐。
*/
class MetricNamingConsistencyTest {
private final SimpleMeterRegistry registry = new SimpleMeterRegistry();
private final TaskObservabilityMetrics metrics = new TaskObservabilityMetrics("server-110", registry);
private void createAllMetrics() {
metrics.taskCreated("similarasin");
metrics.taskRunningDelta("similarasin", 1);
metrics.taskTerminal("similarasin", "success");
metrics.taskTerminal("similarasin", "failed");
metrics.taskTerminal("similarasin", "cancelled");
metrics.taskDuration("similarasin", 1L);
metrics.taskHeartbeatAge("similarasin", 1L);
metrics.fileJobState("similarasin", "pending");
metrics.fileJobState("similarasin", "running");
metrics.fileJobState("similarasin", "success");
metrics.fileJobState("similarasin", "failed");
metrics.fileJobState("similarasin", "retry");
metrics.fileJobDuration("similarasin", 1L);
metrics.upload("similarasin", 1L, 1L, true);
metrics.upload("similarasin", 1L, 1L, false);
metrics.disk("/tmp/x", 1L, 1L);
}
@Test
void allNamesHaveAiimagePrefix() {
createAllMetrics();
for (String name : names()) {
assertTrue(name.startsWith("aiimage."), "指标应带 aiimage. 前缀: " + name);
}
}
@Test
void allNamesLowercaseDotSegments() {
createAllMetrics();
for (String name : names()) {
assertTrue(name.equals(name.toLowerCase()), "指标名应全小写: " + name);
assertTrue(!name.contains(".."), "指标名不应有连续点: " + name);
assertTrue(!name.endsWith("."), "指标名不应以点结尾: " + name);
}
}
@Test
void taskFamilyNamesFrozen() {
createAllMetrics();
for (String n : List.of("aiimage.task.created", "aiimage.task.running", "aiimage.task.success",
"aiimage.task.failed", "aiimage.task.cancelled", "aiimage.task.duration",
"aiimage.task.heartbeat.age")) {
assertTrue(names().contains(n), "缺少 spec 任务指标名: " + n);
}
}
@Test
void fileJobFamilyNamesFrozen() {
createAllMetrics();
for (String n : List.of("aiimage.file-job.pending", "aiimage.file-job.running",
"aiimage.file-job.success", "aiimage.file-job.failed", "aiimage.file-job.retry",
"aiimage.file-job.duration")) {
assertTrue(names().contains(n), "缺少 spec file-job 指标名: " + n);
}
}
@Test
void noTypoDriftSnapshot() {
createAllMetrics();
Set<String> names = names();
// 与 spec 11 §1 允许集完全一致:不允许出现拼写漂移产生的多余名字
Set<String> allowed = Set.of(
"aiimage.task.created", "aiimage.task.running", "aiimage.task.success",
"aiimage.task.failed", "aiimage.task.cancelled", "aiimage.task.duration",
"aiimage.task.heartbeat.age",
"aiimage.file-job.pending", "aiimage.file-job.running", "aiimage.file-job.success",
"aiimage.file-job.failed", "aiimage.file-job.retry", "aiimage.file-job.duration",
"aiimage.upload.duration", "aiimage.upload.size", "aiimage.upload.result",
"aiimage.upload.result.success", "aiimage.upload.result.failure",
"aiimage.tmpdisk.capacity", "aiimage.tmpdisk.used");
Set<String> unexpected = names.stream().filter(n -> !allowed.contains(n)).collect(Collectors.toSet());
assertEquals(Set.of(), unexpected, "不应出现 spec 之外的漂移指标名");
}
@Test
void snapshotAllMetricsCaptured() {
createAllMetrics();
assertTrue(names().size() >= 19, "全量指标快照应覆盖任务/文件/上传/磁盘族,实际 " + names().size());
}
@Test
void caseConsistentAcrossFamilies() {
createAllMetrics();
long upperCount = names().stream().filter(n -> n.chars().anyMatch(Character::isUpperCase)).count();
assertEquals(0, upperCount, "指标名应统一小写");
}
@Test
void metricNamingStableAcrossRepeatedCreation() {
createAllMetrics();
Set<String> first = names();
TaskObservabilityMetrics metrics2 = new TaskObservabilityMetrics("server-121", registry);
metrics2.taskCreated("similarasin");
Set<String> second = names();
assertEquals(first, second, "新增实例不应改变既有指标名集合");
}
private Set<String> names() {
return registry.getMeters().stream()
.map(m -> m.getId().getName()).collect(Collectors.toSet());
}
}