Skip to content

Commit

Permalink
Test: 지원서 조회 / 게시글 댓글 테스트 코드 작성 (#126)
Browse files Browse the repository at this point in the history
* Test: 게시글 댓글 그룹 생성, 조회 쿼리 테스트

* Refac: Comment 테스트 코드 리팩토링

* Refac: 지원서 상태는 지원서 클래스에서 변경, transaction manager 빈 등록
후 재사용

* Test: 어드민 지원서 관련 복잡한 쿼리 테스트 코드 작성

* Fix: 새로 추가된 UNDEFINED 트랙을 응답에 포함하는 문제 수정

* Fix: 지원 상태 업데이트 조건문 변경

* Fix: + 연결자 대신 String formatter 변경
  • Loading branch information
kwonssshyeon authored Oct 21, 2024
1 parent ce25ea7 commit a93937d
Show file tree
Hide file tree
Showing 10 changed files with 368 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.gdsc_knu.official_homepage.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

@Configuration
public class TxTemplateConfig {
public final TransactionTemplate transactionTemplate;

public TxTemplateConfig(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,10 @@ public void changeMark() {
this.isMarked = !this.isMarked;
}

public void approve() {
this.applicationStatus = ApplicationStatus.APPROVED;
}

public void reject() {
this.applicationStatus = ApplicationStatus.REJECTED;
public void updateStatus(ApplicationStatus status) {
if (this.applicationStatus != ApplicationStatus.TEMPORAL){
this.applicationStatus = status;
}
}

public void saveNote(String note) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
package com.gdsc_knu.official_homepage.entity.enumeration;

public enum Track {
FRONT_END, BACK_END, ANDROID, AI, DESIGNER, UNDEFINED
FRONT_END, BACK_END, ANDROID, AI, DESIGNER, UNDEFINED;

public static Track[] getValidTrack() {
return new Track[] {
FRONT_END,
BACK_END,
ANDROID,
AI,
DESIGNER
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface CommentRepository extends JpaRepository<Comment, Long> {
@Query("SELECT c " +
"FROM Comment c " +
"WHERE c.post.id = :postId "+
"ORDER BY c.parent.id ASC, c.createAt ASC")
Page<Comment> findCommentAndReply(Pageable pageable, Long postId);
Page<Comment> findCommentAndReply(Pageable pageable, @Param(value = "postId") Long postId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private void sendMail(String email, String mailTemplate) {
mailSender.send(message);
} catch (MessagingException | MailException e) {
redisRepository.addData(REDIS_KEY, email);
throw new CustomException(ErrorCode.FAILED_SEND_MAIL, email + "의 메일 전송에 실패하였습니다: " + e.getMessage());
throw new CustomException(ErrorCode.FAILED_SEND_MAIL);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;


import java.util.Arrays;
import java.util.List;
import java.util.Map;
Expand All @@ -29,7 +29,9 @@
public class AdminApplicationService {
private final ApplicationRepository applicationRepository;
private final MailService mailService;
private final PlatformTransactionManager transactionManager;
private final TransactionTemplate transactionTemplate;



@Transactional(readOnly = true)
public AdminApplicationResponse.Statistics getStatistic() {
Expand All @@ -50,7 +52,7 @@ public Map<String, Integer> getTrackStatistic() {
}

private void addDefaultTrack(Map<String, Integer> trackCountMap){
Arrays.stream(Track.values())
Arrays.stream(Track.getValidTrack())
.forEach(track -> trackCountMap.putIfAbsent(track.name(), 0));
}

Expand Down Expand Up @@ -78,26 +80,19 @@ public void markApplication(Long id) {
}


public void decideApplication(Long id, ApplicationStatus status) {
/**
* 메일 전송에 실패해도 status 롤백하지 않는다.
* 관리자 기능이므로 비동기로 처리하지 않고, 실행결과를 알려준다.
*/
public void decideApplication(Long id, ApplicationStatus applicationStatus) {
Application application = applicationRepository.findById(id)
.orElseThrow(() -> new CustomException(ErrorCode.APPLICATION_NOT_FOUND));
updateApplicationStatus(application, status);
transactionTemplate.executeWithoutResult(
status -> application.updateStatus(applicationStatus)
);
mailService.sendEach(application);
}

private void updateApplicationStatus(Application application, ApplicationStatus status) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.executeWithoutResult(transactionStatus -> {
if (status == ApplicationStatus.APPROVED) {
application.approve();
} else if (status == ApplicationStatus.REJECTED) {
application.reject();
} else {
throw new CustomException(ErrorCode.INVALID_APPLICATION_STATE);
}
});
}


@Transactional
public AdminApplicationResponse.Detail getApplicationDetail(Long id) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.gdsc_knu.official_homepage.repository.application;

import com.gdsc_knu.official_homepage.OfficialHomepageApplication;
import com.gdsc_knu.official_homepage.config.QueryDslConfig;
import com.gdsc_knu.official_homepage.dto.admin.application.ApplicationStatisticType;
import com.gdsc_knu.official_homepage.entity.application.Application;
import com.gdsc_knu.official_homepage.entity.enumeration.ApplicationStatus;
import com.gdsc_knu.official_homepage.repository.ApplicationRepository;
import org.junit.jupiter.api.AfterEach;
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.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ContextConfiguration;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
@Import(QueryDslConfig.class)
@ContextConfiguration(classes = OfficialHomepageApplication.class)
public class AdminApplicationRepositoryTest {
@Autowired private ApplicationRepository applicationRepository;

@AfterEach
void clear() {
applicationRepository.deleteAll();
}


@Test
@DisplayName("지원 현황을 정상적으로 카운트한다.")
void getStatistic() {
// given
int start = 1;
int countPerStatus = 5;
List<Application> temporal = createApplicationList(start, start+countPerStatus, ApplicationStatus.TEMPORAL);
start+=countPerStatus;
List<Application> save = createApplicationList(start, start+countPerStatus, ApplicationStatus.SAVED);
start+=countPerStatus;
List<Application> approve = createApplicationList(start, start+countPerStatus, ApplicationStatus.APPROVED);
start+=countPerStatus;
List<Application> reject = createApplicationList(start, start+countPerStatus, ApplicationStatus.REJECTED);
List<Application> allApplications = Stream.of(temporal, save, approve, reject)
.flatMap(List::stream)
.toList();
applicationRepository.saveAll(allApplications);

// when
ApplicationStatisticType statistic = applicationRepository.getStatistics();

// then
assertThat(statistic.getTotal()).isEqualTo(15);
assertThat(statistic.getApprovedCount()).isEqualTo(5);
assertThat(statistic.getRejectedCount()).isEqualTo(5);
assertThat(statistic.getOpenCount()).isEqualTo(0);
}

private List<Application> createApplicationList(int startNum, int count, ApplicationStatus status){
List<Application> applicationList = new ArrayList<>();
for (int i=startNum; i<count; i++) {
applicationList.add(createApplication(i, status));
}
return applicationList;
}

private Application createApplication(int id, ApplicationStatus status) {
return Application.builder()
.email(String.format("test%[email protected]", id))
.studentNumber(String.valueOf(id))
.phoneNumber(String.format("010-0000-%s", id))
.applicationStatus(status)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.gdsc_knu.official_homepage.repository.post;

import com.gdsc_knu.official_homepage.OfficialHomepageApplication;
import com.gdsc_knu.official_homepage.config.QueryDslConfig;
import com.gdsc_knu.official_homepage.entity.Member;
import com.gdsc_knu.official_homepage.entity.enumeration.Track;
import com.gdsc_knu.official_homepage.entity.post.Comment;
import com.gdsc_knu.official_homepage.entity.post.Post;
import com.gdsc_knu.official_homepage.repository.CommentRepository;
import org.junit.jupiter.api.BeforeEach;
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.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.util.ReflectionTestUtils;

import static org.assertj.core.api.Assertions.assertThat;

import java.time.LocalDateTime;
import java.util.List;


@DataJpaTest
@Import(QueryDslConfig.class)
@ContextConfiguration(classes = OfficialHomepageApplication.class)
public class CommentRepositoryTest {
@Autowired private CommentRepository commentRepository;
@Autowired private TestEntityManager entityManager;

private Member author;
private Post post;
@BeforeEach
void setUp() {
author = Member.builder()
.email("[email protected]")
.name("테스트 유저")
.track(Track.BACK_END)
.build();
entityManager.persistAndFlush(author);

post = Post.builder()
.member(author)
.build();
entityManager.persistAndFlush(post);
}

@Test
@DisplayName("댓글의 순서가 올바르게 조회된다")
void findCommentAndReply() {
// given
Comment parent1 = Comment.from("1",author,post,null);
Comment child1 = Comment.from("2",author,post,parent1);
Comment child2 = Comment.from("3",author,post,parent1);
Comment parent2 = Comment.from("4",author,post,null);
Comment child3 = Comment.from("5",author,post,parent2);
Comment child4 = Comment.from("6",author,post,parent2);

commentRepository.saveAll(List.of(parent1, parent2, child1, child2, child3, child4));
// 2,5 댓글의 생성 시각 나중으로 변경
ReflectionTestUtils.setField(child3, "createAt", LocalDateTime.now());
ReflectionTestUtils.setField(child1, "createAt", LocalDateTime.now());

// when
Page<Comment> commentList = commentRepository.findCommentAndReply(PageRequest.of(0,6), post.getId());

// then
assertThat(commentList).extracting("content")
.containsExactly("1","3","2","4","6","5");
}


@Test
@DisplayName("부모 댓글 생성 시 자신을 그룹으로 한다.")
void save() {
// given
Comment parent = Comment.from("댓글내용",author,post,null);
Comment child = Comment.from("댓글내용",author,post,parent);

// when
commentRepository.saveAll(List.of(parent, child));

// then
assertThat(parent.getParent()).isEqualTo(parent);
assertThat(child.getParent()).isEqualTo(parent);
}


}
Loading

0 comments on commit a93937d

Please sign in to comment.