Skip to content
Merged
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'org.springframework.boot:spring-boot-starter-mail'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.annotation.Nullable;
import jakarta.validation.constraints.Size;
import lombok.Builder;

import java.time.LocalDate;
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public record UpdateContestDto(
@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;

import java.time.LocalDate;
import java.util.List;

import static com.gongjakso.server.domain.contest.entity.QContest.contest;
Expand Down Expand Up @@ -44,6 +45,9 @@ private OrderSpecifier<?> arg(String sortAt){
if("VIEW".equals(sortAt)){
return contest.view.desc();//조회순
}
if("ACTIVE".equals(sortAt)){
return contest.finishedAt.after(LocalDate.now()).desc(); //활동 중인 공모전
}
return contest.createdAt.desc(); //최신순
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package com.gongjakso.server.domain.contest.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gongjakso.server.domain.contest.dto.request.ContestReq;
import com.gongjakso.server.domain.contest.dto.request.UpdateContestDto;
import com.gongjakso.server.domain.contest.dto.response.ContestListRes;
import com.gongjakso.server.domain.contest.dto.response.ContestRes;
import com.gongjakso.server.domain.contest.mock.WithCustomMockUser;
import com.gongjakso.server.domain.contest.service.ContestService;
import com.gongjakso.server.domain.contest.util.ContestUtilTest;
import com.gongjakso.server.domain.member.entity.Member;
import com.gongjakso.server.domain.member.enumerate.MemberType;
import com.gongjakso.server.domain.member.util.MemberUtilTest;
import com.gongjakso.server.global.security.PrincipalDetails;
import com.gongjakso.server.global.security.jwt.TokenProvider;
import com.gongjakso.server.global.util.redis.RedisClient;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileInputStream;
import java.util.Optional;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(controllers = ContestController.class)
@MockBean(JpaMetamodelMappingContext.class)
@WebAppConfiguration
public class ContestControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean
ContestService contestService;
@MockBean
PrincipalDetails principalDetails;
@MockBean
TokenProvider tokenProvider;
@MockBean
RedisClient redisClient;
@MockBean
HttpServletRequest httpServletRequest;
@MockBean
HttpServletResponse httpServletResponse;

@Test
@WithCustomMockUser
@DisplayName("공모전 생성 api (관리자만 요청)")
void createContest() throws Exception{
Member adminMember = MemberUtilTest.buildMemberByType(MemberType.ADMIN);
ContestReq contestReq = ContestUtilTest.buildContestReq();
given(principalDetails.getMember()).willReturn(adminMember);

final String fileName = "1"; //파일명
final String contentType = "avif"; //파일타입

FileInputStream fileInputStream = new FileInputStream("src/test/java/com/gongjakso/server/domain/contest/image/"+fileName+"."+contentType);
MockMultipartFile image = new MockMultipartFile(
"image", //name
fileName + "." + contentType, //originalFilename
contentType,
fileInputStream
);

mockMvc.perform(multipart("/api/v2/contest")
.file(image)
.header("Authorization","Bearer Token token")
.param("contestReq", new ObjectMapper().writeValueAsString(contestReq)) //param은 문자열로만 데이터 전송이 가능하므로 json문자열로 변환
.contentType(MediaType.MULTIPART_FORM_DATA)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated());

}

@Test
@DisplayName("공모전 정보 API")
void findContestTest() throws Exception {
// given
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
ContestRes contestRes = ContestUtilTest.buildContestResDto();
given(contestService.find(1L, request, response))
.willReturn(contestRes);

// when
mockMvc.perform(get("/api/v2/contest/{contest_id}", 1L))
.andExpect(jsonPath("$.data.title").value("공작소 title"));
}

@Test
@DisplayName("공모전 검색 API")
void searchContestTest() throws Exception {
// given
ContestListRes listRes = new ContestListRes(anyList(),anyInt(),anyLong(),anyInt());

given(contestService.search("공모전", "createdAt", any(Pageable.class)))
.willReturn(listRes);

// when
mockMvc.perform(get("/api/v2/contest/search")
.param("word", "공모전")
.param("sortAt", "createdAt")
.param("page", "0")
.param("size", "12"))
.andExpect(status().isOk());

}

@Test
@WithCustomMockUser
@DisplayName("공모전 수정 API - 관리자만")
void updateContestTest() throws Exception {
// given
final String fileName = "1"; //파일명
final String contentType = "avif"; //파일타입
Member adminMember = MemberUtilTest.buildMemberByType(MemberType.ADMIN);
UpdateContestDto updateDto = ContestUtilTest.buildUpdateContestDto();
Long contestId = 1L;
ContestRes updatedContestRes = ContestUtilTest.buildContestResDto();
given(principalDetails.getMember()).willReturn(adminMember);
given(contestService.update(adminMember, contestId, any(MultipartFile.class), eq(updateDto)))
.willReturn(updatedContestRes);

FileInputStream fileInputStream = new FileInputStream("src/test/java/com/gongjakso/server/domain/contest/image/"+fileName+"."+contentType);
MockMultipartFile image = new MockMultipartFile(
"image", //name
fileName + "." + contentType, //originalFilename
contentType,
fileInputStream
);

// when
mockMvc.perform(multipart("/api/v2/contest/{contest_id}", contestId)
.file(image)
.param("contestReq", new ObjectMapper().writeValueAsString(updateDto))
.contentType(MediaType.MULTIPART_FORM_DATA)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.title").value(updateDto.title()));
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.gongjakso.server.domain.contest.mock;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface WithCustomMockUser {

String email() default "[email protected]";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.gongjakso.server.domain.contest.mock;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.test.context.support.WithSecurityContextFactory;


import java.util.List;

public class WithCustomMockUserSecurityContextFactory implements
WithSecurityContextFactory<WithCustomMockUser> {

@Override
public SecurityContext createSecurityContext(WithCustomMockUser annotation) {
String email = annotation.email();

Authentication auth = new UsernamePasswordAuthenticationToken(email, "",
List.of(new SimpleGrantedAuthority("ADMIN")));

SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(auth);

return context;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.gongjakso.server.domain.contest.service;

import com.gongjakso.server.domain.contest.entity.Contest;
import com.gongjakso.server.domain.contest.repository.ContestRepository;
import com.gongjakso.server.domain.contest.util.ContestUtilTest;
import com.gongjakso.server.domain.member.entity.Member;
import com.gongjakso.server.domain.member.enumerate.MemberType;
import com.gongjakso.server.domain.member.util.MemberUtilTest;
import com.gongjakso.server.global.exception.ApplicationException;
import com.gongjakso.server.global.util.s3.S3Client;
import org.junit.jupiter.api.DisplayName;
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.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

@ExtendWith(MockitoExtension.class)
public class DeleteContestServiceTest {
@Mock
ContestRepository contestRepository;
@Mock
S3Client s3Client;
@InjectMocks
ContestService contestService;

@Test
@DisplayName("memberType = general인 경우 공모전 삭제 테스트")
void deleteGeneralMemberTypeTest(){
Member generalmember = MemberUtilTest.buildMemberByType(MemberType.GENERAL);

assertThatThrownBy(() -> contestService.delete(generalmember, 1L))
.isInstanceOf(ApplicationException.class);
}

@Test
@DisplayName("memberType = admin인 경우 공모전 삭제")
void deleteAdminMemberTypeTest(){
Member adminMember = MemberUtilTest.buildMemberByType(MemberType.ADMIN);
Contest contest = mock(Contest.class);
given(contestRepository.findById(1L)).willReturn(Optional.of(contest));
given(contest.getImgUrl()).willReturn("https://s3.amazonaws.com/gongjakso-bucket/contest/image");

contestService.delete(adminMember,1L);

verify(s3Client).delete("https://s3.amazonaws.com/gongjakso-bucket/contest/image");
verify(contestRepository).delete(contest);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.gongjakso.server.domain.contest.service;

import com.gongjakso.server.domain.contest.dto.response.ContestRes;
import com.gongjakso.server.domain.contest.entity.Contest;
import com.gongjakso.server.domain.contest.repository.ContestRepository;
import com.gongjakso.server.domain.contest.util.ContestUtilTest;
import com.gongjakso.server.global.exception.ApplicationException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
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.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class FindContestServiceTest {
@Mock
private ContestRepository contestRepository;
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
@InjectMocks
private ContestService contestService;

@BeforeEach
void setup(){

}

@Test
@DisplayName("공모전 검색 테스트")
void findContestTest(){
Contest contest = ContestUtilTest.buildContest();
given(contestRepository.findById(1L)).willReturn(Optional.of(contest));

ContestRes contestRes = contestService.find(1L,request,response);

verify(contestRepository).findById(1L);
assertNotNull(contestRes);
}

@Test
@DisplayName("공모전 검색 시 존재하지 않는 공모전 테스트")
void findNotExistContestTest(){
given(contestRepository.findById(1L)).willReturn(Optional.empty());

assertThatThrownBy(() -> contestService.find(1L,request,response))
.isInstanceOf(ApplicationException.class);
}
}
Loading