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.
 
 
 

467 lines
24 KiB

package com.mydreamjob.review;
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.auth.User;
import com.mydreamjob.auth.UserRepository;
import com.mydreamjob.job.Job;
import com.mydreamjob.job.JobRepository;
import com.mydreamjob.job.JobService;
import com.mydreamjob.review.dto.AdminCompanyReviewReplyResponse;
import com.mydreamjob.review.dto.CompanyReviewReplyCreateRequest;
import com.mydreamjob.review.dto.CompanyReviewReplyUpdateRequest;
import com.mydreamjob.review.dto.ReviewReplyStatsResponse;
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.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class CompanyReviewReplyServiceTest {
@Mock private CompanyReviewReplyRepository replyRepository;
@Mock private JobReviewRepository reviewRepository;
@Mock private JobRepository jobRepository;
@Mock private JobService jobService;
@Mock private CompanyRepository companyRepository;
@Mock private UserRepository userRepository;
@InjectMocks private CompanyReviewReplyService replyService;
private static final Long COMPANY_USER_ID = 10L; // 企业账号 userId
private static final Long COMPANY_ID = 20L; // 企业 id
private static final Long OTHER_COMPANY_USER_ID = 11L; // 其他企业账号 userId
private static final Long OTHER_COMPANY_ID = 21L; // 其他企业 id
private static final Long REVIEW_ID = 100L;
private static final Long JOB_ID = 50L;
private static final Long ADMIN_USER_ID = 1L;
@BeforeEach
void setUp() {
// 默认:adminId 为 ADMIN(lenient 避免未用到的用例报 UnnecessaryStubbing)
lenient().when(userRepository.findById(ADMIN_USER_ID))
.thenReturn(Optional.of(adminUser()));
}
private User adminUser() {
return User.builder().id(ADMIN_USER_ID).role(User.Role.ADMIN)
.roles(Set.of(User.Role.ADMIN)).build();
}
private User normalUser(Long id) {
return User.builder().id(id).role(User.Role.COMPANY)
.roles(Set.of(User.Role.COMPANY)).build();
}
private JobReview approvedReview() {
return JobReview.builder()
.id(REVIEW_ID).jobId(JOB_ID).studentUserId(999L)
.rating(4).content("Great job")
.moderationStatus(JobReview.MODERATION_AUTO_APPROVED)
.build();
}
private void mockCompanyOwnership(Long userId, Long companyId) {
when(companyRepository.findByOwnerUserId(userId))
.thenReturn(Optional.of(Company.builder().id(companyId).ownerUserId(userId).build()));
}
private void stubJobWithCompany(Long companyId) {
when(jobService.getJob(JOB_ID)).thenReturn(Job.builder().id(JOB_ID).companyId(companyId).title("Java").build());
}
private CompanyReviewReplyCreateRequest buildCreateReq() {
CompanyReviewReplyCreateRequest req = new CompanyReviewReplyCreateRequest();
req.setContent("感谢您的反馈,我们会持续改进");
return req;
}
// ── 创建(企业归属校验是核心)──────────────────────────────
@Test void createReply_companyOwnsJob_shouldSucceed() {
when(reviewRepository.findById(REVIEW_ID)).thenReturn(Optional.of(approvedReview()));
stubJobWithCompany(COMPANY_ID);
mockCompanyOwnership(COMPANY_USER_ID, COMPANY_ID);
when(replyRepository.existsByReviewId(REVIEW_ID)).thenReturn(false);
when(replyRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
var r = replyService.createReply(COMPANY_USER_ID, REVIEW_ID, buildCreateReq());
assertEquals(CompanyReviewReply.MODERATION_PENDING, r.getModerationStatus());
assertEquals("感谢您的反馈,我们会持续改进", r.getContent());
verify(replyRepository).save(any());
}
@Test void createReply_otherCompany_shouldReturn403() {
when(reviewRepository.findById(REVIEW_ID)).thenReturn(Optional.of(approvedReview()));
stubJobWithCompany(COMPANY_ID);
mockCompanyOwnership(OTHER_COMPANY_USER_ID, OTHER_COMPANY_ID);
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.createReply(OTHER_COMPANY_USER_ID, REVIEW_ID, buildCreateReq()));
assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt());
}
@Test void createReply_reviewNotApproved_shouldReturn404() {
JobReview pending = approvedReview();
pending.setModerationStatus(JobReview.MODERATION_PENDING);
when(reviewRepository.findById(REVIEW_ID)).thenReturn(Optional.of(pending));
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.createReply(COMPANY_USER_ID, REVIEW_ID, buildCreateReq()));
assertEquals(ErrorCode.REVIEW_NOT_FOUND.getCode(), ex.getErrorCodeInt());
}
@Test void createReply_duplicate_shouldReturn409() {
when(reviewRepository.findById(REVIEW_ID)).thenReturn(Optional.of(approvedReview()));
stubJobWithCompany(COMPANY_ID);
mockCompanyOwnership(COMPANY_USER_ID, COMPANY_ID);
when(replyRepository.existsByReviewId(REVIEW_ID)).thenReturn(true);
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.createReply(COMPANY_USER_ID, REVIEW_ID, buildCreateReq()));
assertEquals(ErrorCode.DUPLICATE_REPLY.getCode(), ex.getErrorCodeInt());
}
@Test void createReply_companyUserWithoutOwnedCompany_shouldReturn403() {
when(reviewRepository.findById(REVIEW_ID)).thenReturn(Optional.of(approvedReview()));
stubJobWithCompany(COMPANY_ID);
// companyRepository 不 stub -> 默认空 -> 无归属企业
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.createReply(COMPANY_USER_ID, REVIEW_ID, buildCreateReq()));
assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt());
}
// ── 查询 ─────────────────────────────────────────────────
@Test void getReply_approved_shouldSucceed() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(1L).reviewId(REVIEW_ID).companyUserId(COMPANY_USER_ID)
.content("回复内容").moderationStatus(CompanyReviewReply.MODERATION_APPROVED)
.createdAt(LocalDateTime.now()).build();
when(replyRepository.findByReviewId(REVIEW_ID)).thenReturn(Optional.of(reply));
var r = replyService.getReply(null, REVIEW_ID);
assertEquals("回复内容", r.getContent());
}
@Test void getReply_pending_otherUser_shouldReturn404() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(1L).reviewId(REVIEW_ID).content("回复内容")
.moderationStatus(CompanyReviewReply.MODERATION_PENDING).build();
when(replyRepository.findByReviewId(REVIEW_ID)).thenReturn(Optional.of(reply));
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.getReply(999L, REVIEW_ID));
assertEquals(ErrorCode.REPLY_NOT_FOUND.getCode(), ex.getErrorCodeInt());
}
@Test void getReply_pending_anonymous_shouldReturn404() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(1L).reviewId(REVIEW_ID).companyUserId(COMPANY_USER_ID).content("回复内容")
.moderationStatus(CompanyReviewReply.MODERATION_PENDING).build();
when(replyRepository.findByReviewId(REVIEW_ID)).thenReturn(Optional.of(reply));
// 匿名请求:currentUserIdOrNull() 返回 null(原为把匿名 token 当 ADMIN 解析导致 500)
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.getReply(null, REVIEW_ID));
assertEquals(ErrorCode.REPLY_NOT_FOUND.getCode(), ex.getErrorCodeInt());
}
@Test void getReply_pending_owner_shouldSucceedWithPending() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(1L).reviewId(REVIEW_ID).companyUserId(COMPANY_USER_ID)
.content("回复内容").moderationStatus(CompanyReviewReply.MODERATION_PENDING).build();
when(replyRepository.findByReviewId(REVIEW_ID)).thenReturn(Optional.of(reply));
var r = replyService.getReply(COMPANY_USER_ID, REVIEW_ID);
assertEquals(CompanyReviewReply.MODERATION_PENDING, r.getModerationStatus());
}
@Test void getReply_rejected_owner_shouldSucceedWithReason() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(1L).reviewId(REVIEW_ID).companyUserId(COMPANY_USER_ID)
.content("回复内容").moderationStatus(CompanyReviewReply.MODERATION_REJECTED)
.moderationReason("内容违规").build();
when(replyRepository.findByReviewId(REVIEW_ID)).thenReturn(Optional.of(reply));
var r = replyService.getReply(COMPANY_USER_ID, REVIEW_ID);
assertEquals(CompanyReviewReply.MODERATION_REJECTED, r.getModerationStatus());
assertEquals("内容违规", r.getModerationReason());
}
@Test void batchGetApprovedReplies_shouldFilterApproved() {
CompanyReviewReply approved = CompanyReviewReply.builder()
.id(1L).reviewId(REVIEW_ID).content("OK")
.moderationStatus(CompanyReviewReply.MODERATION_APPROVED).build();
CompanyReviewReply pending = CompanyReviewReply.builder()
.id(2L).reviewId(101L).content("P")
.moderationStatus(CompanyReviewReply.MODERATION_PENDING).build();
// Service 使用 findByReviewIdInAndModerationStatus(ids, APPROVED),只返回 approved
when(replyRepository.findByReviewIdInAndModerationStatus(anyList(), eq(CompanyReviewReply.MODERATION_APPROVED)))
.thenReturn(java.util.List.of(approved));
var map = replyService.batchGetApprovedReplies(java.util.List.of(REVIEW_ID, 101L));
assertEquals(1, map.size());
assertTrue(map.containsKey(REVIEW_ID));
assertFalse(map.containsKey(101L));
}
// ── 更新(企业侧)─────────────────────────────────────────
@Test void updateReply_companyOwns_shouldResetToPending() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(1L).reviewId(REVIEW_ID).companyUserId(COMPANY_USER_ID)
.content("旧内容").moderationStatus(CompanyReviewReply.MODERATION_APPROVED).build();
when(replyRepository.findByReviewId(REVIEW_ID)).thenReturn(Optional.of(reply));
when(reviewRepository.findById(REVIEW_ID)).thenReturn(Optional.of(approvedReview()));
stubJobWithCompany(COMPANY_ID);
mockCompanyOwnership(COMPANY_USER_ID, COMPANY_ID);
when(replyRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
CompanyReviewReplyUpdateRequest req = new CompanyReviewReplyUpdateRequest();
req.setContent("新内容");
var r = replyService.updateReply(COMPANY_USER_ID, REVIEW_ID, req);
assertEquals("新内容", r.getContent());
assertEquals(CompanyReviewReply.MODERATION_PENDING, r.getModerationStatus());
}
@Test void updateReply_otherCompany_shouldReturn403() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(1L).reviewId(REVIEW_ID).content("旧内容").build();
when(replyRepository.findByReviewId(REVIEW_ID)).thenReturn(Optional.of(reply));
when(reviewRepository.findById(REVIEW_ID)).thenReturn(Optional.of(approvedReview()));
stubJobWithCompany(COMPANY_ID);
mockCompanyOwnership(OTHER_COMPANY_USER_ID, OTHER_COMPANY_ID);
CompanyReviewReplyUpdateRequest req = new CompanyReviewReplyUpdateRequest();
req.setContent("新内容");
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.updateReply(OTHER_COMPANY_USER_ID, REVIEW_ID, req));
assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt());
}
// ── 删除(企业侧)─────────────────────────────────────────
@Test void deleteReply_companyOwns_shouldSucceed() {
CompanyReviewReply reply = CompanyReviewReply.builder().id(1L).reviewId(REVIEW_ID).build();
when(replyRepository.findByReviewId(REVIEW_ID)).thenReturn(Optional.of(reply));
when(reviewRepository.findById(REVIEW_ID)).thenReturn(Optional.of(approvedReview()));
stubJobWithCompany(COMPANY_ID);
mockCompanyOwnership(COMPANY_USER_ID, COMPANY_ID);
assertDoesNotThrow(() -> replyService.deleteReply(COMPANY_USER_ID, REVIEW_ID));
verify(replyRepository).delete(reply);
}
@Test void deleteReply_otherCompany_shouldReturn403() {
CompanyReviewReply reply = CompanyReviewReply.builder().id(1L).reviewId(REVIEW_ID).build();
when(replyRepository.findByReviewId(REVIEW_ID)).thenReturn(Optional.of(reply));
when(reviewRepository.findById(REVIEW_ID)).thenReturn(Optional.of(approvedReview()));
stubJobWithCompany(COMPANY_ID);
mockCompanyOwnership(OTHER_COMPANY_USER_ID, OTHER_COMPANY_ID);
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.deleteReply(OTHER_COMPANY_USER_ID, REVIEW_ID));
assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt());
}
// ── 管理员审核 ────────────────────────────────────────────
@Test void moderateReply_approve_shouldSucceed() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(1L).reviewId(REVIEW_ID).content("内容")
.moderationStatus(CompanyReviewReply.MODERATION_PENDING).build();
when(replyRepository.findById(1L)).thenReturn(Optional.of(reply));
when(replyRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
var r = replyService.moderateReply(ADMIN_ROLE_ID(), 1L, CompanyReviewReply.MODERATION_APPROVED, "合规");
assertEquals(CompanyReviewReply.MODERATION_APPROVED, r.getModerationStatus());
}
@Test void moderateReply_reject_shouldSucceed() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(1L).reviewId(REVIEW_ID).content("内容")
.moderationStatus(CompanyReviewReply.MODERATION_PENDING).build();
when(replyRepository.findById(1L)).thenReturn(Optional.of(reply));
when(replyRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
var r = replyService.moderateReply(ADMIN_ROLE_ID(), 1L, CompanyReviewReply.MODERATION_REJECTED, "违规");
assertEquals(CompanyReviewReply.MODERATION_REJECTED, r.getModerationStatus());
}
@Test void moderateReply_alreadyDecided_shouldReturn409() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(1L).reviewId(REVIEW_ID).content("内容")
.moderationStatus(CompanyReviewReply.MODERATION_APPROVED).build();
when(replyRepository.findById(1L)).thenReturn(Optional.of(reply));
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.moderateReply(ADMIN_ROLE_ID(), 1L, CompanyReviewReply.MODERATION_APPROVED, "x"));
assertEquals(ErrorCode.REPLY_ALREADY_DECIDED.getCode(), ex.getErrorCodeInt());
}
@Test void moderateReply_nonAdmin_shouldReturnForbidden() {
Long nonAdminId = OTHER_COMPANY_USER_ID;
when(userRepository.findById(nonAdminId)).thenReturn(Optional.of(normalUser(nonAdminId)));
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.moderateReply(nonAdminId, 1L, CompanyReviewReply.MODERATION_APPROVED, "x"));
assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt());
}
// ── 管理端回复列表(仅 ADMIN) ──────────────────────────────
@Test void listAdminReplies_nonAdmin_shouldReturnForbidden() {
Long nonAdminId = OTHER_COMPANY_USER_ID;
when(userRepository.findById(nonAdminId)).thenReturn(Optional.of(normalUser(nonAdminId)));
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.listAdminReplies(nonAdminId, null, null, null, null, null, 0, 20));
assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt());
}
@Test void listAdminReplies_invalidStatus_shouldReturnBadRequest() {
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.listAdminReplies(ADMIN_USER_ID, null, null, "UNKNOWN", null, null, 0, 20));
assertEquals(ErrorCode.BAD_REQUEST.getCode(), ex.getErrorCodeInt());
}
@Test void listAdminReplies_invalidPage_shouldReturnBadRequest() {
assertThrows(BusinessException.class,
() -> replyService.listAdminReplies(ADMIN_USER_ID, null, null, null, null, null, -1, 20));
assertThrows(BusinessException.class,
() -> replyService.listAdminReplies(ADMIN_USER_ID, null, null, null, null, null, 0, 0));
assertThrows(BusinessException.class,
() -> replyService.listAdminReplies(ADMIN_USER_ID, null, null, null, null, null, 0, 101));
}
@Test void listAdminReplies_defaultStatusPending_andDelegatesFilters() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(3L).reviewId(REVIEW_ID).companyUserId(COMPANY_USER_ID)
.content("回复").moderationStatus(CompanyReviewReply.MODERATION_PENDING).build();
when(replyRepository.findAdminReplies(any(), any(), any(), any(), any(), any()))
.thenReturn(new PageImpl<>(List.of(reply)));
when(reviewRepository.findAllById(anyCollection())).thenReturn(List.of());
Page<AdminCompanyReviewReplyResponse> result =
replyService.listAdminReplies(ADMIN_USER_ID, COMPANY_ID, JOB_ID, null, null, null, 0, 20);
assertEquals(1, result.getTotalElements());
assertEquals(CompanyReviewReply.MODERATION_PENDING, result.getContent().get(0).getModerationStatus());
verify(replyRepository).findAdminReplies(eq(COMPANY_ID), eq(JOB_ID),
eq(CompanyReviewReply.MODERATION_PENDING), any(), any(), any());
}
@Test void listAdminReplies_enrichesJobAndCompany() {
CompanyReviewReply reply = CompanyReviewReply.builder()
.id(3L).reviewId(REVIEW_ID).companyUserId(COMPANY_USER_ID)
.content("回复").moderationStatus(CompanyReviewReply.MODERATION_PENDING).build();
JobReview review = JobReview.builder().id(REVIEW_ID).jobId(JOB_ID)
.studentUserId(2L).rating(3).content("评价内容").build();
Job job = Job.builder().id(JOB_ID).companyId(COMPANY_ID).title("Java 后端工程师").build();
Company company = Company.builder().id(COMPANY_ID).name("本源科技").build();
when(replyRepository.findAdminReplies(any(), any(), any(), any(), any(), any()))
.thenReturn(new PageImpl<>(List.of(reply)));
when(reviewRepository.findAllById(anyCollection())).thenReturn(List.of(review));
when(jobRepository.findAllById(anyCollection())).thenReturn(List.of(job));
when(companyRepository.findAllById(anyCollection())).thenReturn(List.of(company));
Page<AdminCompanyReviewReplyResponse> result =
replyService.listAdminReplies(ADMIN_USER_ID, null, null, null, null, null, 0, 20);
AdminCompanyReviewReplyResponse v = result.getContent().get(0);
assertEquals("Java 后端工程师", v.getJobTitle());
assertEquals("本源科技", v.getCompanyName());
assertEquals(3, v.getReviewRating());
assertEquals(2L, v.getStudentUserId());
assertEquals(COMPANY_USER_ID, v.getCompanyUserId());
}
// ── 回复率统计(企业侧) ──────────────────────────────────
@Test void getReviewReplyStats_shouldComputeMonthRateAndIndustryAvg() {
when(companyRepository.findByOwnerUserId(COMPANY_USER_ID))
.thenReturn(Optional.of(Company.builder().id(COMPANY_ID)
.ownerUserId(COMPANY_USER_ID).industry("IT").build()));
when(replyRepository.findMonthlyReplyStats(any(), any(), any(), any())).thenReturn(List.of(
new Object[]{COMPANY_ID, 5L, 4L}, // 当前企业:80%
new Object[]{OTHER_COMPANY_ID, 4L, 2L})); // 同行企业:50%
ReviewReplyStatsResponse r = replyService.getReviewReplyStats(COMPANY_USER_ID);
assertEquals(80, r.getMonthReplyRate());
assertEquals(65, r.getIndustryAvgReplyRate()); // (0.8+0.5)/2 = 0.65
assertEquals(1, r.getUnrepliedCount());
}
@Test void getReviewReplyStats_noReplies_shouldReturnZeroRate() {
when(companyRepository.findByOwnerUserId(COMPANY_USER_ID))
.thenReturn(Optional.of(Company.builder().id(COMPANY_ID)
.ownerUserId(COMPANY_USER_ID).industry("IT").build()));
when(replyRepository.findMonthlyReplyStats(any(), any(), any(), any()))
.thenReturn(List.<Object[]>of(new Object[]{COMPANY_ID, 2L, 0L}));
ReviewReplyStatsResponse r = replyService.getReviewReplyStats(COMPANY_USER_ID);
assertEquals(0, r.getMonthReplyRate());
assertEquals(2, r.getUnrepliedCount());
}
@Test void getReviewReplyStats_noReviewsThisMonth_shouldReturnZeros() {
when(companyRepository.findByOwnerUserId(COMPANY_USER_ID))
.thenReturn(Optional.of(Company.builder().id(COMPANY_ID)
.ownerUserId(COMPANY_USER_ID).industry("IT").build()));
when(replyRepository.findMonthlyReplyStats(any(), any(), any(), any())).thenReturn(List.of());
ReviewReplyStatsResponse r = replyService.getReviewReplyStats(COMPANY_USER_ID);
assertEquals(0, r.getMonthReplyRate());
assertEquals(0, r.getIndustryAvgReplyRate());
assertEquals(0, r.getUnrepliedCount());
}
@Test void getReviewReplyStats_companyWithoutIndustry_shouldSetIndustryAvgZero() {
when(companyRepository.findByOwnerUserId(COMPANY_USER_ID))
.thenReturn(Optional.of(Company.builder().id(COMPANY_ID)
.ownerUserId(COMPANY_USER_ID).industry(null).build()));
when(replyRepository.findMonthlyReplyStats(any(), any(), any(), any()))
.thenReturn(List.<Object[]>of(new Object[]{COMPANY_ID, 5L, 4L}));
ReviewReplyStatsResponse r = replyService.getReviewReplyStats(COMPANY_USER_ID);
assertEquals(80, r.getMonthReplyRate());
assertEquals(0, r.getIndustryAvgReplyRate());
assertEquals(1, r.getUnrepliedCount());
}
@Test void getReviewReplyStats_companyUserWithoutCompany_shouldReturn403() {
when(companyRepository.findByOwnerUserId(COMPANY_USER_ID)).thenReturn(Optional.empty());
BusinessException ex = assertThrows(BusinessException.class,
() -> replyService.getReviewReplyStats(COMPANY_USER_ID));
assertEquals(ErrorCode.FORBIDDEN.getCode(), ex.getErrorCodeInt());
}
private Long ADMIN_ROLE_ID() {
return 1L;
}
}