Skip to content

Commit d5f3281

Browse files
authored
Merge pull request #152 from Goorm-Synergy/test/#151/member-point-controller-테스트
test: member, point controller 테스트
2 parents 508cd66 + bf15c6b commit d5f3281

8 files changed

Lines changed: 670 additions & 137 deletions

File tree

src/main/java/com/synergy/backend/domain/member/api/dto/request/SignupAttendeeRequestDto.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public record SignupAttendeeRequestDto(
2323

2424
@NotBlank(message = "비밀번호는 필수 입력 값입니다.")
2525
@Size(min = 8, max = 20, message = "비밀번호는 8~20자 이내여야 합니다.")
26-
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,20}$",
26+
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d).{8,20}$",
2727
message = "비밀번호는 영문자와 숫자를 포함해야 합니다.")
2828
@Schema(description = "비밀번호 (영문자 + 숫자 포함 8~20 글자)", example = "abc12345")
2929
String password,

src/main/java/com/synergy/backend/domain/member/api/dto/resposne/RecruiterMyInfoResponseDto.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,10 @@ public static RecruiterMyInfoResponseDto from(Recruiter recruiter) {
1616
recruiter.getResponsibility()
1717
);
1818
}
19+
20+
public static RecruiterMyInfoResponseDto from(String companyPhotoUrl, String recruiterName, String company,
21+
String responsibility) {
22+
return new RecruiterMyInfoResponseDto(
23+
companyPhotoUrl, recruiterName, company, responsibility);
24+
}
1925
}

src/main/java/com/synergy/backend/domain/member/service/AttendeeServiceImpl.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,17 @@ public void addJobInfo(Long id, JobInfoRequestDto request) {
7979
public void addJobInfoDetails(Long id, JobInfoDetailsRequestDto request, MultipartFile profileImage) {
8080
Attendee attendee = findAttendeeById(id);
8181

82+
FileInformationDto uploadedImage = null;
8283
if (profileImage != null && !profileImage.isEmpty()) {
83-
attendee.addImage(fileS3Util.uploadFile(profileImage));
84+
try {
85+
uploadedImage = fileS3Util.uploadFile(profileImage);
86+
} catch (Exception e) {
87+
throw new EmptyImageFileException();
88+
}
89+
}
90+
91+
if (uploadedImage != null) {
92+
attendee.addImage(uploadedImage);
8493
}
8594

8695
attendee.updateJobInfoDetails(
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.synergy.backend.domain.member.api;
2+
3+
import static org.mockito.ArgumentMatchers.*;
4+
import static org.mockito.BDDMockito.*;
5+
import static org.springframework.http.MediaType.*;
6+
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
7+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
8+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
9+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
10+
11+
import java.util.List;
12+
13+
import org.junit.jupiter.api.DisplayName;
14+
import org.junit.jupiter.api.Test;
15+
import org.springframework.data.domain.Page;
16+
import org.springframework.data.domain.PageImpl;
17+
import org.springframework.data.domain.PageRequest;
18+
import org.springframework.security.core.userdetails.UserDetails;
19+
import org.springframework.security.test.context.support.WithMockUser;
20+
21+
import com.synergy.backend.domain.member.api.dto.resposne.AttendeeLevelRankingResponseDto;
22+
import com.synergy.backend.domain.member.api.dto.resposne.AttendeePointRankingResponseDto;
23+
import com.synergy.backend.domain.member.entity.Attendee;
24+
import com.synergy.backend.domain.member.entity.RoleType;
25+
import com.synergy.backend.domain.member.entity.details.MembershipLevelType;
26+
import com.synergy.backend.module.ControllerTestSupport;
27+
28+
class AdminControllerTest extends ControllerTestSupport {
29+
30+
@DisplayName("관리자가 등급별 참가자 랭킹을 조회합니다.")
31+
@Test
32+
@WithMockUser(username = "AD12345", roles = {"ADMIN"})
33+
void getAttendeeLevelRankings() throws Exception {
34+
// given
35+
Attendee attendee1 = Attendee.of("email1@example.com", "encodedPassword", "참가자1", "01000000000");
36+
attendee1.addTotalPoints(800);
37+
Attendee attendee2 = Attendee.of("email2@example.com", "encodedPassword", "참가자2", "01011111111");
38+
attendee2.addTotalPoints(10);
39+
40+
List<AttendeeLevelRankingResponseDto> rankingList = List.of(
41+
AttendeeLevelRankingResponseDto.from(attendee1)
42+
);
43+
Page<AttendeeLevelRankingResponseDto> page = new PageImpl<>(rankingList, PageRequest.of(0, 10),
44+
rankingList.size());
45+
46+
given(adminService.getAttendeeLevelRankings(any(), any())).willReturn(page);
47+
given(jwtProvider.validateAccessToken(anyString())).willReturn(true);
48+
given(jwtProvider.getRoleTypeFromToken(anyString())).willReturn(RoleType.ADMIN);
49+
given(userDetailsService.loadUserByUsername(anyString())).willReturn(mock(UserDetails.class));
50+
51+
// when & then
52+
mockMvc.perform(get("/api/v1/admin/attendees/level-rankings")
53+
.param("membershipLevelType", MembershipLevelType.GOLD.name())
54+
.param("page", "0")
55+
.param("size", "10")
56+
.with(csrf())
57+
.contentType(APPLICATION_JSON))
58+
.andExpect(status().isOk())
59+
.andExpect(jsonPath("$.data.totalElements").value(1))
60+
.andExpect(jsonPath("$.data.content[0].attendeeName").value("참가자1"))
61+
.andExpect(jsonPath("$.data.content[0].membershipLevel").value(MembershipLevelType.GOLD.name()))
62+
.andDo(print());
63+
}
64+
65+
@DisplayName("관리자가 포인트 랭킹을 조회합니다.")
66+
@Test
67+
@WithMockUser(username = "AD12345", roles = {"ADMIN"})
68+
void getAttendeePointRankings() throws Exception {
69+
// given
70+
Attendee attendee1 = Attendee.of("email1@example.com", "encodedPassword", "참가자1", "010-0000-0000");
71+
attendee1.addTotalPoints(100);
72+
Attendee attendee2 = Attendee.of("email2@example.com", "encodedPassword", "참가자2", "010-1111-1111");
73+
attendee2.addTotalPoints(80);
74+
75+
List<AttendeePointRankingResponseDto> rankingList = List.of(
76+
AttendeePointRankingResponseDto.from(attendee1),
77+
AttendeePointRankingResponseDto.from(attendee2)
78+
);
79+
Page<AttendeePointRankingResponseDto> page = new PageImpl<>(rankingList, PageRequest.of(0, 10),
80+
rankingList.size());
81+
82+
given(adminService.getAttendeePointRankings(any())).willReturn(page);
83+
given(jwtProvider.validateAccessToken(anyString())).willReturn(true);
84+
given(jwtProvider.getRoleTypeFromToken(anyString())).willReturn(RoleType.ADMIN);
85+
given(userDetailsService.loadUserByUsername(anyString())).willReturn(mock(UserDetails.class));
86+
87+
// when & then
88+
mockMvc.perform(get("/api/v1/admin/attendees/point-rankings")
89+
.param("page", "0")
90+
.param("size", "10")
91+
.with(csrf())
92+
.contentType(APPLICATION_JSON))
93+
.andExpect(status().isOk())
94+
.andExpect(jsonPath("$.data.totalElements").value(2))
95+
.andExpect(jsonPath("$.data.content[0].attendeeName").value("참가자1"))
96+
.andExpect(jsonPath("$.data.content[0].totalPoints").value(100))
97+
.andDo(print());
98+
}
99+
}
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
package com.synergy.backend.domain.member.api;
2+
3+
import static org.mockito.BDDMockito.*;
4+
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
5+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
6+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
7+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
8+
9+
import java.util.List;
10+
11+
import org.junit.jupiter.api.DisplayName;
12+
import org.junit.jupiter.api.Test;
13+
import org.springframework.http.MediaType;
14+
import org.springframework.mock.web.MockMultipartFile;
15+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
16+
import org.springframework.security.core.context.SecurityContextHolder;
17+
import org.springframework.test.util.ReflectionTestUtils;
18+
19+
import com.synergy.backend.domain.member.api.dto.resposne.LikedRecruiterResponseDto;
20+
import com.synergy.backend.domain.member.api.dto.resposne.ProfileImageUpdatedResponseDto;
21+
import com.synergy.backend.domain.member.entity.Attendee;
22+
import com.synergy.backend.global.security.CustomUserDetails;
23+
import com.synergy.backend.module.ControllerTestSupport;
24+
25+
@DisplayName("AttendeeController 테스트")
26+
class AttendeeControllerTest extends ControllerTestSupport {
27+
28+
@DisplayName("좋아요한 채용담당자 목록을 조회한다")
29+
@Test
30+
void getLikedRecruiters() throws Exception {
31+
// given
32+
Long attendeeId = 1L;
33+
CustomUserDetails userDetails = new CustomUserDetails(createAttendee(attendeeId));
34+
SecurityContextHolder.getContext().setAuthentication(
35+
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
36+
);
37+
38+
List<LikedRecruiterResponseDto> liked = List.of(
39+
new LikedRecruiterResponseDto("회사1", "책임1", "홍길동"),
40+
new LikedRecruiterResponseDto("회사2", "책임2", "김철수")
41+
);
42+
43+
given(recruiterAttendeeLikeService.getLikedRecruiters(attendeeId)).willReturn(liked);
44+
45+
// when & then
46+
mockMvc.perform(get("/api/v1/attendee/liked-recruiters")
47+
.contentType(MediaType.APPLICATION_JSON))
48+
.andExpect(status().isOk())
49+
.andExpect(jsonPath("$.data.length()").value(2))
50+
.andExpect(jsonPath("$.data[0].company").value("회사1"))
51+
.andExpect(jsonPath("$.data[0].responsibility").value("책임1"))
52+
.andExpect(jsonPath("$.data[0].name").value("홍길동"))
53+
.andDo(print());
54+
}
55+
56+
@Test
57+
@DisplayName("참가자 현재 직무 정보 등록 요청을 처리한다")
58+
void addJobInfo() throws Exception {
59+
Long attendeeId = 1L;
60+
CustomUserDetails userDetails = new CustomUserDetails(createAttendee(attendeeId));
61+
SecurityContextHolder.getContext().setAuthentication(
62+
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
63+
);
64+
65+
String requestBody = """
66+
{
67+
"interestCodes": [101, 102, 103],
68+
"jobGroupCode": 1,
69+
"jobPositionCode": 101,
70+
"hiringInterested": true
71+
}
72+
""";
73+
74+
mockMvc.perform(patch("/api/v1/attendee/onboarding/job-info")
75+
.with(csrf())
76+
.contentType(MediaType.APPLICATION_JSON)
77+
.content(requestBody))
78+
.andExpect(status().isOk())
79+
.andExpect(jsonPath("$.code").value(200))
80+
.andDo(print());
81+
82+
then(attendeeService).should().addJobInfo(eq(attendeeId), any());
83+
}
84+
85+
@DisplayName("내 정보를 조회한다")
86+
@Test
87+
void getMyInformation() throws Exception {
88+
Long attendeeId = 1L;
89+
CustomUserDetails userDetails = new CustomUserDetails(createAttendee(attendeeId));
90+
SecurityContextHolder.getContext().setAuthentication(
91+
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
92+
);
93+
94+
given(attendeeService.getMyInformation(attendeeId)).willReturn(null);
95+
96+
mockMvc.perform(get("/api/v1/attendee/my")
97+
.contentType(MediaType.APPLICATION_JSON))
98+
.andExpect(status().isOk())
99+
.andExpect(jsonPath("$.code").value(200))
100+
.andDo(print());
101+
102+
then(attendeeService).should().getMyInformation(attendeeId);
103+
}
104+
105+
@DisplayName("특정 참가자의 상세 정보를 조회한다")
106+
@Test
107+
void getAttendeeInfoDetail() throws Exception {
108+
Long attendeeId = 1L;
109+
Long targetId = 2L;
110+
CustomUserDetails userDetails = new CustomUserDetails(createAttendee(attendeeId));
111+
SecurityContextHolder.getContext().setAuthentication(
112+
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
113+
);
114+
115+
given(attendeeService.getAttendeeInfoDetail(targetId, attendeeId, userDetails.getRole())).willReturn(null);
116+
117+
mockMvc.perform(get("/api/v1/attendee/{attendeeId}", targetId)
118+
.contentType(MediaType.APPLICATION_JSON))
119+
.andExpect(status().isOk())
120+
.andExpect(jsonPath("$.code").value(200))
121+
.andDo(print());
122+
123+
then(attendeeService).should().getAttendeeInfoDetail(targetId, attendeeId, userDetails.getRole());
124+
}
125+
126+
@DisplayName("참가자 상세 직무 정보 등록 요청을 처리한다")
127+
@Test
128+
void addJobInfoDetails() throws Exception {
129+
Long attendeeId = 1L;
130+
CustomUserDetails userDetails = new CustomUserDetails(createAttendee(attendeeId));
131+
SecurityContextHolder.getContext().setAuthentication(
132+
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
133+
);
134+
135+
MockMultipartFile request = new MockMultipartFile(
136+
"request",
137+
"request.json",
138+
MediaType.APPLICATION_JSON_VALUE,
139+
"""
140+
{
141+
"desiredJobGroupCode": 1,
142+
"desiredJobPositionCode": 101,
143+
"educationLevelCode": 3,
144+
"ageGroupCode": 20,
145+
"techStacks": "Java,Spring",
146+
"experienceLevelCode": 2,
147+
"desiredWorkRegionCodes": [11, 22],
148+
"selfIntroduction": "저는 열정적인 백엔드 개발자입니다.",
149+
"additionalInfo": "스타트업 인턴 경험, 외부 해커톤 참가",
150+
"workplaceSelectionFactorCodes": [1, 2],
151+
"preferredCorporateCultureCodes": [1],
152+
"conferencePurposeCodes": [1, 2]
153+
}
154+
""".getBytes()
155+
);
156+
157+
MockMultipartFile profileImage = new MockMultipartFile("profileImage", "profile.png", MediaType.IMAGE_PNG_VALUE,
158+
"image content".getBytes());
159+
160+
mockMvc.perform(multipart("/api/v1/attendee/onboarding/job-info-details")
161+
.file(request)
162+
.file(profileImage)
163+
.with(csrf())
164+
.with(requestBuilder -> {
165+
requestBuilder.setMethod("PATCH");
166+
return requestBuilder;
167+
})
168+
.contentType(MediaType.MULTIPART_FORM_DATA))
169+
.andExpect(status().isOk())
170+
.andExpect(jsonPath("$.code").value(200))
171+
.andDo(print());
172+
173+
then(attendeeService).should().addJobInfoDetails(eq(attendeeId), any(), any());
174+
}
175+
176+
@DisplayName("참가자 프로필 사진을 수정한다")
177+
@Test
178+
void updateProfileImage() throws Exception {
179+
Long attendeeId = 1L;
180+
CustomUserDetails userDetails = new CustomUserDetails(createAttendee(attendeeId));
181+
SecurityContextHolder.getContext().setAuthentication(
182+
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
183+
);
184+
185+
MockMultipartFile profileImage = new MockMultipartFile("profileImage", "profile.png", MediaType.IMAGE_PNG_VALUE,
186+
"image content".getBytes());
187+
188+
given(attendeeService.updateProfileImage(eq(attendeeId), any())).willReturn(
189+
ProfileImageUpdatedResponseDto.from("updated-url"));
190+
191+
mockMvc.perform(multipart("/api/v1/attendee/profile-image")
192+
.file(profileImage)
193+
.with(csrf())
194+
.with(request -> {
195+
request.setMethod("PATCH");
196+
return request;
197+
})
198+
.contentType(MediaType.MULTIPART_FORM_DATA))
199+
.andExpect(status().isOk())
200+
.andExpect(jsonPath("$.code").value(200))
201+
.andExpect(jsonPath("$.data.profileImageUrl").value("updated-url"))
202+
.andDo(print());
203+
204+
then(attendeeService).should().updateProfileImage(eq(attendeeId), any());
205+
}
206+
207+
private Attendee createAttendee(Long id) {
208+
Attendee attendee = Attendee.of("exaple@example.com", "pass", "참가자", "01101010");
209+
ReflectionTestUtils.setField(attendee, "id", id);
210+
return attendee;
211+
}
212+
}

0 commit comments

Comments
 (0)