init local code

This commit is contained in:
chenxing
2026-09-07 09:09:39 +08:00
commit 99cd26b3d2
259 changed files with 20554 additions and 0 deletions
@@ -0,0 +1,155 @@
package com.yunhai.cms.theme;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
/**
* 主题注册表纯单元测试(方案 §8.3 A 组 T1-T8):直接 new,不启 Spring。
* 夹具主题在 src/test/resources/templates/themes/(t- 前缀,§8.7),
* 断言一律用"包含/不包含",禁止精确数量断言(夹具被全量 IT 拾取)。
*/
@DisplayName("ThemeRegistry - 主题包注册与校验(方案 §4.1/§5.1)")
class ThemeRegistryTest {
private ThemeRegistry registry;
@BeforeEach
void setUp() {
registry = new ThemeRegistry();
registry.load();
}
@Test
@DisplayName("T1: default 包注册成功且声明 primary 参数(color/cssVar)")
void defaultPackageRegisteredWithPrimaryParam() {
ThemeInfo info = registry.get(ThemeRegistry.DEFAULT_THEME);
assertThat(info).as("default 包必须注册(缺清单或非法则本测试红,§4.1 theme.json 必备)")
.isNotNull();
assertThat(info.getName()).isEqualTo("default");
assertThat(info.getTitle()).isNotBlank();
assertThat(info.getSpecVersion()).isEqualTo(1);
ThemeParam primary = info.param("primary");
assertThat(primary).isNotNull();
assertThat(primary.getType()).isEqualTo(ThemeParam.TYPE_COLOR);
assertThat(primary.getCssVar()).isEqualTo("--primary");
assertThat(primary.getDefaultValue()).isNotBlank();
}
@Test
@DisplayName("T2: 坏 JSON 的包被剔除(t-broken-json)")
void brokenJsonPackageRejected() {
assertThat(registry.contains("t-broken-json")).isFalse();
assertThat(registry.contains("t-demo-red"))
.as("合法夹具包必须注册成功(防止'全部剔除'假绿)").isTrue();
}
@Test
@DisplayName("T3: 缺必备模板的包被剔除(t-missing-list:有清单无 list.html)")
void missingTemplatePackageRejected() {
assertThat(registry.contains("t-missing-list")).isFalse();
assertThat(registry.contains("t-demo-red")).isTrue();
}
@Test
@DisplayName("T4: name 与目录名不一致的包被剔除(t-mismatch)")
void nameMismatchPackageRejected() {
assertThat(registry.contains("t-mismatch")).isFalse();
assertThat(registry.contains("t-mismatch-x")).isFalse();
assertThat(registry.contains("t-demo-red")).isTrue();
}
@Test
@DisplayName("T5: param key 含非法字符的包被剔除(t-bad-key)")
void badParamKeyPackageRejected() {
assertThat(registry.contains("t-bad-key")).isFalse();
assertThat(registry.contains("t-demo-red")).isTrue();
}
@Test
@DisplayName("T6: cssVar 出现在非 color 类型参数上被剔除(t-bad-cssvar)")
void cssVarOnNonColorParamRejected() {
assertThat(registry.contains("t-bad-cssvar")).isFalse();
assertThat(registry.contains("t-demo-red")).isTrue();
}
@Test
@DisplayName("T7: screenshot 声明的资源缺失被剔除(t-bad-shot)")
void missingScreenshotPackageRejected() {
assertThat(registry.contains("t-bad-shot")).isFalse();
assertThat(registry.contains("t-demo-red")).isTrue();
}
@Test
@DisplayName("T8: 列表至少包含 default(护栏用例)")
void listContainsDefault() {
assertThat(registry.list())
.extracting(ThemeInfo::getName)
.contains(ThemeRegistry.DEFAULT_THEME);
}
@Test
@DisplayName("P2-T33: 三新包参数默认值互异(防拷贝忘改的低成本哨兵)")
void newPackagesPrimaryDefaultsDiffer() {
String defaultPrimary = registry.get(ThemeRegistry.DEFAULT_THEME).param("primary").getDefaultValue();
for (String name : new String[] {"classic-red", "blue-white", "warm-kinder"}) {
ThemeParam primary = registry.get(name).param("primary");
assertThat(primary).as("%s 应声明 primary 参数", name).isNotNull();
assertThat(primary.getDefaultValue())
.as("%s 的 primary 默认值不得与 default 相同(拷贝忘改哨兵)", name)
.isNotEqualTo(defaultPrimary);
}
}
@Test
@DisplayName("default 包缺失时 fail-fast(方案 §5.1,启动失败防线)")
void defaultMissingFailsFast() {
ThemeRegistry empty = new ThemeRegistry() {
@Override
public void load() {
// 模拟扫描不到任何包:load 为空即注册表为空
}
};
assertThatCode(empty::verifyDefaultPresent)
.as("default 缺失必须抛异常阻止启动(fail-fast,§5.1)")
.isInstanceOf(IllegalStateException.class);
}
// ===== B-7:image 类型参数路径校验与应用真实路径一致(T-6)=====
@Test
@DisplayName("B-7: image 参数仅 /cms/upload/** 合法(含 context-path),旧 /upload/** 不再放行")
void imageParamValidatesAgainstContextUploadPath() {
ThemeParam image = new ThemeParam();
image.setKey("heroImage");
image.setType(ThemeParam.TYPE_IMAGE);
assertThat(image.isValidValue("/cms/upload/cms/demo/202609/a.jpg")).isTrue();
assertThat(image.isValidValue("/upload/cms/demo/a.jpg"))
.as("旧约定缺 /cms 前缀,context-path=/cms 部署下渲染必 404").isFalse();
assertThat(image.isValidValue("javascript:alert(1)")).isFalse();
assertThat(image.isValidValue("https://evil.example/x.png")).isFalse();
assertThat(image.isValidValue(null)).isFalse();
assertThat(image.isValidValue("")).isFalse();
}
@Test
@DisplayName("B-7/T-6: image 参数端到端——夹具声明 → 注册加载 → filterValidValues 按 /cms/upload/ 过滤")
void imageParamFixtureEndToEnd() {
ThemeInfo info = registry.get("t-demo-red");
ThemeParam hero = info.param("heroImage");
assertThat(hero).as("夹具包应声明 image 类型参数").isNotNull();
assertThat(hero.getType()).isEqualTo(ThemeParam.TYPE_IMAGE);
assertThat(hero.getDefaultValue()).isEqualTo("/cms/upload/cms/t-demo-red/hero.png");
assertThat(info.filterValidValues(java.util.Collections.singletonMap(
"heroImage", "/cms/upload/cms/x/202609/b.png")))
.containsKey("heroImage");
assertThat(info.filterValidValues(java.util.Collections.singletonMap(
"heroImage", "/upload/cms/x/b.png")))
.as("旧 /upload/** 值应被过滤丢弃,不落库")
.doesNotContainKey("heroImage");
}
}