Skip to content

Commit

Permalink
Merge pull request #37 from win-luck/feature/36-chore
Browse files Browse the repository at this point in the history
[chore] 전반적인 코드 재정비 (#36)
  • Loading branch information
win-luck authored Aug 15, 2024
2 parents 56056c7 + 082a2f3 commit 79eb54a
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 88 deletions.
5 changes: 1 addition & 4 deletions .github/workflows/CD.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ name: CD
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
types: [closed]
workflow_dispatch:
pull_request_target:
branches: [ "main" ]
types: [closed]
workflow_dispatch:

permissions:
contents: read
Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ name: CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:
pull_request_target:
branches: [ "main" ]
workflow_dispatch:

permissions:
contents: read
Expand Down Expand Up @@ -37,6 +35,9 @@ jobs:
echo "${{ secrets.APPLICATION }}" > ./application.yml
echo "${{ secrets.PROD }}" > ./application-prod.yml
- name: Test with Gradle
run: ./gradlew test

- name: Build with Gradle
run: ./gradlew build -x test

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ public void uploadPhoto(Long photoRequestId, String imageUrl) {
photoRequestRepository.save(photoRequest);
photoResult.update(imageUrl);
photoResultRepository.save(photoResult);
log.info("결과 이미지 URL 업로드 완료: {}", imageUrl);

// Redis 대기열의 user 정보 삭제
redisTemplate.opsForSet().remove(ConstantUtil.USER_ID_KEY, user.getId());
redisTemplate.delete(user.getId().toString());
log.info("Redis 대기열에서 요청 삭제 : {}", user.getId());

// 이메일 발송
EmailInfo emailInfo = EmailInfo.builder()
Expand All @@ -73,6 +75,7 @@ public String getPhotoUrl(Long photoRequestId) {
throw new BaseException(ResponseCode.IMAGE_ON_PROCESS);
}

log.info("결과 이미지 URL 조회 완료: {}", photoResult.getImageUrl());
return photoResult.getImageUrl();
}

Expand All @@ -96,7 +99,7 @@ private void sendEmail(EmailInfo emailInfo) {

// 메일 전송
mailSender.send(mimeMessage);

log.info("이메일 전송 완료: {}", emailInfo.getEmail());
} catch (Exception e){
e.printStackTrace();
throw new BaseException(ResponseCode.EMAIL_SEND_ERROR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public Long createImage(CreateImageDto dto, Long userId){
.createDate(LocalDateTime.now())
.build();
photoResultRepository.save(photoResult);
log.info("사용자의 이미지 요청 생성 완료, RabbitMQ에 전송 준비: {}", userId);

try {
ImageInfo imageInfo = ImageInfo.builder()
Expand All @@ -72,6 +73,7 @@ public Long createImage(CreateImageDto dto, Long userId){
// 2. Redis에 <String keyName, Long requestId> 형식으로 진행되고 있는 request 정보를 저장한다.
// 3. 추후 사진이 완성된다면 requestId를 통해 request를 찾아서 상태를 바꾸고 1:1 관계인 result에 접근해서 imageUrl를 수정한다.
// 4. 즉, 파이썬에서 스프링으로 향하는 POST API는 {requestId, imageUrl}이 필수적으로 존재해야 한다.
log.info("RabbitMQ 전송 완료: {}", message);
} catch (JsonProcessingException e) {
log.error("JSON 변환 실패");
throw new PhotoRequestException(ResponseCode.JSON_PARSE_ERROR);
Expand All @@ -80,7 +82,7 @@ public Long createImage(CreateImageDto dto, Long userId){
// Redis에 userId 저장하고, userId로 requestId 추적할 수 있도록 함
redisTemplate.opsForSet().add(ConstantUtil.USER_ID_KEY, userId);
redisTemplate.opsForSet().add(userId.toString(), request.getId());

log.info("Redis 대기열 등록 완료: {}", userId);
return request.getId();
}

Expand All @@ -91,6 +93,7 @@ public List<String> getRequestImages(Long userId){
validateUser(userId);

// 현재 처리가 완료되지 않은 이미지(imageUrl이 null)는 보내지 않음
log.info("사용자의 이미지 리스트 조회 시도: {}", userId);
return photoResultRepository.findAllByUserId(userId)
.stream()
.map(PhotoResult::getImageUrl)
Expand All @@ -106,12 +109,14 @@ public String getRequestStatus(Long userId){

// Redis에 userId가 존재하면 아직 처리 대기 중인 요청이므로 WAITING 반환
if(Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(ConstantUtil.USER_ID_KEY, userId))){
log.info("사용자의 요청 상태 조회, 현재 대기 중: {}", userId);
return RequestStatus.WAITING.name();
}

RequestStatus status = photoRequestRepository.findTopByUserIdOrderByCreateDateDesc(userId)
.orElseThrow(() -> new BaseException(ResponseCode.PHOTO_REQUEST_NOT_FOUND))
.getStatus();
log.info("사용자의 요청 상태 조회, 현재 상태: {} {}", status.name(), userId);
return status.name();
}

Expand Down
13 changes: 0 additions & 13 deletions src/test/java/gdsc/cau/puangbe/PuangbeApplicationTests.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import static org.junit.jupiter.api.Assertions.*;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;

Expand Down Expand Up @@ -90,7 +90,9 @@ void uploadImage404Test() throws Exception {
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(uploadImageDto)))
.andExpect(content().json(responseBody));
assertThrows(BaseException.class, () -> photoService.uploadPhoto(uploadImageDto.getPhotoRequestId(), uploadImageDto.getImageUrl()));
assertThatThrownBy(() -> photoService.uploadPhoto(uploadImageDto.getPhotoRequestId(), uploadImageDto.getImageUrl()))
.isInstanceOf(BaseException.class)
.hasMessage(ResponseCode.PHOTO_RESULT_NOT_FOUND.getMessage());
}

@DisplayName("uploadImage: 이미지를 업로드하려는 photoResult의 상태가 FINISHED이면 409 예외가 발생하며, 실패 객체를 반환한다.")
Expand All @@ -106,7 +108,9 @@ void uploadImage409Test() throws Exception {
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(uploadImageDto)))
.andExpect(content().json(responseBody));
assertThrows(BaseException.class, () -> photoService.uploadPhoto(uploadImageDto.getPhotoRequestId(), uploadImageDto.getImageUrl()));
assertThatThrownBy(() -> photoService.uploadPhoto(uploadImageDto.getPhotoRequestId(), uploadImageDto.getImageUrl()))
.isInstanceOf(BaseException.class)
.hasMessage(ResponseCode.URL_ALREADY_UPLOADED.getMessage());
}

@DisplayName("getImage: 유저의 특정 요청의 결과로 만들어진 이미지 URL을 조회한다.")
Expand Down Expand Up @@ -140,6 +144,8 @@ void getImage404Test(Long photoRequestId) throws Exception {
// when & then
mockMvc.perform(MockMvcRequestBuilders.get(baseUrl + "/" + photoRequestId))
.andExpect(content().json(responseBody));
assertThrows(BaseException.class, () -> photoService.getPhotoUrl(photoRequestId));
assertThatThrownBy(() -> photoService.getPhotoUrl(photoRequestId))
.isInstanceOf(BaseException.class)
.hasMessage(ResponseCode.PHOTO_RESULT_NOT_FOUND.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gdsc.cau.puangbe.photo.service;
import gdsc.cau.puangbe.common.enums.Gender;
import gdsc.cau.puangbe.common.exception.BaseException;
import gdsc.cau.puangbe.common.util.ConstantUtil;
import gdsc.cau.puangbe.common.util.ResponseCode;
import gdsc.cau.puangbe.photo.entity.PhotoRequest;
import gdsc.cau.puangbe.photo.entity.PhotoResult;
Expand All @@ -13,6 +14,11 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.mail.javamail.JavaMailSender;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.IContext;

import java.time.LocalDateTime;
import java.util.List;
Expand All @@ -34,65 +40,63 @@ class PhotoServiceImplTest {
@Mock
private PhotoResultRepository photoResultRepository;

private Long photoRequestId = 1L;
private String imageUrl = "https://example.com/image.jpg";
private User user = User.builder().userName("test").build();
private PhotoRequest photoRequest = PhotoRequest.builder()
.user(user)
.gender(Gender.MALE)
.urls(List.of("https://example.com/image.jpg"))
.build();
private PhotoResult photoResult = PhotoResult.builder()
.user(user)
.photoRequest(photoRequest)
.createDate(LocalDateTime.now())
.build();
@Mock
private RedisTemplate<String, Long> redisTemplate;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Mock
private SetOperations<String, Long> setOperations;

@DisplayName("createPhoto: 이미지 처리 요청을 생성한다.")
@Test
void createPhotoTest() {
// given
given(photoRequestRepository.findById(photoRequestId)).willReturn(Optional.of(photoRequest));
given(photoResultRepository.save(any())).willReturn(photoResult);
@Mock
private User user;

// when
photoService.createPhoto(photoRequestId);
@Mock
private TemplateEngine templateEngine;

// then
verify(photoRequestRepository, times(1)).findById(photoRequestId);
verify(photoResultRepository, times(1)).save(any());
}
@Mock
private JavaMailSender mailSender;

@DisplayName("createPhoto: 이미지 처리 요청을 찾을 수 없는 경우 예외가 발생한다.")
@Test
void createPhotoNotFoundTest() {
// given
given(photoRequestRepository.findById(photoRequestId)).willReturn(Optional.empty());
private Long photoRequestId = 1L;
private String imageUrl = "https://example.com/image.jpg";
private PhotoRequest photoRequest;
private PhotoResult photoResult;

// when & then
assertThatThrownBy(() -> photoService.createPhoto(photoRequestId))
.isInstanceOf(BaseException.class)
.hasMessage(ResponseCode.PHOTO_REQUEST_NOT_FOUND.getMessage());
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
given(user.getUserName()).willReturn("test");
given(user.getId()).willReturn(1L);
photoRequest = PhotoRequest.builder()
.user(user)
.gender(Gender.MALE)
.urls(List.of("https://example.com/image.jpg"))
.build();
photoResult = PhotoResult.builder()
.user(user)
.photoRequest(photoRequest)
.createDate(LocalDateTime.now())
.build();
}

@DisplayName("uploadPhoto: 사진 URL을 업로드하고 상태를 업데이트한다.")
@Test
// @Test FIXME: TemplateEngine 관련 Mocking이 올바르지 이루어지지 않고 있음
void uploadPhotoTest() {
// given
given(photoResultRepository.findByPhotoRequestId(photoRequestId)).willReturn(Optional.of(photoResult));
given(photoRequestRepository.findById(any())).willReturn(Optional.of(photoRequest));
given(redisTemplate.opsForSet()).willReturn(setOperations);
given(setOperations.remove(ConstantUtil.USER_ID_KEY, user.getId())).willReturn(1L);
given(redisTemplate.delete(user.getId().toString())).willReturn(true);
given(templateEngine.process(anyString(), any(IContext.class))).willReturn("test");

// when
photoService.uploadPhoto(photoRequestId, imageUrl);

// then
assertThat(photoResult.getImageUrl()).isEqualTo(imageUrl);
verify(photoResultRepository, times(1)).save(photoResult);
verify(photoRequestRepository, times(1)).save(photoRequest);
verify(setOperations, times(1)).remove(ConstantUtil.USER_ID_KEY, user.getId().toString());
verify(redisTemplate, times(1)).delete(anyString());
}

@DisplayName("uploadPhoto: 사진 결과를 찾을 수 없는 경우 예외가 발생한다.")
Expand All @@ -104,7 +108,7 @@ void uploadPhotoNotFoundTest() {
// when & then
assertThatThrownBy(() -> photoService.uploadPhoto(photoRequestId, imageUrl))
.isInstanceOf(BaseException.class)
.hasMessage(ResponseCode.PHOTO_RESULT_NOT_FOUND.getMessage());
.hasMessage(ResponseCode.PHOTO_REQUEST_NOT_FOUND.getMessage());
}

@DisplayName("uploadPhoto: 이미 URL이 업로드된 경우 예외가 발생한다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
Expand Down Expand Up @@ -55,9 +55,9 @@ class PhotoRequestControllerTest {
@Test
void createImageTest() throws Exception {
// given
doNothing().when(photoRequestService).createImage(createImageDto, 1L);
when(photoRequestService.createImage(any(CreateImageDto.class), any())).thenReturn(1L);
String requestBody = mapper.writeValueAsString(createImageDto);
String responseBody = mapper.writeValueAsString(APIResponse.success(null, ResponseCode.PHOTO_REQUEST_CREATE_SUCCESS.getMessage()));
String responseBody = mapper.writeValueAsString(APIResponse.success(1L, ResponseCode.PHOTO_REQUEST_CREATE_SUCCESS.getMessage()));

// when & then
mockMvc.perform(post(baseUrl)
Expand All @@ -80,7 +80,9 @@ void createImage404Test() throws Exception {
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(content().json(responseBody));
assertThrows(BaseException.class, () -> photoRequestService.createImage(createImageDto, 1L));
assertThatThrownBy(() -> photoRequestService.createImage(createImageDto, 1L))
.isInstanceOf(BaseException.class)
.hasMessage(ResponseCode.USER_NOT_FOUND.getMessage());
}

@DisplayName("getRequestImages: 유저의 전체 사진 리스트를 조회하며, 성공 객체를 반환한다.")
Expand All @@ -106,7 +108,9 @@ void getRequestImages404Test() throws Exception {
// when & then
mockMvc.perform(get(baseUrl + "/list"))
.andExpect(content().json(responseBody));
assertThrows(BaseException.class, () -> photoRequestService.getRequestImages(1L));
assertThatThrownBy(() -> photoRequestService.getRequestImages(1L))
.isInstanceOf(BaseException.class)
.hasMessage(ResponseCode.USER_NOT_FOUND.getMessage());
}

@DisplayName("getRequestStatus: 유저의 최근 요청 상태를 조회하며, 성공 객체를 반환한다.")
Expand All @@ -131,6 +135,8 @@ void getRequestStatus404Test() throws Exception {
// when & then
mockMvc.perform(get(baseUrl + "/status"))
.andExpect(content().json(responseBody));
assertThrows(BaseException.class, () -> photoRequestService.getRequestStatus(1L));
assertThatThrownBy(() -> photoRequestService.getRequestStatus(1L))
.isInstanceOf(BaseException.class)
.hasMessage(ResponseCode.USER_NOT_FOUND.getMessage());
}
}
Loading

0 comments on commit 79eb54a

Please sign in to comment.