You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
418 lines
22 KiB
418 lines
22 KiB
package com.mydreamjob.application;
|
|
|
|
import com.mydreamjob.audit.AuditService;
|
|
import com.mydreamjob.common.exception.BusinessException;
|
|
import com.mydreamjob.common.exception.ErrorCode;
|
|
import com.mydreamjob.application.dto.ApplicationCreateRequest;
|
|
import com.mydreamjob.application.dto.ApplicationStatusRequest;
|
|
import com.mydreamjob.application.dto.CandidateDetailView;
|
|
import com.mydreamjob.application.dto.CandidateListResponse;
|
|
import com.mydreamjob.profile.StudentProfile;
|
|
import jakarta.persistence.EntityManager;
|
|
import jakarta.persistence.TypedQuery;
|
|
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.junit.jupiter.MockitoExtension;
|
|
import org.springframework.test.util.ReflectionTestUtils;
|
|
|
|
import java.util.*;
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.ArgumentMatchers.anyString;
|
|
import static org.mockito.ArgumentMatchers.anyInt;
|
|
import static org.mockito.ArgumentMatchers.eq;
|
|
import static org.mockito.Mockito.*;
|
|
|
|
@ExtendWith(MockitoExtension.class)
|
|
class ApplicationServiceTest {
|
|
|
|
@Mock private ApplicationRepository applicationRepository;
|
|
@Mock private com.mydreamjob.job.JobService jobService;
|
|
@Mock private com.mydreamjob.auth.UserRepository userRepository;
|
|
@Mock private com.mydreamjob.company.CompanyRepository companyRepository;
|
|
@Mock private AuditService auditService;
|
|
@Mock private ApplicationStatusHistoryRepository historyRepository;
|
|
@Mock private com.mydreamjob.notification.NotificationService notificationService;
|
|
@Mock private EntityManager em;
|
|
@Mock private com.mydreamjob.candidatescore.refresh.CandidateScoringRefreshRequestService scoringRefreshRequestService;
|
|
@InjectMocks private ApplicationService applicationService;
|
|
|
|
private static final Long STUDENT_ID = 100L;
|
|
private static final Long COMPANY_ID = 1L;
|
|
private static final Long JOB_ID = 10L;
|
|
private static final Long APP_ID = 200L;
|
|
private com.mydreamjob.job.Job publishedJob;
|
|
private Application application;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
ReflectionTestUtils.setField(applicationService, "em", em);
|
|
publishedJob = com.mydreamjob.job.Job.builder().id(JOB_ID).companyId(COMPANY_ID).title("Java").status("PUBLISHED").build();
|
|
application = Application.builder().id(APP_ID).jobId(JOB_ID).studentUserId(STUDENT_ID).status("SUBMITTED").build();
|
|
}
|
|
|
|
private void mockStudentRole() {
|
|
com.mydreamjob.auth.User user = com.mydreamjob.auth.User.builder().id(STUDENT_ID).phone("13800000001")
|
|
.role(com.mydreamjob.auth.User.Role.STUDENT).active(true).verified(true).build();
|
|
user.setRoles(new HashSet<>(Set.of(com.mydreamjob.auth.User.Role.STUDENT)));
|
|
when(userRepository.findById(STUDENT_ID)).thenReturn(Optional.of(user));
|
|
}
|
|
|
|
private void mockStudentRoleWithRoles() {
|
|
com.mydreamjob.auth.User user = com.mydreamjob.auth.User.builder().id(STUDENT_ID).phone("TEST-STUDENT")
|
|
.role(com.mydreamjob.auth.User.Role.STUDENT).active(true).verified(true).build();
|
|
user.setRoles(new HashSet<>(Set.of(com.mydreamjob.auth.User.Role.STUDENT)));
|
|
when(userRepository.findByIdWithRoles(STUDENT_ID)).thenReturn(Optional.of(user));
|
|
}
|
|
|
|
// ── 撤回 ─────────────────────────────────────────────────
|
|
|
|
@Test void withdraw_submitted_shouldSucceed() {
|
|
mockStudentRole();
|
|
when(applicationRepository.findById(APP_ID)).thenReturn(Optional.of(application));
|
|
when(applicationRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
|
when(historyRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
|
|
|
Application result = applicationService.withdraw(STUDENT_ID, APP_ID);
|
|
assertEquals("WITHDRAWN", result.getStatus());
|
|
}
|
|
|
|
@Test void withdraw_wrongStudent_shouldReturn403() {
|
|
mockStudentRole();
|
|
application.setStudentUserId(999L);
|
|
when(applicationRepository.findById(APP_ID)).thenReturn(Optional.of(application));
|
|
|
|
BusinessException ex = assertThrows(BusinessException.class,
|
|
() -> applicationService.withdraw(STUDENT_ID, APP_ID));
|
|
assertEquals(ErrorCode.APPLICATION_OWNER_MISMATCH.getCode(), ex.getErrorCodeInt());
|
|
}
|
|
|
|
@Test void withdraw_alreadyWithdrawn_shouldReturn409() {
|
|
mockStudentRole();
|
|
application.setStatus("WITHDRAWN");
|
|
when(applicationRepository.findById(APP_ID)).thenReturn(Optional.of(application));
|
|
|
|
BusinessException ex = assertThrows(BusinessException.class,
|
|
() -> applicationService.withdraw(STUDENT_ID, APP_ID));
|
|
assertEquals(ErrorCode.INVALID_APPLICATION_STATUS_TRANSITION.getCode(), ex.getErrorCodeInt());
|
|
}
|
|
|
|
@Test void withdraw_rejected_shouldReturn409() {
|
|
mockStudentRole();
|
|
application.setStatus("REJECTED");
|
|
when(applicationRepository.findById(APP_ID)).thenReturn(Optional.of(application));
|
|
|
|
BusinessException ex = assertThrows(BusinessException.class,
|
|
() -> applicationService.withdraw(STUDENT_ID, APP_ID));
|
|
assertEquals(ErrorCode.INVALID_APPLICATION_STATUS_TRANSITION.getCode(), ex.getErrorCodeInt());
|
|
}
|
|
|
|
// ── 时间线 ───────────────────────────────────────────────
|
|
|
|
@Test void getTimeline_asStudent_shouldSucceed() {
|
|
mockStudentRoleWithRoles();
|
|
when(applicationRepository.findById(APP_ID)).thenReturn(Optional.of(application));
|
|
when(jobService.getJob(JOB_ID)).thenReturn(publishedJob);
|
|
when(historyRepository.findByApplicationIdOrderByCreatedAtAsc(APP_ID)).thenReturn(List.of());
|
|
|
|
var result = applicationService.getTimeline(APP_ID, STUDENT_ID);
|
|
assertNotNull(result);
|
|
}
|
|
|
|
@Test void getTimeline_wrongUser_shouldReturn403() {
|
|
com.mydreamjob.auth.User other = com.mydreamjob.auth.User.builder().id(999L).phone("13800000999")
|
|
.role(com.mydreamjob.auth.User.Role.STUDENT).active(true).verified(true).build();
|
|
other.setRoles(new HashSet<>(Set.of(com.mydreamjob.auth.User.Role.STUDENT)));
|
|
when(userRepository.findByIdWithRoles(999L)).thenReturn(Optional.of(other));
|
|
when(applicationRepository.findById(APP_ID)).thenReturn(Optional.of(application));
|
|
when(jobService.getJob(JOB_ID)).thenReturn(publishedJob);
|
|
|
|
BusinessException ex = assertThrows(BusinessException.class,
|
|
() -> applicationService.getTimeline(APP_ID, 999L));
|
|
assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt());
|
|
}
|
|
|
|
// ── 状态历史记录 ─────────────────────────────────────────
|
|
|
|
@Test void apply_shouldRecordHistory() {
|
|
mockStudentRole();
|
|
ApplicationCreateRequest req = new ApplicationCreateRequest(); req.setJobId(JOB_ID);
|
|
when(jobService.getJob(JOB_ID)).thenReturn(publishedJob);
|
|
when(applicationRepository.existsByJobIdAndStudentUserId(JOB_ID, STUDENT_ID)).thenReturn(false);
|
|
when(applicationRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
|
when(historyRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
|
|
|
applicationService.apply(STUDENT_ID, req);
|
|
verify(historyRepository, times(1)).save(any());
|
|
}
|
|
|
|
// ── 企业状态更新记录历史 ─────────────────────────────────
|
|
|
|
@Test void updateStatus_shouldRecordHistory() {
|
|
ApplicationStatusRequest req = new ApplicationStatusRequest(); req.setStatus("VIEWED");
|
|
when(applicationRepository.findById(APP_ID)).thenReturn(Optional.of(application));
|
|
when(jobService.getJob(JOB_ID)).thenReturn(publishedJob);
|
|
when(applicationRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
|
when(historyRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
|
|
|
applicationService.updateStatus(COMPANY_ID, APP_ID, req);
|
|
verify(historyRepository, times(1)).save(any());
|
|
}
|
|
|
|
// ── 原有测试兼容 ─────────────────────────────────────────
|
|
|
|
@Test void apply_shouldSucceed() {
|
|
mockStudentRole();
|
|
ApplicationCreateRequest req = new ApplicationCreateRequest(); req.setJobId(JOB_ID);
|
|
when(jobService.getJob(JOB_ID)).thenReturn(publishedJob);
|
|
when(applicationRepository.existsByJobIdAndStudentUserId(JOB_ID, STUDENT_ID)).thenReturn(false);
|
|
when(applicationRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
|
when(historyRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
|
|
|
Application result = applicationService.apply(STUDENT_ID, req);
|
|
assertEquals("SUBMITTED", result.getStatus());
|
|
}
|
|
|
|
// ── 企业候选人列表 ─────────────────────────────────────────
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private TypedQuery<Long> mockCountQuery(long total) {
|
|
TypedQuery<Long> q = mock(TypedQuery.class);
|
|
when(q.getSingleResult()).thenReturn(total);
|
|
return q;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private TypedQuery<Object[]> mockDataQuery(Object[]... rows) {
|
|
TypedQuery<Object[]> q = mock(TypedQuery.class);
|
|
when(q.getResultList()).thenReturn(List.of(rows));
|
|
return q;
|
|
}
|
|
|
|
private Object[] buildRow(Long appId, Long jobId, String jobTitle, String status,
|
|
String name, String school, String edu, Short gradYear,
|
|
String avatarUrl) {
|
|
return new Object[]{appId, jobId, jobTitle, status, name, school, edu, gradYear, avatarUrl,
|
|
java.time.LocalDateTime.now(), java.time.LocalDateTime.now()};
|
|
}
|
|
|
|
@Test void listCompanyCandidates_shouldReturnPaginatedResults() {
|
|
Object[] row = buildRow(200L, JOB_ID, "Java工程师", "SUBMITTED",
|
|
"张三", "清华大学", "本科", (short)2026, "https://img/1.png");
|
|
TypedQuery<Long> countQ = mockCountQuery(1L);
|
|
TypedQuery<Object[]> dataQ = mockDataQuery(row);
|
|
when(em.createQuery(anyString(), eq(Long.class))).thenReturn(countQ);
|
|
when(em.createQuery(anyString(), eq(Object[].class))).thenReturn(dataQ);
|
|
|
|
CandidateListResponse resp = applicationService.listCompanyCandidates(
|
|
COMPANY_ID, null, null, null, null, null, 0, 20);
|
|
assertEquals(1, resp.getItems().size());
|
|
assertEquals("张三", resp.getItems().get(0).getCandidateName());
|
|
assertEquals(1, resp.getTotal());
|
|
}
|
|
|
|
@Test void listCompanyCandidates_shouldFilterByJobId() {
|
|
Object[] row = buildRow(200L, JOB_ID, "Java", "SUBMITTED",
|
|
"张三", "清华", "本科", null, null);
|
|
TypedQuery<Long> countQ = mockCountQuery(1L);
|
|
TypedQuery<Object[]> dataQ = mockDataQuery(row);
|
|
when(em.createQuery(anyString(), eq(Long.class))).thenReturn(countQ);
|
|
when(em.createQuery(anyString(), eq(Object[].class))).thenReturn(dataQ);
|
|
|
|
CandidateListResponse resp = applicationService.listCompanyCandidates(
|
|
COMPANY_ID, JOB_ID, null, null, null, null, 0, 20);
|
|
assertEquals(1, resp.getItems().size());
|
|
assertEquals(JOB_ID, resp.getItems().get(0).getJobId());
|
|
}
|
|
|
|
@Test void listCompanyCandidates_shouldFilterByStatus() {
|
|
Object[] row = buildRow(200L, JOB_ID, "Java", "SUBMITTED",
|
|
"张三", "清华", "本科", null, null);
|
|
TypedQuery<Long> countQ = mockCountQuery(1L);
|
|
TypedQuery<Object[]> dataQ = mockDataQuery(row);
|
|
when(em.createQuery(anyString(), eq(Long.class))).thenReturn(countQ);
|
|
when(em.createQuery(anyString(), eq(Object[].class))).thenReturn(dataQ);
|
|
|
|
CandidateListResponse resp = applicationService.listCompanyCandidates(
|
|
COMPANY_ID, null, "SUBMITTED", null, null, null, 0, 20);
|
|
assertEquals("SUBMITTED", resp.getItems().get(0).getStatus());
|
|
}
|
|
|
|
@Test void listCompanyCandidates_shouldReturnEmptyForNoResults() {
|
|
TypedQuery<Long> countQ = mockCountQuery(0L);
|
|
TypedQuery<Object[]> dataQ = mockDataQuery();
|
|
when(em.createQuery(anyString(), eq(Long.class))).thenReturn(countQ);
|
|
when(em.createQuery(anyString(), eq(Object[].class))).thenReturn(dataQ);
|
|
|
|
CandidateListResponse resp = applicationService.listCompanyCandidates(
|
|
COMPANY_ID, null, null, null, null, null, 0, 20);
|
|
assertEquals(0, resp.getItems().size());
|
|
assertEquals(0, resp.getTotal());
|
|
}
|
|
|
|
@Test void listCompanyCandidates_invalidPage_shouldThrow() {
|
|
var ex = assertThrows(BusinessException.class, () ->
|
|
applicationService.listCompanyCandidates(COMPANY_ID, null, null, null, null, null, -1, 20));
|
|
assertEquals(ErrorCode.BAD_REQUEST.getCode(), ex.getErrorCodeInt());
|
|
}
|
|
|
|
@Test void listCompanyCandidates_invalidSize_shouldThrow() {
|
|
var ex = assertThrows(BusinessException.class, () ->
|
|
applicationService.listCompanyCandidates(COMPANY_ID, null, null, null, null, null, 0, 0));
|
|
assertEquals(ErrorCode.BAD_REQUEST.getCode(), ex.getErrorCodeInt());
|
|
}
|
|
|
|
@Test void listCompanyCandidates_sizeTooLarge_shouldThrow() {
|
|
var ex = assertThrows(BusinessException.class, () ->
|
|
applicationService.listCompanyCandidates(COMPANY_ID, null, null, null, null, null, 0, 101));
|
|
assertEquals(ErrorCode.BAD_REQUEST.getCode(), ex.getErrorCodeInt());
|
|
}
|
|
|
|
@Test void listCompanyCandidates_invalidStatus_shouldThrow() {
|
|
var ex = assertThrows(BusinessException.class, () ->
|
|
applicationService.listCompanyCandidates(COMPANY_ID, null, "INVALID", null, null, null, 0, 20));
|
|
assertEquals(ErrorCode.BAD_REQUEST.getCode(), ex.getErrorCodeInt());
|
|
}
|
|
|
|
@Test void listCompanyCandidates_invalidSort_shouldThrow() {
|
|
var ex = assertThrows(BusinessException.class, () ->
|
|
applicationService.listCompanyCandidates(COMPANY_ID, null, null, null, "invalid", null, 0, 20));
|
|
assertEquals(ErrorCode.BAD_REQUEST.getCode(), ex.getErrorCodeInt());
|
|
}
|
|
|
|
@Test void listCompanyCandidates_invalidOrder_shouldThrow() {
|
|
var ex = assertThrows(BusinessException.class, () ->
|
|
applicationService.listCompanyCandidates(COMPANY_ID, null, null, null, null, "invalid", 0, 20));
|
|
assertEquals(ErrorCode.BAD_REQUEST.getCode(), ex.getErrorCodeInt());
|
|
}
|
|
|
|
@Test void listCompanyCandidates_shouldSortByUpdatedAt() {
|
|
Object[] row = buildRow(200L, JOB_ID, "Java", "SUBMITTED",
|
|
"张三", "清华", "本科", null, null);
|
|
TypedQuery<Long> countQ = mockCountQuery(1L);
|
|
TypedQuery<Object[]> dataQ = mockDataQuery(row);
|
|
when(em.createQuery(anyString(), eq(Long.class))).thenReturn(countQ);
|
|
when(em.createQuery(anyString(), eq(Object[].class))).thenReturn(dataQ);
|
|
|
|
CandidateListResponse resp = applicationService.listCompanyCandidates(
|
|
COMPANY_ID, null, null, null, "updatedAt", "asc", 0, 20);
|
|
assertEquals(1, resp.getItems().size());
|
|
}
|
|
|
|
// ── 企业候选人详情 ─────────────────────────────────────────
|
|
|
|
@Test void getCompanyCandidateDetail_shouldReturnDetail() {
|
|
Application app = Application.builder().id(APP_ID).jobId(JOB_ID)
|
|
.studentUserId(STUDENT_ID).status("SUBMITTED").coverLetter("cover").build();
|
|
com.mydreamjob.job.Job job = com.mydreamjob.job.Job.builder()
|
|
.id(JOB_ID).companyId(COMPANY_ID).title("Java").city("北京")
|
|
.employmentType("全职").build();
|
|
StudentProfile sp = StudentProfile.builder().userId(STUDENT_ID)
|
|
.realName("张三").school("清华").education("本科").bio("bio")
|
|
.location("北京").graduationYear((short)2026).skillsJson("[\"Java\",\"Spring\"]").build();
|
|
com.mydreamjob.auth.User u = mock(com.mydreamjob.auth.User.class);
|
|
when(u.getAvatarUrl()).thenReturn("https://img/1.png");
|
|
|
|
Object[] row = new Object[]{app, job, sp, u};
|
|
@SuppressWarnings("unchecked")
|
|
TypedQuery<Object[]> detailQuery = mock(TypedQuery.class);
|
|
when(detailQuery.setParameter(anyString(), any())).thenReturn(detailQuery);
|
|
when(detailQuery.getResultList()).thenReturn(Collections.singletonList(row));
|
|
when(em.createQuery(anyString(), eq(Object[].class))).thenReturn(detailQuery);
|
|
|
|
CandidateDetailView detail = applicationService.getCompanyCandidateDetail(COMPANY_ID, APP_ID);
|
|
assertEquals("张三", detail.getCandidateName());
|
|
assertEquals("cover", detail.getCoverLetter());
|
|
assertEquals("北京", detail.getJobCity());
|
|
assertEquals(2, detail.getCandidateSkills().size());
|
|
assertEquals("https://img/1.png", detail.getCandidateAvatarUrl());
|
|
// 成功详情查询不产生额外 Repository 查询(无 N+1)
|
|
verify(applicationRepository, never()).findById(anyLong());
|
|
}
|
|
|
|
@Test void getCompanyCandidateDetail_wrongCompany_shouldThrow() {
|
|
// 投递存在,但岗位 companyId 与当前企业不匹配 → APPLICATION_OWNER_MISMATCH
|
|
Application app = Application.builder().id(APP_ID).jobId(JOB_ID)
|
|
.studentUserId(STUDENT_ID).status("SUBMITTED").build();
|
|
com.mydreamjob.job.Job job = com.mydreamjob.job.Job.builder()
|
|
.id(JOB_ID).companyId(COMPANY_ID).title("Java").build();
|
|
Object[] row = new Object[]{app, job, null, null};
|
|
@SuppressWarnings("unchecked")
|
|
TypedQuery<Object[]> detailQuery = mock(TypedQuery.class);
|
|
when(detailQuery.setParameter(anyString(), any())).thenReturn(detailQuery);
|
|
when(detailQuery.getResultList()).thenReturn(Collections.singletonList(row));
|
|
when(em.createQuery(anyString(), eq(Object[].class))).thenReturn(detailQuery);
|
|
|
|
var ex = assertThrows(BusinessException.class, () ->
|
|
applicationService.getCompanyCandidateDetail(999L, APP_ID));
|
|
assertEquals(ErrorCode.APPLICATION_OWNER_MISMATCH.getCode(), ex.getErrorCodeInt());
|
|
// 越权分支不触发额外的 Repository 查询
|
|
verify(applicationRepository, never()).findById(anyLong());
|
|
}
|
|
|
|
@Test void getCompanyCandidateDetail_appNotFound_shouldThrow() {
|
|
@SuppressWarnings("unchecked")
|
|
TypedQuery<Object[]> detailQuery = mock(TypedQuery.class);
|
|
when(detailQuery.setParameter(anyString(), any())).thenReturn(detailQuery);
|
|
when(detailQuery.getResultList()).thenReturn(Collections.emptyList());
|
|
when(em.createQuery(anyString(), eq(Object[].class))).thenReturn(detailQuery);
|
|
|
|
var ex = assertThrows(BusinessException.class, () ->
|
|
applicationService.getCompanyCandidateDetail(COMPANY_ID, APP_ID));
|
|
assertEquals(ErrorCode.APPLICATION_NOT_FOUND.getCode(), ex.getErrorCodeInt());
|
|
// 不存在分支不触发额外的 Repository 查询
|
|
verify(applicationRepository, never()).findById(anyLong());
|
|
}
|
|
|
|
@Test void getCompanyCandidateDetail_noStudentProfile_shouldReturnNullFields() {
|
|
Application app = Application.builder().id(APP_ID).jobId(JOB_ID)
|
|
.studentUserId(STUDENT_ID).status("SUBMITTED").coverLetter(null).build();
|
|
com.mydreamjob.job.Job job = com.mydreamjob.job.Job.builder()
|
|
.id(JOB_ID).companyId(COMPANY_ID).title("Java").build();
|
|
|
|
Object[] row = new Object[]{app, job, null, null};
|
|
@SuppressWarnings("unchecked")
|
|
TypedQuery<Object[]> detailQuery = mock(TypedQuery.class);
|
|
when(detailQuery.setParameter(anyString(), any())).thenReturn(detailQuery);
|
|
when(detailQuery.getResultList()).thenReturn(Collections.singletonList(row));
|
|
when(em.createQuery(anyString(), eq(Object[].class))).thenReturn(detailQuery);
|
|
|
|
CandidateDetailView detail = applicationService.getCompanyCandidateDetail(COMPANY_ID, APP_ID);
|
|
assertNull(detail.getCandidateName());
|
|
assertNull(detail.getCandidateSchool());
|
|
assertNull(detail.getCoverLetter());
|
|
}
|
|
|
|
// ── getTimeline 已知行为回归 ────────────────────────────────
|
|
|
|
@Test void getTimeline_companyOwner_canReadWhenVerified() {
|
|
Long companyUserId = 300L;
|
|
com.mydreamjob.auth.User companyUser = com.mydreamjob.auth.User.builder().id(companyUserId).phone("13800000300")
|
|
.role(com.mydreamjob.auth.User.Role.COMPANY).active(true).verified(true).build();
|
|
companyUser.setRoles(new HashSet<>(Set.of(com.mydreamjob.auth.User.Role.COMPANY)));
|
|
when(userRepository.findByIdWithRoles(companyUserId)).thenReturn(Optional.of(companyUser));
|
|
when(companyRepository.findByOwnerUserId(companyUserId)).thenReturn(Optional.of(
|
|
com.mydreamjob.company.Company.builder().id(COMPANY_ID).ownerUserId(companyUserId).verified(true).build()));
|
|
when(applicationRepository.findById(APP_ID)).thenReturn(Optional.of(application));
|
|
when(jobService.getJob(JOB_ID)).thenReturn(publishedJob);
|
|
when(historyRepository.findByApplicationIdOrderByCreatedAtAsc(APP_ID)).thenReturn(List.of());
|
|
|
|
var result = applicationService.getTimeline(APP_ID, companyUserId);
|
|
assertNotNull(result);
|
|
}
|
|
|
|
// ── LIKE 转义 ──────────────────────────────────────────────
|
|
|
|
@Test void escapeLike_shouldEscapeSpecialChars() {
|
|
assertEquals("test\\_key\\%word", applicationService.escapeLike("test_key%word"));
|
|
assertEquals("test\\\\slash", applicationService.escapeLike("test\\slash"));
|
|
}
|
|
|
|
@Test void escapeLike_null_shouldReturnNull() {
|
|
assertNull(applicationService.escapeLike(null));
|
|
}
|
|
}
|
|
|