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,112 @@
package com.yunhai.cms.theme;
import com.yunhai.cms.controller.api.ThemeController;
import com.yunhai.cms.service.impl.ThemeService;
import com.yunhai.cms.support.CmsIntegrationTestBase;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* 主题管理接口契约(方案 §8.3 E 组 T18-T21,§5.2)。
* 测试分层(§8.6):T18 全上下文 MockMvc(匿名 CAS 302 契约,现有唯一先例模式);
* T19 服务级直调;T20/T21 standalone MockMvc(不走 CAS 过滤器链的控制器薄契约)。
*/
@DisplayName("ThemeApi - /api/theme 接口契约(方案 §5.2)")
class ThemeApiIT extends CmsIntegrationTestBase {
@Autowired
private MockMvc mockMvc;
@Autowired
private ThemeService themeService;
@Autowired
private ThemeRegistry themeRegistry;
private MockMvc standalone;
@BeforeEach
void setUp() {
purgeTables();
standalone = MockMvcBuilders
.standaloneSetup(new ThemeController(themeService, themeRegistry)).build();
}
@AfterEach
void clean() {
purgeTables();
}
@Test
@DisplayName("T18: /api/theme/list 匿名 302 至 SSO(CAS 契约,同 /api/site 先例)")
void themeList_anonymousRedirectsToSso() throws Exception {
mockMvc.perform(get("/api/theme/list"))
.andExpect(status().is3xxRedirection())
.andExpect(result -> assertThat(result.getResponse().getRedirectedUrl())
.contains("sso."));
}
@Test
@DisplayName("T19: 主题列表返回 schema(含 default:name/title/params/bannerSize/screenshotUrl)")
void themeList_containsDefaultWithSchema() {
List<com.yunhai.cms.model.vo.ThemeVo> list = themeService.list();
assertThat(list).as("至少包含 default(Registry 已注册)")
.extracting(vo -> vo.getName()).contains("default");
com.yunhai.cms.model.vo.ThemeVo def = list.stream()
.filter(v -> "default".equals(v.getName()))
.findFirst().orElseThrow(IllegalStateException::new);
assertThat(def.getTitle()).isNotBlank();
assertThat(def.getBannerSize()).isNotBlank();
assertThat(def.getScreenshotUrl()).isEqualTo("/api/theme/default/asset/screenshot.png");
assertThat(def.getParams()).isNotEmpty();
com.yunhai.cms.model.vo.ThemeVo.ThemeParamVo primary = def.getParams().stream()
.filter(p -> "primary".equals(p.getKey()))
.findFirst().orElseThrow(IllegalStateException::new);
assertThat(primary.getType()).isEqualTo("color");
assertThat(primary.getCssVar()).isEqualTo("--primary");
assertThat(primary.getDefaultValue()).isNotBlank();
}
@Test
@DisplayName("T20: 资产代理防穿越(..%2f 拒绝 400/404)")
void assetTraversalRejected() throws Exception {
standalone.perform(get("/api/theme/t-demo-red/asset/..%2F..%2Fapplication.yml"))
.andExpect(result -> assertThat(result.getResponse().getStatus()).isIn(400, 404));
}
@Test
@DisplayName("T21: 未注册主题资产 404(不落目录拼接)")
void unregisteredThemeAsset404() throws Exception {
standalone.perform(get("/api/theme/no-such-theme/asset/screenshot.png"))
.andExpect(status().isNotFound());
}
@Test
@DisplayName("T21 补充: 夹具主题 screenshot 经代理可取且带缓存头")
void fixtureScreenshotServed() throws Exception {
standalone.perform(get("/api/theme/t-demo-red/asset/screenshot.png"))
.andExpect(status().isOk())
.andExpect(header().string("Cache-Control", "max-age=3600, public"));
}
@Test
@DisplayName("P2-T32: 三新包 screenshot 经资产代理可取(registry 校验存在 + 接口冒烟)")
void newPackageScreenshotsServed() throws Exception {
for (String name : new String[] {"classic-red", "blue-white", "warm-kinder"}) {
standalone.perform(get("/api/theme/{name}/asset/screenshot.png", name))
.andExpect(status().isOk())
.andExpect(header().string("Cache-Control", "max-age=3600, public"));
}
}
}