package com.mydreamjob.feedback; import com.mydreamjob.application.Application; import com.mydreamjob.application.ApplicationRepository; import com.mydreamjob.common.exception.BusinessException; import com.mydreamjob.common.exception.ErrorCode; import com.mydreamjob.company.Company; import com.mydreamjob.company.CompanyRepository; import com.mydreamjob.feedback.dto.CandidateFeedbackRequest; import com.mydreamjob.job.Job; import com.mydreamjob.job.JobService; 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 java.util.Optional; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class CandidateFeedbackServiceTest { @Mock private CandidateFeedbackRepository feedbackRepository; @Mock private ApplicationRepository applicationRepository; @Mock private JobService jobService; @Mock private CompanyRepository companyRepository; @InjectMocks private CandidateFeedbackService feedbackService; private static final Long COMPANY_USER_ID = 10L; // 企业账号 userId private static final Long COMPANY_ID = 20L; // 企业 id(job.companyId) private static final Long OTHER_COMPANY_USER_ID = 11L; // 其他企业账号 userId private static final Long OTHER_COMPANY_ID = 21L; // 其他企业 id private static final Long ADMIN_LIKE_USER_ID = 1L; // 无归属企业、且恰等于某企业 id private static final Long STUDENT_USER_ID = 100L; private static final Long OTHER_STUDENT_ID = 101L; private static final Long APPLICATION_ID = 1L; private static final Long JOB_ID = 50L; private Application app; private Job job; @BeforeEach void setUp() { app = Application.builder().id(APPLICATION_ID).jobId(JOB_ID).studentUserId(STUDENT_USER_ID).build(); job = Job.builder().id(JOB_ID).companyId(COMPANY_ID).title("Java").build(); } private void mockCompanyOwnership(Long userId, Long companyId) { when(companyRepository.findByOwnerUserId(userId)) .thenReturn(Optional.of(Company.builder().id(companyId).ownerUserId(userId).build())); } private CandidateFeedbackRequest buildReq() { CandidateFeedbackRequest req = new CandidateFeedbackRequest(); req.setRating(4); req.setContent("Good candidate"); return req; } private void stubAppAndJob() { when(applicationRepository.findById(APPLICATION_ID)).thenReturn(Optional.of(app)); when(jobService.getJob(JOB_ID)).thenReturn(job); } // ── 创建 ───────────────────────────────────────────────── @Test void createFeedback_companyOwnsJob_shouldSucceed() { stubAppAndJob(); mockCompanyOwnership(COMPANY_USER_ID, COMPANY_ID); when(feedbackRepository.existsByApplicationId(APPLICATION_ID)).thenReturn(false); when(feedbackRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); var r = feedbackService.createFeedback(COMPANY_USER_ID, APPLICATION_ID, buildReq()); assertEquals(4, r.getRating()); } @Test void createFeedback_duplicate_shouldReturn409() { stubAppAndJob(); mockCompanyOwnership(COMPANY_USER_ID, COMPANY_ID); when(feedbackRepository.existsByApplicationId(APPLICATION_ID)).thenReturn(true); BusinessException ex = assertThrows(BusinessException.class, () -> feedbackService.createFeedback(COMPANY_USER_ID, APPLICATION_ID, buildReq())); assertEquals(ErrorCode.DUPLICATE_FEEDBACK.getCode(), ex.getErrorCodeInt()); } @Test void createFeedback_otherCompany_shouldReturn403() { stubAppAndJob(); mockCompanyOwnership(OTHER_COMPANY_USER_ID, OTHER_COMPANY_ID); BusinessException ex = assertThrows(BusinessException.class, () -> feedbackService.createFeedback(OTHER_COMPANY_USER_ID, APPLICATION_ID, buildReq())); assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt()); } @Test void createFeedback_adminUserIdEqualsCompanyId_shouldReturn403() { // userId 恰等于 job.companyId,但该用户无归属企业 -> 必须 403(安全回归) Job adminJob = Job.builder().id(JOB_ID).companyId(ADMIN_LIKE_USER_ID).title("Java").build(); when(applicationRepository.findById(APPLICATION_ID)).thenReturn(Optional.of(app)); when(jobService.getJob(JOB_ID)).thenReturn(adminJob); // 不 stub companyRepository:默认空 -> 无归属企业 BusinessException ex = assertThrows(BusinessException.class, () -> feedbackService.createFeedback(ADMIN_LIKE_USER_ID, APPLICATION_ID, buildReq())); assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt()); } @Test void createFeedback_byStudent_shouldReturn403() { stubAppAndJob(); // 学生无归属企业 BusinessException ex = assertThrows(BusinessException.class, () -> feedbackService.createFeedback(STUDENT_USER_ID, APPLICATION_ID, buildReq())); assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt()); } @Test void createFeedback_appNotFound_shouldReturn404() { when(applicationRepository.findById(999L)).thenReturn(Optional.empty()); BusinessException ex = assertThrows(BusinessException.class, () -> feedbackService.createFeedback(COMPANY_USER_ID, 999L, buildReq())); assertEquals(ErrorCode.APPLICATION_NOT_FOUND.getCode(), ex.getErrorCodeInt()); } // ── 查询 ───────────────────────────────────────────────── @Test void getFeedback_asCompany_shouldSucceed() { stubAppAndJob(); mockCompanyOwnership(COMPANY_USER_ID, COMPANY_ID); CandidateFeedback fb = CandidateFeedback.builder().applicationId(APPLICATION_ID).rating(4).content("Good").build(); when(feedbackRepository.findByApplicationId(APPLICATION_ID)).thenReturn(Optional.of(fb)); var r = feedbackService.getFeedback(COMPANY_USER_ID, APPLICATION_ID); assertEquals(4, r.getRating()); } @Test void getFeedback_asStudent_shouldSucceed() { stubAppAndJob(); CandidateFeedback fb = CandidateFeedback.builder().applicationId(APPLICATION_ID).rating(4).content("Good").build(); when(feedbackRepository.findByApplicationId(APPLICATION_ID)).thenReturn(Optional.of(fb)); var r = feedbackService.getFeedback(STUDENT_USER_ID, APPLICATION_ID); assertEquals(4, r.getRating()); } @Test void getFeedback_adminUserIdEqualsCompanyId_shouldReturn403() { Job adminJob = Job.builder().id(JOB_ID).companyId(ADMIN_LIKE_USER_ID).title("Java").build(); when(applicationRepository.findById(APPLICATION_ID)).thenReturn(Optional.of(app)); when(jobService.getJob(JOB_ID)).thenReturn(adminJob); BusinessException ex = assertThrows(BusinessException.class, () -> feedbackService.getFeedback(ADMIN_LIKE_USER_ID, APPLICATION_ID)); assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt()); } @Test void getFeedback_otherStudent_shouldReturn403() { stubAppAndJob(); BusinessException ex = assertThrows(BusinessException.class, () -> feedbackService.getFeedback(OTHER_STUDENT_ID, APPLICATION_ID)); assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt()); } // ── 修改 ───────────────────────────────────────────────── @Test void updateFeedback_shouldSucceed() { stubAppAndJob(); mockCompanyOwnership(COMPANY_USER_ID, COMPANY_ID); CandidateFeedback fb = CandidateFeedback.builder().applicationId(APPLICATION_ID).rating(4).content("Good").build(); when(feedbackRepository.findByApplicationId(APPLICATION_ID)).thenReturn(Optional.of(fb)); when(feedbackRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); CandidateFeedbackRequest req = buildReq(); req.setRating(5); var r = feedbackService.updateFeedback(COMPANY_USER_ID, APPLICATION_ID, req); assertEquals(5, r.getRating()); } @Test void updateFeedback_otherCompany_shouldReturn403() { stubAppAndJob(); mockCompanyOwnership(OTHER_COMPANY_USER_ID, OTHER_COMPANY_ID); BusinessException ex = assertThrows(BusinessException.class, () -> feedbackService.updateFeedback(OTHER_COMPANY_USER_ID, APPLICATION_ID, buildReq())); assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt()); } @Test void updateFeedback_byStudent_shouldReturn403() { stubAppAndJob(); BusinessException ex = assertThrows(BusinessException.class, () -> feedbackService.updateFeedback(STUDENT_USER_ID, APPLICATION_ID, buildReq())); assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt()); } // ── 删除 ───────────────────────────────────────────────── @Test void deleteFeedback_shouldSucceed() { stubAppAndJob(); mockCompanyOwnership(COMPANY_USER_ID, COMPANY_ID); CandidateFeedback fb = CandidateFeedback.builder().applicationId(APPLICATION_ID).build(); when(feedbackRepository.findByApplicationId(APPLICATION_ID)).thenReturn(Optional.of(fb)); assertDoesNotThrow(() -> feedbackService.deleteFeedback(COMPANY_USER_ID, APPLICATION_ID)); verify(feedbackRepository).delete(fb); } @Test void deleteFeedback_otherCompany_shouldReturn403() { stubAppAndJob(); mockCompanyOwnership(OTHER_COMPANY_USER_ID, OTHER_COMPANY_ID); BusinessException ex = assertThrows(BusinessException.class, () -> feedbackService.deleteFeedback(OTHER_COMPANY_USER_ID, APPLICATION_ID)); assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt()); } @Test void deleteFeedback_byStudent_shouldReturn403() { stubAppAndJob(); BusinessException ex = assertThrows(BusinessException.class, () -> feedbackService.deleteFeedback(STUDENT_USER_ID, APPLICATION_ID)); assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt()); } }