Files
lpt-be/src/test/java/com/guo/learningprogresstracker/service/impl/StudySessionsServiceImplTest.java
T
cat-shark d0c78ceb28 test: 新增 StudySessionsServiceImpl 单元测试 (10 cases)
覆盖场景:ONGOING/PAUSED 状态的消息返回、报告创建、
边界值、会话不存在异常、updateById 调用验证
2026-05-30 17:37:29 +08:00

214 lines
8.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.guo.learningprogresstracker.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
import com.guo.learningprogresstracker.entity.StudySessionsEntity;
import com.guo.learningprogresstracker.enums.StudySessionStateEnum;
import com.guo.learningprogresstracker.mapper.StudyReportFragmentsMapper;
import com.guo.learningprogresstracker.mapper.StudyReportsMapper;
import com.guo.learningprogresstracker.mapper.StudySessionsMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.mockito.junit.jupiter.MockitoExtension;
import java.time.LocalDateTime;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class StudySessionsServiceImplTest {
@Spy
@InjectMocks
private StudySessionsServiceImpl studySessionsService;
@Mock
private StudyReportsMapper studyReportsMapper;
@Mock
private StudyReportFragmentsMapper studyReportFragmentsMapper;
@Mock
private StudySessionsMapper studySessionsMapper;
@BeforeEach
void setUp() {
// stub updateById,避免调用真实的 getBaseMapper()
doReturn(true).when(studySessionsService).updateById(any());
}
private StudySessionsEntity createSession(String state, int minutesAgo) {
StudySessionsEntity session = new StudySessionsEntity();
session.setSessionNum("SESSION_TEST");
session.setSessionState(state);
session.setStartTime(LocalDateTime.now().minusMinutes(minutesAgo));
session.setLastStartTime(LocalDateTime.now().minusMinutes(minutesAgo));
session.setEffectiveTime(0);
session.setActualTime(0);
return session;
}
private void mockGetOneOpt(StudySessionsEntity session) {
doReturn(Optional.of(session)).when(studySessionsService).getOneOpt(any(Wrapper.class));
}
/**
* 场景1ONGOING 状态,有效时间 < 10分钟 → 返回提示消息
*/
@Test
void endedStudySession_ongoing_lessThan10Min_shouldReturnMessage() throws Exception {
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 5);
mockGetOneOpt(session);
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
assertEquals("本次有效学习时间不足10分钟,不计入总学习时间", message);
assertEquals(0, session.getEffectiveTime(), "effectiveTime 应被清零");
assertEquals(StudySessionStateEnum.ENDED.name(), session.getSessionState());
verify(studyReportsMapper).insert(any(StudyReportsEntity.class));
}
/**
* 场景2ONGOING 状态,有效时间 > 10分钟 → 返回 null(正常结束)
*/
@Test
void endedStudySession_ongoing_moreThan10Min_shouldReturnNull() throws Exception {
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 15);
mockGetOneOpt(session);
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
assertNull(message, "有效时间超过10分钟不应返回提示");
assertTrue(session.getEffectiveTime() > 0, "effectiveTime 应保留");
assertEquals(StudySessionStateEnum.ENDED.name(), session.getSessionState());
}
/**
* 场景3PAUSED 状态,有效时间 < 10分钟 → 返回 nullPAUSED 不触发提示)
*/
@Test
void endedStudySession_paused_lessThan10Min_shouldReturnNull() throws Exception {
StudySessionsEntity session = createSession(StudySessionStateEnum.PAUSED.name(), 5);
session.setEffectiveTime(5 * 60);
mockGetOneOpt(session);
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
assertNull(message, "PAUSED 状态结束不返回提示(用户已主动暂停)");
assertEquals(0, session.getEffectiveTime(), "effectiveTime 仍应被实体层清零");
}
/**
* 场景4PAUSED 状态,有效时间 > 10分钟 → 返回 nulleffectiveTime 保留
*/
@Test
void endedStudySession_paused_moreThan10Min_shouldReturnNull() throws Exception {
StudySessionsEntity session = createSession(StudySessionStateEnum.PAUSED.name(), 15);
session.setEffectiveTime(15 * 60);
mockGetOneOpt(session);
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
assertNull(message);
assertTrue(session.getEffectiveTime() > 0, "effectiveTime 应保留");
}
/**
* 场景5:无论是否触发清零,学习报告都应正常创建
*/
@Test
void endedStudySession_shouldAlwaysCreateReport() throws Exception {
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 3);
mockGetOneOpt(session);
studySessionsService.endedStudySession("SESSION_TEST", "测试报告内容");
verify(studyReportsMapper).insert(argThat(report ->
"SESSION_TEST".equals(((StudyReportsEntity) report).getSessionNum())
&& "测试报告内容".equals(((StudyReportsEntity) report).getContent())
));
}
/**
* 场景6:ONGOING 状态,有效时间恰好 = 10分钟 → 有效时间保留(600 不 < 600
*/
@Test
void endedStudySession_ongoing_exactly10Min_shouldReturnNull() throws Exception {
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 10);
mockGetOneOpt(session);
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
assertNull(message, "有效时间等于10分钟应保留(阈值为 < 600");
assertTrue(session.getEffectiveTime() >= 10 * 60, "effectiveTime 应保留");
}
/**
* 场景7:会话不存在 → 抛出异常
*/
@Test
void endedStudySession_sessionNotFound_shouldThrowException() {
doReturn(Optional.empty()).when(studySessionsService).getOneOpt(any(Wrapper.class));
assertThrows(Exception.class, () ->
studySessionsService.endedStudySession("NOT_EXIST", "内容"));
}
/**
* 场景8:ONGOING 状态,有效时间极短(1分钟) → 返回提示消息
*/
@Test
void endedStudySession_ongoing_veryShortTime_shouldReturnMessage() throws Exception {
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 1);
mockGetOneOpt(session);
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
assertEquals("本次有效学习时间不足10分钟,不计入总学习时间", message);
assertEquals(0, session.getEffectiveTime());
}
/**
* 场景9:ONGOING 状态,刚开始就结束(effectiveTime ≈ 0 → 返回提示消息
*/
@Test
void endedStudySession_ongoing_immediatelyEnded_shouldReturnMessage() throws Exception {
StudySessionsEntity session = new StudySessionsEntity();
session.setSessionNum("SESSION_INSTANT");
session.setSessionState(StudySessionStateEnum.ONGOING.name());
session.setStartTime(LocalDateTime.now());
session.setLastStartTime(LocalDateTime.now());
session.setEffectiveTime(0);
session.setActualTime(0);
mockGetOneOpt(session);
String message = studySessionsService.endedStudySession("SESSION_INSTANT", "内容");
assertEquals("本次有效学习时间不足10分钟,不计入总学习时间", message);
assertEquals(0, session.getEffectiveTime());
}
/**
* 场景10:确认 updateById 被调用(会话状态持久化)
*/
@Test
void endedStudySession_shouldCallUpdateById() throws Exception {
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 5);
mockGetOneOpt(session);
studySessionsService.endedStudySession("SESSION_TEST", "内容");
verify(studySessionsService).updateById(session);
}
}