feat:
All checks were successful
缓存序列化结构检查 / cache-schema-check (push) Has been skipped

1、多 key 默认拼成 1 条企微通知
2、UTF-8 超过 4096 字节 → 按 key 拆开,每条带完整抬头,依次发送
3、条间间隔 200ms,降低机器人限频风险
4、CI 控制台会标明「超长已按 key 拆为 N 条」并逐条打印
This commit is contained in:
2026-07-14 14:30:24 +08:00
parent 59326ebe3c
commit e7aa970ef8
4 changed files with 252 additions and 40 deletions

View File

@@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -55,11 +56,15 @@ class ReportBuilderTest {
String md = new ReportBuilder("[缓存结构变更]").toMarkdown(report);
// Key颜色 + 行内代码(与 ReportBuilder.appendKeyLine 一致)
assertTrue(md.contains(
"- Key --> <font color=\"warning\">`saas:period-config:migration:current`</font>"));
"- <font color=\"warning\">`Key --> saas:period-config:migration:current`</font>"));
int oldSection = md.indexOf("> **value值由**");
int newSection = md.indexOf("> **变更为:**");
assertTrue(oldSection >= 0, "应包含「value值由」段落实际 markdown:\n" + md);
assertTrue(newSection > oldSection, "应包含「变更为」段落且在旧值之后,实际 markdown:\n" + md);
int oldSection = md.indexOf("value 值从");
int newSection = md.indexOf("变更为");
String oldPart = md.substring(oldSection, newSection);
String newPart = md.substring(newSection);
@@ -126,6 +131,8 @@ class ReportBuilderTest {
assertTrue(console.contains("======== 字段明细 ========"));
assertTrue(console.contains("**删除字段**: x"));
assertTrue(console.contains("<font color=\"warning\">\"x\":\"\"</font>"));
assertTrue(console.contains("> **value值由**"));
assertTrue(console.contains("> **变更为:**"));
assertFalse(console.contains("`{\"x\":\"\"}`"));
}
@@ -138,4 +145,77 @@ class ReportBuilderTest {
assertTrue(out.contains("<font color=\"warning\">\"dbName\":\"\"</font>"));
assertFalse(out.contains("<font color=\"warning\">\"id\":\"\"</font>"));
}
@Test
void multipleSmallKeysStayInOneWeComMessage() {
CheckReport report = baseReport();
report.getKeyChanges().add(simpleKey("k-a", "{\"a\":\"\"}", "{\"a\":\"\",\"x\":\"\"}", "x"));
report.getKeyChanges().add(simpleKey("k-b", "{\"b\":\"\"}", "{\"b\":\"\",\"y\":\"\"}", "y"));
List<String> messages = new ReportBuilder("[缓存结构变更]").toWeComMessages(report);
assertEquals(1, messages.size());
assertTrue(messages.get(0).contains("k-a"));
assertTrue(messages.get(0).contains("k-b"));
assertTrue(ReportBuilder.utf8Bytes(messages.get(0)) <= ReportBuilder.WECOM_MARKDOWN_MAX_BYTES);
}
@Test
void oversizedMultiKeysSplitOneMessagePerKey() {
CheckReport report = baseReport();
// 两侧骨架各约 1.8KB,两条拼在一起超 4096单条仍应落在上限内
String fat = fatJson(1800);
report.getKeyChanges().add(simpleKey("fat-key-1", fat, fat + "1", null));
report.getKeyChanges().add(simpleKey("fat-key-2", fat, fat + "2", null));
List<String> messages = new ReportBuilder("[缓存结构变更]").toWeComMessages(report);
assertEquals(2, messages.size(), "超长应按 key 拆成 2 条");
assertTrue(messages.get(0).contains("fat-key-1"));
assertFalse(messages.get(0).contains("fat-key-2"));
assertTrue(messages.get(1).contains("fat-key-2"));
assertFalse(messages.get(1).contains("fat-key-1"));
assertTrue(ReportBuilder.utf8Bytes(messages.get(0)) <= ReportBuilder.WECOM_MARKDOWN_MAX_BYTES);
assertTrue(ReportBuilder.utf8Bytes(messages.get(1)) <= ReportBuilder.WECOM_MARKDOWN_MAX_BYTES);
// 抬头在每条中重复
assertTrue(messages.get(0).contains("## [缓存结构变更] jnpf-java-cloud"));
assertTrue(messages.get(1).contains("## [缓存结构变更] jnpf-java-cloud"));
String console = new ReportBuilder("[缓存结构变更]").toConsole(report);
assertTrue(console.contains("超长已按 key 拆为 2 条"));
}
private static CheckReport baseReport() {
CheckReport report = new CheckReport();
report.setRepository("jnpf-java-cloud");
report.setBranch("feature/x");
report.setOldSha("aaa11111");
report.setNewSha("bbb22222");
report.setModifier("dongzi");
report.setModifyTime("2026-07-14 12:00:00");
return report;
}
private static KeyStructureChange simpleKey(String pattern, String oldJson, String newJson,
String addedField) {
KeyStructureChange key = new KeyStructureChange();
key.setKeyPattern(pattern);
key.setOldSkeletonJson(oldJson);
key.setNewSkeletonJson(newJson);
if (addedField != null) {
SchemaChange added = new SchemaChange(ChangeType.FIELD_ADDED);
added.setFieldPath(addedField);
added.setKeyPattern(pattern);
key.getFieldDetails().add(added);
// 保证 hasChanges / 控制台字段明细有数据
}
return key;
}
private static String fatJson(int minChars) {
StringBuilder sb = new StringBuilder("{\"pad\":\"");
while (sb.length() < minChars) {
sb.append('x');
}
sb.append("\"}");
return sb.toString();
}
}