Files
YschcmsService/src/test/java/com/yunhai/cms/support/CmsIntegrationTestBase.java
T

173 lines
6.7 KiB
Java

package com.yunhai.cms.support;
import com.yunhai.cms.entity.CmsArticle;
import com.yunhai.cms.entity.CmsBanner;
import com.yunhai.cms.entity.CmsCategory;
import com.yunhai.cms.entity.CmsLink;
import com.yunhai.cms.entity.CmsSite;
import com.yunhai.cms.enums.ArticleStatusEnum;
import com.yunhai.cms.enums.CategoryTypeEnum;
import com.yunhai.cms.repository.CmsArticleRepository;
import com.yunhai.cms.repository.CmsAttachmentRepository;
import com.yunhai.cms.repository.CmsBannerRepository;
import com.yunhai.cms.repository.CmsCategoryRepository;
import com.yunhai.cms.repository.CmsLinkRepository;
import com.yunhai.cms.repository.CmsSiteRepository;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cache.CacheManager;
import java.time.LocalDateTime;
/**
* 集成测试基类:真实 Spring 上下文 + 本地 MySQL(bsch_cms_test) + Caffeine 缓存。
*
* <p>所有子类共享同一上下文配置(本类注解即完整配置),Spring TestContext 只启动一次,
* schema-cms-test.sql 的 DROP/CREATE 也只执行一次;跨测试的库与缓存清洁由
* {@link #clearCaches()} / {@link #purgeTables()} 负责:
* <ul>
* <li>@Transactional 用例:回滚即隔离,仅需清缓存(缓存是进程级全局的,不随事务回滚);</li>
* <li>非事务用例(事件 AFTER_COMMIT / MockMvc / 定时任务):@BeforeEach purge + @AfterEach purge。</li>
* </ul>
*/
@SpringBootTest(properties = "cms.schedule.auto-enabled=false")
@AutoConfigureMockMvc
public abstract class CmsIntegrationTestBase {
@Autowired
protected CacheManager cacheManager;
@Autowired
protected CmsSiteRepository siteRepository;
@Autowired
protected CmsCategoryRepository categoryRepository;
@Autowired
protected CmsArticleRepository articleRepository;
@Autowired
protected CmsBannerRepository bannerRepository;
@Autowired
protected CmsLinkRepository linkRepository;
@Autowired
protected CmsAttachmentRepository attachmentRepository;
@BeforeEach
void clearCachesBeforeEachTest() {
clearCaches();
}
/** 清空全部 CMS 缓存(cms:site / cms:article / cms:list),防止用例间缓存串扰 */
protected void clearCaches() {
for (String name : cacheManager.getCacheNames()) {
cacheManager.getCache(name).clear();
}
}
/** 清空 6 张业务表(无外键约束,顺序无碍),非事务用例的隔离手段 */
protected void purgeTables() {
attachmentRepository.deleteAllInBatch();
articleRepository.deleteAllInBatch();
categoryRepository.deleteAllInBatch();
bannerRepository.deleteAllInBatch();
linkRepository.deleteAllInBatch();
siteRepository.deleteAllInBatch();
}
// ===== 落库种子(返回带自增 id 的实体)=====
protected CmsSite persistSite(String code, Integer schId, String name) {
CmsSite site = new CmsSite();
site.setCode(code);
site.setPath(code); // W4:顶层 path=code(与 SiteService.save 口径一致)
site.setSchId(schId);
site.setSchName("学校-" + schId);
site.setName(name);
site.setTheme("default");
site.setStatus(1);
return siteRepository.saveAndFlush(site);
}
/** 带上级站点的落库(院系子站,《院系子站方案》):parentSiteId 传 null 视为顶层 0 */
protected CmsSite persistSite(String code, Integer schId, String name, Integer parentSiteId) {
CmsSite site = persistSite(code, schId, name);
if (parentSiteId == null || parentSiteId == 0) {
site.setParentSiteId(0);
} else {
CmsSite parent = siteRepository.findById(parentSiteId)
.orElseThrow(IllegalStateException::new);
site.setParentSiteId(parentSiteId);
site.setPath(parent.getPath() + "/" + code); // W4:子站 path=父path/code
}
return siteRepository.saveAndFlush(site);
}
protected CmsCategory persistCategory(Integer siteId, Integer parentId, String name,
CategoryTypeEnum type) {
CmsCategory c = new CmsCategory();
c.setSiteId(siteId);
c.setParentId(parentId == null ? 0 : parentId);
c.setName(name);
c.setType(type.getCode());
c.setStatus(1);
return categoryRepository.saveAndFlush(c);
}
protected CmsArticle persistArticle(Integer siteId, Integer categoryId, String title,
ArticleStatusEnum status, LocalDateTime publishTime) {
CmsArticle a = new CmsArticle();
a.setSiteId(siteId);
a.setCategoryId(categoryId);
a.setTitle(title);
a.setContent("<p>" + title + " 正文</p>");
a.setSummary(title + " 摘要");
a.setStatus(status.getCode());
a.setPublishTime(publishTime);
return articleRepository.saveAndFlush(a);
}
protected CmsBanner persistBanner(Integer siteId, String position, String title,
LocalDateTime start, LocalDateTime end) {
CmsBanner b = new CmsBanner();
b.setSiteId(siteId);
b.setPosition(position);
b.setTitle(title);
b.setImageUrl("/upload/cms/banner.png");
b.setLinkType(3);
b.setLinkValue("https://www.example.com");
b.setStatus(1);
b.setStartTime(start);
b.setEndTime(end);
return bannerRepository.saveAndFlush(b);
}
protected CmsLink persistLink(Integer siteId, String groupName, String name) {
CmsLink l = new CmsLink();
l.setSiteId(siteId);
l.setGroupName(groupName);
l.setName(name);
l.setLinkUrl("https://www.example.com");
l.setStatus(1);
return linkRepository.saveAndFlush(l);
}
/** 简易轮询等待(无 awaitility 依赖):最长 waitMillis 内每 50ms 检查一次期望 */
protected static void awaitTrue(long waitMillis, java.util.function.BooleanSupplier condition)
throws InterruptedException {
long deadline = System.currentTimeMillis() + waitMillis;
while (System.currentTimeMillis() < deadline) {
if (condition.getAsBoolean()) {
return;
}
Thread.sleep(50);
}
}
protected static LocalDateTime nowPlusSeconds(long seconds) {
return LocalDateTime.now().plusSeconds(seconds);
}
protected static LocalDateTime nowMinusSeconds(long seconds) {
return LocalDateTime.now().minusSeconds(seconds);
}
}