init local code
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user