Files
YschcmsService/src/test/java/com/yunhai/cms/portal/ThemeRenderIT.java
T
2026-09-07 09:09:39 +08:00

97 lines
3.6 KiB
Java

package com.yunhai.cms.portal;
import com.yunhai.cms.entity.CmsSite;
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.MvcResult;
import java.nio.charset.StandardCharsets;
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.status;
/**
* 主题微调参数渲染注入(方案 §8.3 C 组 T13-T15,§5.3):
* 站点 config_json.theme_params 经快照装配 → head 内联 :root 覆盖串(位于皮肤 CSS 之后)。
*/
@DisplayName("ThemeRender - 微调参数 CSS 注入(方案 §5.3)")
class ThemeRenderIT extends CmsIntegrationTestBase {
@Autowired
private MockMvc mockMvc;
@BeforeEach
void seed() {
purgeTables();
}
@AfterEach
void clean() {
purgeTables();
}
private void persistSiteWithConfig(String code, String configJson) {
CmsSite site = new CmsSite();
site.setCode(code);
site.setSchId(1);
site.setSchName("学校-1");
site.setName("注入测试门户");
site.setTheme("default");
site.setConfigJson(configJson);
site.setStatus(1);
siteRepository.saveAndFlush(site);
}
private String homeHtml(String siteCode) throws Exception {
MvcResult result = mockMvc.perform(get("/portal/{code}/", siteCode))
.andExpect(status().isOk())
.andReturn();
return result.getResponse().getContentAsString(StandardCharsets.UTF_8);
}
@Test
@DisplayName("T13: 微调参数注入 CSS 变量且位于皮肤 CSS link 之后")
void themeParamsInjectedAfterSkinCss() throws Exception {
persistSiteWithConfig("inj1", "{\"theme_params\":{\"primary\":\"#b01a2e\"}}");
String html = homeHtml("inj1");
assertThat(html).as("合法色值应注入").contains("--primary:#b01a2e");
int cssLinkIndex = html.indexOf("css/main.css");
int varIndex = html.indexOf("--primary:#b01a2e");
assertThat(cssLinkIndex).as("皮肤 CSS link 应存在").isGreaterThan(-1);
assertThat(varIndex).as("内联覆盖串必须在皮肤 CSS link 之后(层叠后写覆盖)")
.isGreaterThan(cssLinkIndex);
}
@Test
@DisplayName("T14: 非法色值不注入(red;} / javascript:alert(1) 防注入)")
void illegalColorValuesNotInjected() throws Exception {
persistSiteWithConfig("inj2", "{\"theme_params\":{\"primary\":\"red;}}\"}}");
String html = homeHtml("inj2");
assertThat(html).doesNotContain("red;}}").doesNotContain("red;}");
assertThat(html).doesNotContain("--primary:red");
purgeTables();
persistSiteWithConfig("inj3", "{\"theme_params\":{\"primary\":\"javascript:alert(1)\"}}");
String html3 = homeHtml("inj3");
assertThat(html3).doesNotContain("javascript:alert(1)");
assertThat(html3).doesNotContain("--primary:javascript");
}
@Test
@DisplayName("T15: 未声明 key 不注入(按主题 schema 过滤)")
void undeclaredKeysNotInjected() throws Exception {
persistSiteWithConfig("inj4", "{\"theme_params\":{\"zzhack\":\"zzvalue\"}}");
String html = homeHtml("inj4");
assertThat(html).doesNotContain("zzhack").doesNotContain("zzvalue");
}
}