package com.guo.learningprogresstracker.service.impl; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.guo.learningprogresstracker.dto.ReviewFeedItem; import com.guo.learningprogresstracker.dto.request.UpsertReviewMindMapRequest; import com.guo.learningprogresstracker.entity.ReviewMindMapEntity; import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity; import com.guo.learningprogresstracker.entity.StudyReportsEntity; import com.guo.learningprogresstracker.entity.StudySessionsEntity; import com.guo.learningprogresstracker.entity.TaskEntity; import com.guo.learningprogresstracker.enums.ReviewMindMapFileFormatEnum; import com.guo.learningprogresstracker.enums.ReviewMindMapParseStatusEnum; import com.guo.learningprogresstracker.mapper.ReviewMindMapMapper; import com.guo.learningprogresstracker.mapper.StudyReportFragmentsMapper; import com.guo.learningprogresstracker.mapper.StudyReportsMapper; import com.guo.learningprogresstracker.mapper.StudySessionsMapper; import com.guo.learningprogresstracker.mapper.TasksMapper; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.mock.web.MockMultipartFile; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDateTime; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ReviewServiceImplTest { @BeforeAll static void initTableInfo() { MybatisConfiguration configuration = new MybatisConfiguration(); TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), StudyReportsEntity.class); TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), StudyReportFragmentsEntity.class); TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), StudySessionsEntity.class); TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), TaskEntity.class); TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), ReviewMindMapEntity.class); } @InjectMocks private ReviewServiceImpl reviewService; @Mock private StudyReportsMapper studyReportsMapper; @Mock private StudyReportFragmentsMapper studyReportFragmentsMapper; @Mock private StudySessionsMapper studySessionsMapper; @Mock private TasksMapper tasksMapper; @Mock private ReviewMindMapMapper reviewMindMapMapper; @Test void getReviewFeed_shouldApplyFinalLimitAfterMerge() { StudyReportsEntity report = new StudyReportsEntity(); report.setId(1); report.setSessionNum("S1"); report.setContent("report"); report.setCreatedTime(LocalDateTime.now()); StudyReportFragmentsEntity fragment = new StudyReportFragmentsEntity(); fragment.setId(2); fragment.setSessionNum("S1"); fragment.setContent("fragment"); fragment.setCreatedTime(LocalDateTime.now().minusMinutes(1)); StudySessionsEntity session = new StudySessionsEntity(); session.setSessionNum("S1"); session.setTaskNum("T1"); TaskEntity task = new TaskEntity(); task.setTaskNum("T1"); task.setTaskName("Task"); when(studyReportsMapper.selectList(any())).thenReturn(List.of(report)); when(studyReportFragmentsMapper.selectList(any())).thenReturn(List.of(fragment)); when(studySessionsMapper.selectList(any())).thenReturn(List.of(session)); when(tasksMapper.selectList(any())).thenReturn(List.of(task)); List items = reviewService.getReviewFeed(1, "recent"); assertEquals(1, items.size()); assertEquals("REPORT", items.get(0).getSourceType()); assertEquals("Task", items.get(0).getTaskName()); } @Test void upsertMindMap_shouldCreateWhenMissing() throws Exception { when(tasksMapper.exists(any())).thenReturn(true); when(reviewMindMapMapper.selectOne(any())).thenReturn(null); when(reviewMindMapMapper.insert(any())).thenAnswer(invocation -> { ReviewMindMapEntity entity = invocation.getArgument(0); entity.setId(3); return 1; }); UpsertReviewMindMapRequest request = new UpsertReviewMindMapRequest(); request.setTitle("Map"); request.setContent("root"); request.setContentFormat("mermaid"); ReviewMindMapEntity entity = reviewService.upsertMindMap("T1", request); assertNotNull(entity.getId()); assertEquals("T1", entity.getTaskNum()); assertEquals("MERMAID", entity.getContentFormat()); verify(reviewMindMapMapper).insert(any(ReviewMindMapEntity.class)); } @Test void upsertMindMap_shouldUpdateExisting() throws Exception { ReviewMindMapEntity existing = new ReviewMindMapEntity(); existing.setId(5); existing.setTaskNum("T1"); existing.setTitle("Old"); existing.setContent("old"); existing.setContentFormat("TEXT"); when(tasksMapper.exists(any())).thenReturn(true); when(reviewMindMapMapper.selectOne(any())).thenReturn(existing); UpsertReviewMindMapRequest request = new UpsertReviewMindMapRequest(); request.setTitle("New"); request.setContent("new"); request.setContentFormat("json"); ReviewMindMapEntity entity = reviewService.upsertMindMap("T1", request); assertEquals(5, entity.getId()); assertEquals("New", entity.getTitle()); assertEquals("JSON", entity.getContentFormat()); verify(reviewMindMapMapper).updateById(existing); } @Test void uploadMindMap_shouldParseMarkdownFile() throws Exception { when(tasksMapper.exists(any())).thenReturn(true); when(reviewMindMapMapper.selectOne(any())).thenReturn(null); when(reviewMindMapMapper.insert(any())).thenAnswer(invocation -> { ReviewMindMapEntity entity = invocation.getArgument(0); entity.setId(9); return 1; }); MockMultipartFile file = new MockMultipartFile( "file", "java-review.md", "text/markdown", "# Java 并发\n## 线程基础\n- RUNNABLE\n".getBytes()); ReviewMindMapEntity entity = reviewService.uploadMindMap("T1", file); try { assertEquals(9, entity.getId()); assertEquals("FILE", entity.getSourceType()); assertEquals(ReviewMindMapFileFormatEnum.MARKDOWN.getCode(), entity.getFileFormat()); assertEquals(ReviewMindMapParseStatusEnum.SUCCESS.getCode(), entity.getParseStatus()); assertEquals("JSON", entity.getContentFormat()); assertNotNull(entity.getParsedContent()); assertEquals(true, entity.getParsedContent().contains("线程基础")); assertEquals(true, entity.getContent().contains("RUNNABLE")); } finally { if (entity.getFilePath() != null) { Path storedFile = Path.of(entity.getFilePath()); Files.deleteIfExists(storedFile); Files.deleteIfExists(storedFile.getParent()); } } verify(reviewMindMapMapper).insert(any(ReviewMindMapEntity.class)); } }