@Transactional
public List<RandomMatch> createRandomMatch(String username,
RandomMatchDto randomMatchDto) {
LocalDateTime now = LocalDateTime.now();
verifyAlreadyApplied(randomMatchDto.getContentCategory(), member, now);
}
@Transactional
public List<RandomMatch> createRandomMatch(String username,
RandomMatchDto randomMatchDto, LocalDateTime now) {
verifyAlreadyApplied(randomMatchDto.getContentCategory(), member, now);
}
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class MatchService {
private finalMatchRepositorymatchRepository;
private finalUserRepositoryuserRepository;
private finalMemberRepositorymemberRepository;
private finalActivityRepositoryactivityRepository;
private finalMemberMappermemberMapper;
@Transactional
public ResponseEntity<Void> makeReview(String username, String matchId,
MatchReviewRequest request) {
verifyCallerParticipatedInMatch(username,matchId);
User caller = userRepository.findByUsername(username)
.orElseThrow(() ->
new NoEntityException(ErrorCode.ENTITY_NOT_FOUND));
verifyReviewedMemberInMatchAndNotCaller(matchId,request.getMemberReviewDtos().stream()
.map(MemberReviewDto::getNickname)
.collect(Collectors.toList()), caller);
Match match = matchRepository.findByApiId(matchId)
.orElseThrow(() ->
new NoEntityException(ErrorCode.ENTITY_NOT_FOUND));
match.review(caller.getMember());
//리뷰 작성자 참여 점수 추가.
Member memberReviewAuthor = memberRepository.findByNickname(username)
.orElseThrow(() ->
new NoEntityException(ErrorCode.ENTITY_NOT_FOUND));
activityRepository.save(
Activity.of(memberReviewAuthor,
match.getContentCategory(),
ActivityMatchScore.MAKE_MATCH_REVIEW));
/**
*리뷰에 따라 점수 추가
*/
List<Activity> activities =request.getMemberReviewDtos().stream()
.map((memberReviewDto) -> {
Member member = memberRepository.findByNickname(memberReviewDto.getNickname())
.orElseThrow(() ->
new NoEntityException(ErrorCode.ENTITY_NOT_FOUND));
return Activity.of(member, match.getContentCategory(),
memberReviewDto.getActivityMatchScore());
})
.collect(Collectors.toList());
activityRepository.saveAll(activities);
return ResponseEntity.ok().build();
}
private void verifyReviewedMemberInMatchAndNotCaller(StringmatchId,List<String>nicknames, Usercaller) {
Match match = matchRepository.findByApiId(matchId)
.orElseThrow(() ->
new NoEntityException(ErrorCode.ENTITY_NOT_FOUND));
Set<Member> memberSet = match.getMatchMembers()
.stream()
.map(MatchMember::getMember)
.collect(Collectors.toSet());
memberRepository.findAllByNicknameIn(nicknames)
.forEach((member) -> {
if (!memberSet.contains(member)) {
throw new BusinessException(ErrorCode.REVIEWED_MEMBER_NOT_IN_MATCH);
}else if(caller.getMember().equals(member)){
throw new BusinessException(ErrorCode.REVIEWING_SELF);
}
});
}
//자기 매치인지 확인
private void verifyCallerParticipatedInMatch(Usercaller, StringmatchId) {
Match match = matchRepository.findByApiId(matchId)
.orElseThrow(() ->
new NoEntityException(ErrorCode.ENTITY_NOT_FOUND));
if (!caller.getUserRoles().stream()
.map(UserRole::getRole)
.map(Role::getValue)
.collect(Collectors.toSet())
.contains(RoleEnum.ROLE_ADMIN) &&
!match.getMatchMembers().stream()
.map(MatchMember::getMember)
.collect(Collectors.toSet())
.contains(caller.getMember())) {
throw new BusinessException(ErrorCode.NOT_MATCH_PARTICIPATED);
}
}
}
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class MatchService {
private final MatchRepository matchRepository;
private final UserRepository userRepository;
private final MemberRepository memberRepository;
private final ActivityRepository activityRepository;
private final MemberMapper memberMapper;
@Transactional
public List<Activity> makeReview(String username, String matchId,
MatchReviewRequest request) {
Match match = matchRepository.findByApiId(matchId)
.orElseThrow(() ->
new NoEntityException(ErrorCode.ENTITY_NOT_FOUND));
User reviewingUser = getUserByUsernameOrException(username);
Map<String, ActivityMatchScore> nicknameActivityScoreMap = request.getMemberReviewDtos().stream()
.collect(Collectors.toMap(MemberReviewDto::getNickname,
MemberReviewDto::getActivityMatchScore));
List<Member> reviewedTargets = memberRepository.findAllByNicknameIn(
new ArrayList<>(nicknameActivityScoreMap.keySet()));
// request의 nickname이 member에 없는 경우
if (reviewedTargets.size() != nicknameActivityScoreMap.size()) {
throw new NoEntityException(ErrorCode.ENTITY_NOT_FOUND);
}
List<Activity> createdActivities = match.makeReview(reviewingUser.getMember(),
reviewedTargets.stream()
.collect(Collectors.toMap(m -> m,
m -> nicknameActivityScoreMap.get(m.getNickname()))));
return activityRepository.saveAll(createdActivities);
}
}
@Builder(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
@Table(name = "MATCHS", uniqueConstraints = {
@UniqueConstraint(name = "API_ID_UNIQUE", columnNames = {"apiId"}),
})
@Entity
public class Match extends BaseEntity {
/********************************* 비니지스 로직 *********************************/
public List<Activity> makeReview(Member reviewer, Map<Member, ActivityMatchScore> reviewedMemberScoreMap) {
verifyMemberParticipatedInMatchOrAdmin(reviewer);
verifyReviewedMemberIsInMatchAndNotReviewer(reviewer, reviewedMemberScoreMap.keySet());
//리뷰 작성자 매칭 참여 여부 true로 변경
matchMembers.stream()
.filter(mm ->
mm.getMember().equals(reviewer))
.findAny()
.orElseThrow(() ->
new IllegalArgumentException("해당 매치에 참여한 멤버가 아닙니다."))
.updateisReviewedToTrue();
List<Activity> activities = new ArrayList<>();
//리뷰 작성자 참여 점수 추가.
Activity reviewerActivity = Activity.of(reviewer,
contentCategory,
ActivityMatchScore.MAKE_MATCH_REVIEW);
activities.add(reviewerActivity);
// 리뷰에 따라 점수 추가
reviewedMemberScoreMap.forEach((member, score) -> {
Activity activity = Activity.of(member,
contentCategory,
score);
activities.add(activity);
});
return activities;
}
/**
* 자기가 참여한 매치인지 확인
* @param member
*/
public void verifyMemberParticipatedInMatchOrAdmin(Member member) {
if (member.getUser().hasRole(RoleEnum.ROLE_ADMIN)){
return ;
}
if (!matchMembers.stream()
.map(MatchMember::getMember)
.collect(Collectors.toSet())
.contains(member)) {
throw new BusinessException(ErrorCode.NOT_MATCH_PARTICIPATED);
}
}
public Boolean isMemberReviewingBefore(Member member){
return getMatchMembers().stream()
.filter((mm) ->
member.equals(mm.getMember())
).findFirst()
.orElseThrow(() ->
new IllegalArgumentException("해당 매치에 참여한 멤버가 아닙니다."))
.getIsReviewed();
}
/**
* 리뷰 대상자가 자기 자신이 아니고 매칭에 포함되어있는지 확인
* @param reviewer
* @param reviewedMemberSet
*/
private void verifyReviewedMemberIsInMatchAndNotReviewer(Member reviewer, Set<Member> reviewedMemberSet) {
Set<Member> memberSet = this.getMatchMembers()
.stream()
.map(MatchMember::getMember)
.collect(Collectors.toSet());
reviewedMemberSet
.forEach((member) -> {
if (!memberSet.contains(member)) {
throw new BusinessException(ErrorCode.REVIEWED_MEMBER_NOT_IN_MATCH);
}else if(reviewer.equals(member)){
throw new BusinessException(ErrorCode.REVIEWING_SELF);
}
});
}
}
@Builder.Default
@OneToMany(mappedBy = "match", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
private List<MatchMember> matchMembers = new ArrayList<>();
@Builder.Default
@OneToMany(mappedBy = "match", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
private List<MatchConditionMatch> matchConditionMatches = new ArrayList<>();
데이터가 남아있을 수 있고 스키마가 변경될 수 있기 때문에 테스트 시 테이블을 재생성하도록 한다.
이 경우 DB의 초기 데이터를 미리 생성해놓는 것은 힘들기 때문에 테스트 실행 시 항상 초기화 될 수 있도록 해준다.
@TestConfiguration
@Slf4j
@RequiredArgsConstructor
public class TestBootstrapConfig {
@Value("${spring.jpa.hibernate.data-loader}")
private Integer dataLoader;
private final BootstrapDataLoader bootstrapDataLoader;
@Transactional
@PostConstruct
public void initDB() {
if (dataLoader.equals(2)){
bootstrapDataLoader.createDefaultUsers();
bootstrapDataLoader.createMatchCondition();
}
}
}
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import({ArticleService.class, MemberMapperImpl.class,
Auditor.class, QuerydslConfig.class, JpaAndEntityPackagePathConfig.class,
TestBootstrapConfig.class, BootstrapDataLoader.class})
class ArticleServiceWithDaoTest {
@MockBean
private AlarmProducer alarmProducer;
@EnableJpaRepositories(basePackages = "partner42.modulecommon.repository")
@EntityScan(basePackages = "partner42.modulecommon.domain")
@Configuration
public class JpaAndEntityPackagePathConfig {
}
@TestConfiguration
@Configuration
@Import({Auditor.class, QuerydslConfig.class, JpaAndEntityPackagePathConfig.class,
TestBootstrapConfig.class, BootstrapDataLoader.class, BCryptPasswordEncoder.class})
public class ServiceWithDAOTestDefaultConfig {
}
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import({ArticleService.class, MemberMapperImpl.class,
ServiceWithDAOTestDefaultconfig.class})
class ArticleServiceWithDaoTest {
@MockBean
private AlarmProducer alarmProducer;
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import({ArticleService.class, MemberMapperImpl.class,
ServiceWithDAOTestDefaultConfig.class,
})
class ArticleServiceWithDaoTest {
@Autowired
private UserRepository userRepository;
@Autowired
private ArticleRepository articleRepository;
@Autowired
private ArticleService articleService;
@Autowired
private ArticleMemberRepository articleMemberRepository;
@Autowired
private ArticleMatchConditionRepository articleMatchConditionRepository;
@Autowired
private MatchConditionRepository matchConditionRepository;
@Autowired
private MatchRepository matchRepository;
@BeforeEach
void setUp() {
}
@Test
void createArticle() {
//given
User takim = userRepository.findByUsername("takim").get();
LocalDate date = LocalDate.now().plusDays(1);
ArticleDto dto = ArticleDto.builder()
.contentCategory(ContentCategory.MEAL)
.date(date)
.participantNumMax(3)
.content("content")
.title("title")
.anonymity(false)
.matchConditionDto(
MatchConditionDto.builder()
.placeList(List.of(Place.GAEPO, Place.SEOCHO))
.timeOfEatingList(List.of(TimeOfEating.DINNER, TimeOfEating.LUNCH))
.wayOfEatingList(List.of(WayOfEating.DELIVERY))
.typeOfStudyList(List.of(TypeOfStudy.INNER_CIRCLE))
.build()
).build();
//when
ArticleOnlyIdResponse response = articleService.createArticle(takim.getUsername(), dto);
Article article = articleRepository.findByApiIdAndIsDeletedIsFalse(response.getArticleId())
.get();
//then
assertThat(article).extracting(Article::getDate, Article::getTitle, Article::getContent,
Article::getAnonymity, Article::getIsComplete, Article::getParticipantNum,
Article::getParticipantNumMax, Article::getContentCategory)
.containsExactly(date, "title", "content", false, false, 1, 3, ContentCategory.MEAL);
assertThat(article.getArticleMatchConditions()).extracting(
ArticleMatchCondition::getMatchCondition)
.extracting("value", "conditionCategory")
.containsExactlyInAnyOrder(
tuple(Place.GAEPO.name(), ConditionCategory.Place),
tuple(Place.SEOCHO.name(), ConditionCategory.Place),
tuple(TimeOfEating.DINNER.name(), ConditionCategory.TimeOfEating),
tuple(TimeOfEating.LUNCH.name(), ConditionCategory.TimeOfEating),
tuple(WayOfEating.DELIVERY.name(), ConditionCategory.WayOfEating),
tuple(TypeOfStudy.INNER_CIRCLE.name(), ConditionCategory.TypeOfStudy)
);
assertThat(article.getArticleMembers())
.extracting(ArticleMember::getMember, ArticleMember::getIsAuthor)
.containsExactly(tuple(takim.getMember(), true));
}
@Test
void deleteArticle() {
User takim = userRepository.findByUsername("takim").get();
LocalDate date = LocalDate.now().plusDays(1);
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.MEAL));
ArticleMember am = articleMemberRepository.save(
ArticleMember.of(takim.getMember(), true, article));
ArticleMatchCondition amc = articleMatchConditionRepository.save(
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(),
article));
articleService.deleteArticle(takim.getUsername(), article.getApiId());
Optional<Article> optionalArticle = articleRepository.findByApiId(article.getApiId());
//then
assertThat(optionalArticle).isNotPresent();
}
@Test
void deleteArticle_whenNotAuthorUserDelete_thenThrow() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.MEAL));
ArticleMember am = articleMemberRepository.save(
ArticleMember.of(takim.getMember(), true, article));
ArticleMatchCondition amc = articleMatchConditionRepository.save(
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(),
article));
Optional<Article> optionalArticle = articleRepository.findByApiId(article.getApiId());
//then
assertThatThrownBy(() ->
articleService.deleteArticle(sorkim.getUsername(), article.getApiId()))
.isInstanceOf(InvalidInputException.class);
}
@Test
void softDelete() {
User takim = userRepository.findByUsername("takim").get();
LocalDate date = LocalDate.now().plusDays(1);
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.MEAL));
ArticleMember am = articleMemberRepository.save(
ArticleMember.of(takim.getMember(), true, article));
ArticleMatchCondition amc = articleMatchConditionRepository.save(
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(),
article));
articleService.softDelete(takim.getUsername(), article.getApiId());
//then
articleRepository.findByApiId(article.getApiId()).ifPresent(
(at) ->
assertThat(at).extracting(Article::getIsDeleted)
.isEqualTo(true)
);
}
@Test
void softDelete_whenNotAuthorUserDelete_thenThrow() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.MEAL));
ArticleMember am = articleMemberRepository.save(
ArticleMember.of(takim.getMember(), true, article));
ArticleMatchCondition amc = articleMatchConditionRepository.save(
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name())
.get(), article));
//then
assertThatThrownBy(() ->
articleService.softDelete(sorkim.getUsername(), article.getApiId()))
.isInstanceOf(InvalidInputException.class);
}
@Test
void updateArticle() {
//given
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
ArticleDto dto = ArticleDto.builder()
.contentCategory(ContentCategory.MEAL)
.date(date)
.participantNumMax(3)
.content("content")
.title("title")
.anonymity(false)
.matchConditionDto(
MatchConditionDto.builder()
.placeList(List.of(Place.GAEPO, Place.SEOCHO))
.timeOfEatingList(List.of(TimeOfEating.DINNER, TimeOfEating.LUNCH))
.wayOfEatingList(List.of(WayOfEating.DELIVERY))
.typeOfStudyList(List.of(TypeOfStudy.INNER_CIRCLE))
.build()
).build();
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", true, 4, ContentCategory.STUDY));
ArticleMember am = articleMemberRepository.save(
ArticleMember.of(takim.getMember(), true, article));
articleMatchConditionRepository.saveAll(
List.of(
ArticleMatchCondition.of(
matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(),
article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(),
article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article)
)
);
ArticleOnlyIdResponse articleOnlyIdResponse = articleService.updateArticle(dto,
takim.getUsername(), article.getApiId());
Article updatedArticle = articleRepository.findByApiId(articleOnlyIdResponse.getArticleId())
.get();
//then
assertThat(updatedArticle).extracting(Article::getDate, Article::getTitle,
Article::getContent,
Article::getAnonymity, Article::getIsComplete, Article::getParticipantNum,
Article::getParticipantNumMax, Article::getContentCategory)
.containsExactly(date, "title", "content", false, false, 1, 3, ContentCategory.MEAL);
assertThat(article.getArticleMatchConditions()).extracting(
ArticleMatchCondition::getMatchCondition)
.extracting(MatchCondition::getValue,
MatchCondition::getConditionCategory)
.containsExactlyInAnyOrder(
tuple(Place.GAEPO.name(), ConditionCategory.Place),
tuple(Place.SEOCHO.name(), ConditionCategory.Place),
tuple(TimeOfEating.DINNER.name(), ConditionCategory.TimeOfEating),
tuple(TimeOfEating.LUNCH.name(), ConditionCategory.TimeOfEating),
tuple(WayOfEating.DELIVERY.name(), ConditionCategory.WayOfEating),
tuple(TypeOfStudy.INNER_CIRCLE.name(), ConditionCategory.TypeOfStudy)
);
assertThat(article.getArticleMembers())
.extracting(ArticleMember::getMember, ArticleMember::getIsAuthor)
.containsExactly(tuple(takim.getMember(), true));
}
@Test
void updateArticle_whenNotAuthorUserUpdate_thenThrow() {
//given
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
ArticleDto dto = ArticleDto.builder()
.contentCategory(ContentCategory.MEAL)
.date(date)
.participantNumMax(3)
.content("content")
.title("title")
.anonymity(false)
.matchConditionDto(
MatchConditionDto.builder()
.placeList(List.of(Place.GAEPO, Place.SEOCHO))
.timeOfEatingList(List.of(TimeOfEating.DINNER, TimeOfEating.LUNCH))
.wayOfEatingList(List.of(WayOfEating.DELIVERY))
.typeOfStudyList(List.of(TypeOfStudy.INNER_CIRCLE))
.build()
).build();
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", true, 4, ContentCategory.STUDY));
ArticleMember am = articleMemberRepository.save(
ArticleMember.of(takim.getMember(), true, article));
articleMatchConditionRepository.saveAll(
List.of(
ArticleMatchCondition.of(
matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(),
article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(),
article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article)
)
);
//then
assertThatThrownBy(() ->
articleService.updateArticle(dto,
sorkim.getUsername(), article.getApiId()))
.isInstanceOf(InvalidInputException.class);
}
@Test
void readOneArticle() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.MEAL));
ArticleMember am = articleMemberRepository.save(
ArticleMember.of(takim.getMember(), true, article));
articleMatchConditionRepository.saveAll(
List.of(
ArticleMatchCondition.of(
matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(),
article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(),
article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article)
)
);
ArticleReadOneResponse articleReadOneResponse = articleService.readOneArticle(
takim.getUsername(), article.getApiId());
//then
assertThat(articleReadOneResponse)
.extracting(ArticleReadOneResponse::getArticleId, ArticleReadOneResponse::getUserId,
ArticleReadOneResponse::getTitle, ArticleReadOneResponse::getDate,
ArticleReadOneResponse::getCreatedAt, ArticleReadOneResponse::getContent,
ArticleReadOneResponse::getAnonymity, ArticleReadOneResponse::getIsToday,
ArticleReadOneResponse::getParticipantNumMax,
ArticleReadOneResponse::getParticipantNum,
ArticleReadOneResponse::getContentCategory)
.containsExactly(article.getApiId(), takim.getApiId(), "a", date,
article.getCreatedAt(), "a", false, false, 3, 1,
ContentCategory.MEAL);
assertThat(articleReadOneResponse)
.extracting(ArticleReadOneResponse::getMatchConditionDto)
.extracting(MatchConditionDto::getPlaceList,
MatchConditionDto::getTimeOfEatingList,
MatchConditionDto::getWayOfEatingList,
MatchConditionDto::getTypeOfStudyList
)
.usingRecursiveComparison().ignoringAllOverriddenEquals().ignoringCollectionOrder()
.isEqualTo(List.of(List.of(Place.GAEPO),
List.of(TimeOfEating.MIDNIGHT, TimeOfEating.BREAKFAST),
List.of(WayOfEating.TAKEOUT),
List.of()
));
assertThat(articleReadOneResponse)
.extracting(ArticleReadOneResponse::getParticipantsOrAuthor)
.usingRecursiveComparison().ignoringAllOverriddenEquals().ignoringCollectionOrder()
.isEqualTo(List.of(
MemberDto.builder()
.nickname(takim.getMember().getNickname())
.isAuthor(true)
.isMe(true)
));
}
@Test
void readOneArticle_whenReadByOtherUserOrNotAuthenticatedUser_thenIsMeFalse() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.MEAL));
ArticleMember am = articleMemberRepository.save(
ArticleMember.of(takim.getMember(), true, article));
articleMatchConditionRepository.saveAll(
List.of(
ArticleMatchCondition.of(
matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(),
article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(),
article),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article)
)
);
ArticleReadOneResponse articleReadOneResponseBySorkim = articleService.readOneArticle(
sorkim.getUsername(), article.getApiId());
ArticleReadOneResponse articleReadOneResponseByUnAuthenticated = articleService.readOneArticle(
sorkim.getUsername(), article.getApiId());
//then
assertThat(articleReadOneResponseBySorkim)
.extracting(ArticleReadOneResponse::getParticipantsOrAuthor)
.usingRecursiveComparison().ignoringAllOverriddenEquals().ignoringCollectionOrder()
.isEqualTo(List.of(
MemberDto.builder()
.nickname(takim.getMember().getNickname())
.isAuthor(true)
.isMe(false)
));
assertThat(articleReadOneResponseByUnAuthenticated)
.extracting(ArticleReadOneResponse::getParticipantsOrAuthor)
.usingRecursiveComparison().ignoringAllOverriddenEquals().ignoringCollectionOrder()
.isEqualTo(List.of(
MemberDto.builder()
.nickname(takim.getMember().getNickname())
.isAuthor(true)
.isMe(false)
));
}
@Test
void readAllArticle() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
//when
Article article1 = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.MEAL));
articleMemberRepository.save(
ArticleMember.of(takim.getMember(), true, article1));
articleMatchConditionRepository.saveAll(
List.of(
ArticleMatchCondition.of(
matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article1),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(),
article1),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(),
article1),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(),
article1)
)
);
Article article2 = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.STUDY));
articleMemberRepository.save(
ArticleMember.of(sorkim.getMember(), true, article2));
articleMatchConditionRepository.saveAll(
List.of(
ArticleMatchCondition.of(
matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article2),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(),
article2)
)
);
List<ArticleReadResponse> articleReadResponses = articleService.readAllArticle(
PageRequest.of(0, 10), new ArticleSearch()).getContent();
//then
assertThat(articleReadResponses)
.extracting(ArticleReadResponse::getNickname, ArticleReadResponse::getUserId,
ArticleReadResponse::getArticleId, ArticleReadResponse::getTitle,
ArticleReadResponse::getContent, ArticleReadResponse::getDate,
ArticleReadResponse::getCreatedAt, ArticleReadResponse::getAnonymity,
ArticleReadResponse::getIsComplete, ArticleReadResponse::getIsToday,
ArticleReadResponse::getParticipantNumMax,
ArticleReadResponse::getParticipantNum, ArticleReadResponse::getContentCategory)
.containsExactlyInAnyOrder(
tuple("takim", takim.getApiId(), article1.getApiId(), "a", "a", date,
article1.getCreatedAt(), false, false, false, 3, 1, ContentCategory.MEAL),
tuple("sorkim", sorkim.getApiId(), article2.getApiId(), "a", "a", date,
article2.getCreatedAt(), false, false, false, 3, 1, ContentCategory.STUDY)
);
assertThat(articleReadResponses)
.extracting(ArticleReadResponse::getMatchConditionDto)
.extracting(MatchConditionDto::getPlaceList, MatchConditionDto::getTimeOfEatingList,
MatchConditionDto::getWayOfEatingList, MatchConditionDto::getTypeOfStudyList)
.containsExactlyInAnyOrder(
tuple(
List.of(Place.GAEPO),
List.of(TimeOfEating.MIDNIGHT, TimeOfEating.BREAKFAST),
List.of(WayOfEating.TAKEOUT),
List.of()
),
tuple(
List.of(Place.GAEPO),
List.of(),
List.of(WayOfEating.TAKEOUT),
List.of()
)
);
}
@Test
void readAllArticle_whenPageSizeNearFindAllSize_thenSliceHasNext() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
//when
Article article1 = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.MEAL));
articleMemberRepository.save(
ArticleMember.of(takim.getMember(), true, article1));
articleMatchConditionRepository.saveAll(
List.of(
ArticleMatchCondition.of(
matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article1),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(),
article1),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(),
article1),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(),
article1)
)
);
Article article2 = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.STUDY));
articleMemberRepository.save(
ArticleMember.of(sorkim.getMember(), true, article2));
articleMatchConditionRepository.saveAll(
List.of(
ArticleMatchCondition.of(
matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article2),
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(),
article2)
)
);
SliceImpl<ArticleReadResponse> articleReadResponsesUnder = articleService.readAllArticle(
PageRequest.of(0, 1), new ArticleSearch());
SliceImpl<ArticleReadResponse> articleReadResponsesEquals = articleService.readAllArticle(
PageRequest.of(0, 2), new ArticleSearch());
SliceImpl<ArticleReadResponse> articleReadResponsesOver = articleService.readAllArticle(
PageRequest.of(0, 3), new ArticleSearch());
//then
assertThat(articleReadResponsesUnder.hasNext()).isTrue();
assertThat(articleReadResponsesEquals.hasNext()).isFalse();
assertThat(articleReadResponsesOver.hasNext()).isFalse();
}
@Test
void participateArticle() {
//given
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.MEAL));
ArticleMember.of(takim.getMember(), true, article);
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article);
ArticleOnlyIdResponse articleOnlyIdResponse = articleService.participateArticle(
sorkim.getUsername(), article.getApiId());
Article participatedArticle = articleRepository.findByApiId(
articleOnlyIdResponse.getArticleId()).get();
//then
assertThat(participatedArticle).extracting(Article::getParticipantNum)
.isEqualTo(2);
assertThat(participatedArticle.getArticleMembers())
.extracting(ArticleMember::getIsAuthor, ArticleMember::getMember)
.containsExactlyInAnyOrder(tuple(true, takim.getMember()),
tuple(false, sorkim.getMember()));
}
@Test
void participateArticle_verifyAlarmProducer_SendIsCalled() {
//given
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, 3, ContentCategory.MEAL));
ArticleMember.of(takim.getMember(), true, article);
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(),
article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article);
ArticleOnlyIdResponse articleOnlyIdResponse = articleService.participateArticle(
sorkim.getUsername(), article.getApiId());
Article participatedArticle = articleRepository.findByApiId(
articleOnlyIdResponse.getArticleId()).get();
//then
verify(alarmProducer).send(
new AlarmEvent(AlarmType.PARTICIPATION_ON_MY_POST, AlarmArgs.builder()
.opinionId(null)
.articleId(articleOnlyIdResponse.getArticleId())
.callingMemberNickname(sorkim.getMember().getNickname())
.build(), article.getAuthorMember().getUser().getId(), SseEventName.ALARM_LIST));
}
@Test
void participateArticle_whenArticleIsFullOrAlreadyParticipatedUser_thenThrow() {
//given
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
User hyenam = userRepository.findByUsername("hyenam").get();
LocalDate date = LocalDate.now().plusDays(1);
int participantNumMax = 2;
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, participantNumMax, ContentCategory.MEAL));
ArticleMember.of(takim.getMember(), true, article);
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article);
ArticleOnlyIdResponse articleOnlyIdResponse = articleService.participateArticle(
sorkim.getUsername(), article.getApiId());
//then
assertThatThrownBy(() -> articleService.participateArticle(sorkim.getUsername(), article.getApiId()))
.isInstanceOf(InvalidInputException.class);
assertThatThrownBy(() -> articleService.participateArticle(hyenam.getUsername(), article.getApiId()))
.isInstanceOf(BusinessException.class)
.hasMessage(ErrorCode.FULL_ARTICLE.getMessage());
}
@Test
void participateCancelArticle() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
int participantNumMax = 2;
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, participantNumMax, ContentCategory.MEAL));
ArticleMember.of(takim.getMember(), true, article);
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article);
ArticleOnlyIdResponse articleOnlyIdResponse = articleService.participateArticle(
sorkim.getUsername(), article.getApiId());
ArticleOnlyIdResponse articleOnlyIdResponseCancel = articleService.participateCancelArticle(
sorkim.getUsername(), article.getApiId());
Article participatedArticle = articleRepository.findByApiId(
articleOnlyIdResponse.getArticleId()).get();
//then
assertThat(participatedArticle).extracting(Article::getParticipantNum)
.isEqualTo(1);
assertThat(participatedArticle.getArticleMembers())
.extracting(ArticleMember::getIsAuthor, ArticleMember::getMember)
.containsExactlyInAnyOrder(tuple(true, takim.getMember()));
}
@Test
void participateCancelArticle_verifyAlarmProducer_sendIsCalled() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
LocalDate date = LocalDate.now().plusDays(1);
int participantNumMax = 2;
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, participantNumMax, ContentCategory.MEAL));
ArticleMember.of(takim.getMember(), true, article);
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(),
article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article);
ArticleOnlyIdResponse articleOnlyIdResponse = articleService.participateArticle(
sorkim.getUsername(), article.getApiId());
ArticleOnlyIdResponse articleOnlyIdResponseCancel = articleService.participateCancelArticle(
sorkim.getUsername(), article.getApiId());
//then
verify(alarmProducer).send(
new AlarmEvent(AlarmType.PARTICIPATION_CANCEL_ON_MY_POST, AlarmArgs.builder()
.opinionId(null)
.articleId(articleOnlyIdResponse.getArticleId())
.callingMemberNickname(sorkim.getMember().getNickname())
.build(), article.getAuthorMember().getUser().getId(), SseEventName.ALARM_LIST));
}
@Test
void participateCancelArticle_whenNotParticipatedUserCancel_thenThrow() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
User hyenam = userRepository.findByUsername("hyenam").get();
LocalDate date = LocalDate.now().plusDays(1);
int participantNumMax = 2;
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, participantNumMax, ContentCategory.MEAL));
ArticleMember.of(takim.getMember(), true, article);
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article);
articleService.participateArticle(sorkim.getUsername(), article.getApiId());
//then
assertThatThrownBy(() ->
articleService.participateCancelArticle(
hyenam.getUsername(), article.getApiId()))
.isInstanceOf(InvalidInputException.class)
.hasMessage(ErrorCode.NOT_PARTICIPATED_MEMBER.getMessage());
}
@Test
void participateCancelArticle_whenAuthorMember_thenThrow() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
User hyenam = userRepository.findByUsername("hyenam").get();
LocalDate date = LocalDate.now().plusDays(1);
int participantNumMax = 2;
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, participantNumMax, ContentCategory.MEAL));
ArticleMember.of(takim.getMember(), true, article);
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article);
//then
assertThatThrownBy(() ->
articleService.participateCancelArticle(
takim.getUsername(), article.getApiId()))
.isInstanceOf(InvalidInputException.class)
.hasMessage(ErrorCode.NOT_ALLOW_AUTHOR_MEMBER_DELETE.getMessage());
}
@Test
void completeArticle() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
User hyenam = userRepository.findByUsername("hyenam").get();
LocalDate date = LocalDate.now().plusDays(1);
int participantNumMax = 3;
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, participantNumMax, ContentCategory.MEAL));
ArticleMember.of(takim.getMember(), true, article);
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article);
articleService.participateArticle(sorkim.getUsername(), article.getApiId());
EmailDto<MatchOnlyIdResponse> matchOnlyIdResponseEmailDto = articleService.completeArticle(
takim.getUsername(), article.getApiId());
Match match = matchRepository.findByApiId(
matchOnlyIdResponseEmailDto.getResponse().getMatchId()).get();
//then
assertThat(match).extracting(Match::getMatchStatus, Match::getContentCategory,
Match::getMethodCategory, Match::getParticipantNum)
.containsExactly(MatchStatus.MATCHED, ContentCategory.MEAL, MethodCategory.MANUAL,
2);
assertThat(match.getMatchMembers())
.extracting(MatchMember::getMember, MatchMember::getIsAuthor,
MatchMember::getIsReviewed)
.containsExactly(
tuple(takim.getMember(), true, false),
tuple(sorkim.getMember(), false, false)
);
assertThat(match.getMatchConditionMatches())
.extracting(MatchConditionMatch::getMatchCondition)
.extracting(MatchCondition::getValue)
.containsExactlyInAnyOrder(
Place.GAEPO.name(),
TimeOfEating.MIDNIGHT.name(),
TimeOfEating.BREAKFAST.name(),
WayOfEating.TAKEOUT.name()
);
assertThat(matchOnlyIdResponseEmailDto.getEmails())
.containsExactlyInAnyOrder(
takim.getEmail(),
sorkim.getEmail()
);
}
@Test
void completeArticle_whenCompleteTwice_thenThrow() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
User hyenam = userRepository.findByUsername("hyenam").get();
LocalDate date = LocalDate.now().plusDays(1);
int participantNumMax = 3;
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, participantNumMax, ContentCategory.MEAL));
ArticleMember.of(takim.getMember(), true, article);
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(),
article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article);
EmailDto<MatchOnlyIdResponse> matchOnlyIdResponseEmailDto = articleService.completeArticle(
takim.getUsername(), article.getApiId());
assertThatThrownBy(() ->
articleService.completeArticle(
takim.getUsername(), article.getApiId()))
.isInstanceOf(BusinessException.class)
.hasMessage(ErrorCode.COMPLETED_ARTICLE.getMessage());
}
@Test
void completeArticle_whenNotAuthorComplete_thenThrow() {
User takim = userRepository.findByUsername("takim").get();
User sorkim = userRepository.findByUsername("sorkim").get();
User hyenam = userRepository.findByUsername("hyenam").get();
LocalDate date = LocalDate.now().plusDays(1);
int participantNumMax = 3;
//when
Article article = articleRepository.save(
Article.of(date, "a", "a", false, participantNumMax, ContentCategory.MEAL));
ArticleMember.of(takim.getMember(), true, article);
ArticleMatchCondition.of(matchConditionRepository.findByValue(Place.GAEPO.name()).get(),
article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.MIDNIGHT.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(TimeOfEating.BREAKFAST.name()).get(), article);
ArticleMatchCondition.of(
matchConditionRepository.findByValue(WayOfEating.TAKEOUT.name()).get(), article);
articleService.participateArticle(sorkim.getUsername(), article.getApiId());
//then
assertThatThrownBy(() ->
articleService.completeArticle(
sorkim.getUsername(), article.getApiId()))
.isInstanceOf(InvalidInputException.class)
.hasMessage(ErrorCode.NOT_ARTICLE_AUTHOR.getMessage());
}
}
@Slf4j
@ExtendWith(MockitoExtension.class)
class RandomMatchServiceTest {
@InjectMocks
private RandomMatchService randomMatchService;
@Mock
private RandomMatchRepository randomMatchRepository;
@Mock
private UserRepository userRepository;
@BeforeEach
void setUp() {
}
@Test
void createRandomMatch_whenMatchConditionRandomMatchDtoProper_ThenRandomMatchList()
throws Exception {
//given
User takim = User.of("takim", null, null, null, null, Member.of("takim"));
LocalDateTime now = CustomTimeUtils.nowWithoutNano();
String username = "takim";
RandomMatchDto randomMatchDto = RandomMatchDto.builder()
.contentCategory(ContentCategory.MEAL)
.matchConditionRandomMatchDto(MatchConditionRandomMatchDto.builder()
.placeList(List.of(Place.GAEPO))
.wayOfEatingList(List.of(WayOfEating.DELIVERY))
.build())
.build();
RandomMatchDto randomMatchDtoEmptyList = RandomMatchDto.builder()
.contentCategory(ContentCategory.MEAL)
.matchConditionRandomMatchDto(MatchConditionRandomMatchDto.builder()
.placeList(List.of(Place.GAEPO))
.wayOfEatingList(List.of())
.build())
.build();
RandomMatchDto randomMatchStudyDto = RandomMatchDto.builder()
.contentCategory(ContentCategory.STUDY)
.matchConditionRandomMatchDto(MatchConditionRandomMatchDto.builder()
.placeList(List.of(Place.GAEPO))
.typeOfStudyList(List.of(TypeOfStudy.INNER_CIRCLE))
.build())
.build();
RandomMatchDto randomMatchStudyDtoEmptyList = RandomMatchDto.builder()
.contentCategory(ContentCategory.STUDY)
.matchConditionRandomMatchDto(MatchConditionRandomMatchDto.builder()
.placeList(List.of(Place.GAEPO))
.typeOfStudyList(List.of())
.build())
.build();
given(randomMatchRepository.findByCreatedAtAfterAndIsExpiredAndMemberIdAndContentCategory(
any())).willReturn(List.of());
given(userRepository.findByUsername(anyString())).willReturn(Optional.ofNullable(takim));
//when
List<RandomMatch> randomMatches = randomMatchService.createRandomMatch(username,
randomMatchDto, now);
List<RandomMatch> randomMatchesWithEmptyList = randomMatchService.createRandomMatch(
username, randomMatchDtoEmptyList, now);
List<RandomMatch> randomMatchesStudy = randomMatchService.createRandomMatch(username,
randomMatchStudyDto, now);
List<RandomMatch> randomMatchesStudyWithEmptyList = randomMatchService.createRandomMatch(
username, randomMatchStudyDtoEmptyList, now);
//then
assertThat(randomMatches).hasSize(1);
//모든 필드와 객체가 적절하게 생성되는지
assertThat(randomMatches).extracting(RandomMatch::getIsExpired)
.containsExactly(false);
assertThat(randomMatches).extracting(RandomMatch::getRandomMatchCondition)
.extracting(RandomMatchCondition::getPlace, RandomMatchCondition::getContentCategory,
RandomMatchCondition::getTypeOfStudy, RandomMatchCondition::getWayOfEating)
.containsExactlyInAnyOrder(
tuple(Place.GAEPO, ContentCategory.MEAL, null, WayOfEating.DELIVERY)
);
//조건 필드가 빈 List인 경우 모든 조건으로 RandomMatch반환.
assertThat(randomMatchesWithEmptyList).extracting(RandomMatch::getRandomMatchCondition)
.extracting(RandomMatchCondition::getWayOfEating)
.containsOnly(WayOfEating.values());
//study(다른 ContentCategory)
assertThat(randomMatchesStudy).extracting(RandomMatch::getIsExpired)
.containsExactly(false);
assertThat(randomMatchesStudy).extracting(RandomMatch::getRandomMatchCondition)
.extracting(RandomMatchCondition::getPlace, RandomMatchCondition::getContentCategory,
RandomMatchCondition::getTypeOfStudy, RandomMatchCondition::getWayOfEating)
.containsExactlyInAnyOrder(
tuple(Place.GAEPO, ContentCategory.STUDY, TypeOfStudy.INNER_CIRCLE, null)
);
//조건 필드가 빈 List인 경우 모든 조건으로 RandomMatch반환.
assertThat(randomMatchesStudyWithEmptyList).extracting(RandomMatch::getRandomMatchCondition)
.extracting(RandomMatchCondition::getTypeOfStudy)
.containsOnly(TypeOfStudy.values());
}
@Test
void createRandomMatch_whenUsernameNotExist_ThenThrowException() throws Exception {
//given
User takim = User.of("takim", null, null, null, null, Member.of("takim"));
LocalDateTime now = CustomTimeUtils.nowWithoutNano();
RandomMatchDto randomMatchDto = RandomMatchDto.builder()
.contentCategory(ContentCategory.MEAL)
.matchConditionRandomMatchDto(MatchConditionRandomMatchDto.builder()
.placeList(List.of(Place.GAEPO))
.wayOfEatingList(List.of(WayOfEating.DELIVERY))
.build())
.build();
String notExistUsername = "notExistUsername";
given(userRepository.findByUsername(anyString())).willReturn(Optional.ofNullable(takim));
given(userRepository.findByUsername(notExistUsername)).willReturn(Optional.empty());
//when
//then
assertThatThrownBy(() ->
randomMatchService.createRandomMatch(notExistUsername, randomMatchDto, now))
.isInstanceOf(NoEntityException.class);
}
@Test
void createRandomMatch_whenSameUserAlreadyApplySameCategoryRandomMatch_ThenException()
throws Exception {
//given
Member takimMember = Member.of("takim");
User takim = User.of("takim", null, null, null, null, takimMember);
takimMember.setId(1L);
Member unknownMember = Member.of("unknown");
User unknown = User.of("unknown", null, null, null, null, unknownMember);
unknownMember.setId(2L);
LocalDateTime now = CustomTimeUtils.nowWithoutNano();
RandomMatchDto randomMatchDto = RandomMatchDto.builder()
.contentCategory(ContentCategory.MEAL)
.matchConditionRandomMatchDto(MatchConditionRandomMatchDto.builder()
.placeList(List.of(Place.GAEPO))
.wayOfEatingList(List.of(WayOfEating.DELIVERY))
.build())
.build();
RandomMatchDto randomMatchDtoEmptyList = RandomMatchDto.builder()
.contentCategory(ContentCategory.MEAL)
.matchConditionRandomMatchDto(MatchConditionRandomMatchDto.builder()
.placeList(List.of(Place.GAEPO))
.wayOfEatingList(List.of())
.build())
.build();
RandomMatchDto randomMatchStudyDto = RandomMatchDto.builder()
.contentCategory(ContentCategory.STUDY)
.matchConditionRandomMatchDto(MatchConditionRandomMatchDto.builder()
.placeList(List.of(Place.GAEPO))
.typeOfStudyList(List.of(TypeOfStudy.INNER_CIRCLE))
.build())
.build();
RandomMatchDto randomMatchStudyDtoEmptyList = RandomMatchDto.builder()
.contentCategory(ContentCategory.STUDY)
.matchConditionRandomMatchDto(MatchConditionRandomMatchDto.builder()
.placeList(List.of(Place.GAEPO))
.typeOfStudyList(List.of())
.build())
.build();
given(
randomMatchRepository.findByCreatedAtAfterAndIsExpiredAndMemberIdAndContentCategory(
RandomMatchSearch.builder()
.contentCategory(ContentCategory.MEAL)
.memberId(takimMember.getId())
.isExpired(false)
.createdAt(now.minusMinutes(30))
.build()))
.willReturn(List.of()).willReturn(
List.of(RandomMatch.of(null, null)));
given(
randomMatchRepository.findByCreatedAtAfterAndIsExpiredAndMemberIdAndContentCategory(
RandomMatchSearch.builder()
.contentCategory(ContentCategory.STUDY)
.memberId(takimMember.getId())
.isExpired(false)
.createdAt(now.minusMinutes(30))
.build()))
.willReturn(List.of()).willReturn(
List.of(RandomMatch.of(null, null)));
given(
randomMatchRepository.findByCreatedAtAfterAndIsExpiredAndMemberIdAndContentCategory(
RandomMatchSearch.builder()
.contentCategory(ContentCategory.MEAL)
.memberId(unknownMember.getId())
.isExpired(false)
.createdAt(now.minusMinutes(30))
.build()))
.willReturn(List.of()).willReturn(
List.of(RandomMatch.of(null, null)));
given(userRepository.findByUsername("takim")).willReturn(Optional.ofNullable(takim));
given(userRepository.findByUsername("unknown")).willReturn(Optional.ofNullable(unknown));
//when
List<RandomMatch> randomMatches = randomMatchService.createRandomMatch(takim.getUsername(),
randomMatchDto, now);
//then
assertThatThrownBy(() ->
randomMatchService.createRandomMatch(takim.getUsername(), randomMatchDto, now))
.isInstanceOf(RandomMatchAlreadyExistException.class);
assertThatThrownBy(() ->
randomMatchService.createRandomMatch(takim.getUsername(), randomMatchDtoEmptyList, now))
.isInstanceOf(RandomMatchAlreadyExistException.class);
assertThatNoException().isThrownBy(() -> randomMatchService.createRandomMatch(
takim.getUsername(),
randomMatchStudyDto, now));
assertThatThrownBy(() ->
randomMatchService.createRandomMatch(takim.getUsername(),
randomMatchStudyDto, now))
.isInstanceOf(RandomMatchAlreadyExistException.class);
assertThatNoException().isThrownBy(() ->
randomMatchService.createRandomMatch(unknown.getUsername(),
randomMatchDto, now));
assertThatThrownBy(() ->
randomMatchService.createRandomMatch(unknown.getUsername(),
randomMatchDto, now)
)
.isInstanceOf(RandomMatchAlreadyExistException.class);
}
}