|
@@ -0,0 +1,151 @@
|
|
|
|
|
+package com.etotem.cfc.unit;
|
|
|
|
|
+
|
|
|
|
|
+import com.etotem.cfc.entity.Article;
|
|
|
|
|
+import com.etotem.cfc.entity.User;
|
|
|
|
|
+import com.etotem.cfc.mapper.ArticleMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.UserMapper;
|
|
|
|
|
+import com.etotem.cfc.service.ArticleService;
|
|
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
|
|
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
|
+
|
|
|
|
|
+import java.lang.reflect.Field;
|
|
|
|
|
+import java.util.Collections;
|
|
|
|
|
+
|
|
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
|
|
+import static org.mockito.Mockito.*;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 文章发布路径自动打标签单元测试(纯逻辑,mock Mapper / 线程池,无需 DB)
|
|
|
|
|
+ *
|
|
|
|
|
+ * 覆盖:发布/审核通过/家庭发布且 tags 为空时异步调度生成;
|
|
|
|
|
+ * 已有标签、未发布、文章不存在时不调度。
|
|
|
|
|
+ */
|
|
|
|
|
+class ArticleAutoTagServiceTest {
|
|
|
|
|
+
|
|
|
|
|
+ private ArticleService service;
|
|
|
|
|
+ private ArticleMapper articleMapper;
|
|
|
|
|
+ private UserMapper userMapper;
|
|
|
|
|
+ private ThreadPoolTaskExecutor taskExecutor;
|
|
|
|
|
+
|
|
|
|
|
+ @BeforeEach
|
|
|
|
|
+ void setUp() throws Exception {
|
|
|
|
|
+ service = new ArticleService();
|
|
|
|
|
+ articleMapper = mock(ArticleMapper.class);
|
|
|
|
|
+ userMapper = mock(UserMapper.class);
|
|
|
|
|
+ taskExecutor = mock(ThreadPoolTaskExecutor.class);
|
|
|
|
|
+ inject("articleMapper", articleMapper);
|
|
|
|
|
+ inject("userMapper", userMapper);
|
|
|
|
|
+ inject("taskExecutor", taskExecutor);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void inject(String field, Object value) throws Exception {
|
|
|
|
|
+ Field f = ArticleService.class.getDeclaredField(field);
|
|
|
|
|
+ f.setAccessible(true);
|
|
|
|
|
+ f.set(service, value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Article article(Long id, String status, String tags) {
|
|
|
|
|
+ Article a = new Article();
|
|
|
|
|
+ a.setId(id);
|
|
|
|
|
+ a.setStatus(status);
|
|
|
|
|
+ a.setTags(tags);
|
|
|
|
|
+ a.setTitle("测试文章");
|
|
|
|
|
+ a.setContent("<p>正文</p>");
|
|
|
|
|
+ return a;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void toggleStatus_发布且标签为空_调度自动打标() {
|
|
|
|
|
+ when(articleMapper.selectById(1L)).thenReturn(article(1L, "draft", ""));
|
|
|
|
|
+
|
|
|
|
|
+ service.toggleStatus(1L, "published");
|
|
|
|
|
+
|
|
|
|
|
+ verify(taskExecutor, times(1)).execute(any(Runnable.class));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void toggleStatus_标签非空_不调度自动打标() {
|
|
|
|
|
+ when(articleMapper.selectById(1L)).thenReturn(article(1L, "draft", "健康,饮水"));
|
|
|
|
|
+
|
|
|
|
|
+ service.toggleStatus(1L, "published");
|
|
|
|
|
+
|
|
|
|
|
+ verify(taskExecutor, never()).execute(any(Runnable.class));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void toggleStatus_置为草稿_不调度自动打标() {
|
|
|
|
|
+ when(articleMapper.selectById(1L)).thenReturn(article(1L, "published", null));
|
|
|
|
|
+
|
|
|
|
|
+ service.toggleStatus(1L, "draft");
|
|
|
|
|
+
|
|
|
|
|
+ verify(taskExecutor, never()).execute(any(Runnable.class));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void toggleStatus_文章不存在_不调度自动打标() {
|
|
|
|
|
+ when(articleMapper.selectById(1L)).thenReturn(null);
|
|
|
|
|
+
|
|
|
|
|
+ service.toggleStatus(1L, "published");
|
|
|
|
|
+
|
|
|
|
|
+ verify(taskExecutor, never()).execute(any(Runnable.class));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void auditArticle_审核通过且标签为空_调度自动打标() {
|
|
|
|
|
+ Article a = article(2L, "draft", null);
|
|
|
|
|
+ a.setAuditStatus("pending");
|
|
|
|
|
+ when(articleMapper.selectById(2L)).thenReturn(a);
|
|
|
|
|
+
|
|
|
|
|
+ service.auditArticle(2L, "approved", null, 99L);
|
|
|
|
|
+
|
|
|
|
|
+ verify(taskExecutor, times(1)).execute(any(Runnable.class));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void auditArticle_审核拒绝_不调度自动打标() {
|
|
|
|
|
+ Article a = article(2L, "draft", "");
|
|
|
|
|
+ a.setAuditStatus("pending");
|
|
|
|
|
+ when(articleMapper.selectById(2L)).thenReturn(a);
|
|
|
|
|
+
|
|
|
|
|
+ service.auditArticle(2L, "rejected", "内容不符", 99L);
|
|
|
|
|
+
|
|
|
|
|
+ verify(taskExecutor, never()).execute(any(Runnable.class));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void publishByUser_家庭可见直接发布且标签为空_调度自动打标() {
|
|
|
|
|
+ User user = new User();
|
|
|
|
|
+ user.setId(10L);
|
|
|
|
|
+ user.setFamilyId(114L);
|
|
|
|
|
+ when(userMapper.selectById(10L)).thenReturn(user);
|
|
|
|
|
+ when(articleMapper.insert(any(Article.class))).thenAnswer(inv -> {
|
|
|
|
|
+ Article a = inv.getArgument(0);
|
|
|
|
|
+ a.setId(200L);
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ });
|
|
|
|
|
+ // ensureAutoTagsIfEmpty 内部会按 id 重新查询已持久化的文章(published + 空标签)
|
|
|
|
|
+ Article persisted = new Article();
|
|
|
|
|
+ persisted.setId(200L);
|
|
|
|
|
+ persisted.setStatus("published");
|
|
|
|
|
+ persisted.setTags(null);
|
|
|
|
|
+ when(articleMapper.selectById(200L)).thenReturn(persisted);
|
|
|
|
|
+
|
|
|
|
|
+ Article created = service.publishByUser("标题", "<p>内容</p>", null, "private", "mind", 10L, "user");
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals("published", created.getStatus());
|
|
|
|
|
+ verify(taskExecutor, times(1)).execute(any(Runnable.class));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void publishByUser_公开文章待审核草稿_不立即打标() {
|
|
|
|
|
+ when(articleMapper.insert(any(Article.class))).thenReturn(1);
|
|
|
|
|
+
|
|
|
|
|
+ Article created = service.publishByUser("标题", "<p>内容</p>", null, "public", null, 10L, "user");
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals("draft", created.getStatus());
|
|
|
|
|
+ verify(taskExecutor, never()).execute(any(Runnable.class));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|