diff --git a/src/main/java/usw/suwiki/domain/evaluatepost/domain/EvaluatePost.java b/src/main/java/usw/suwiki/domain/evaluatepost/domain/EvaluatePost.java index 7f52e9dc..ee7c16b2 100644 --- a/src/main/java/usw/suwiki/domain/evaluatepost/domain/EvaluatePost.java +++ b/src/main/java/usw/suwiki/domain/evaluatepost/domain/EvaluatePost.java @@ -1,6 +1,5 @@ package usw.suwiki.domain.evaluatepost.domain; -import java.time.LocalDateTime; import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; @@ -13,7 +12,6 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.data.annotation.LastModifiedDate; import usw.suwiki.domain.evaluatepost.controller.dto.EvaluatePostSaveDto; import usw.suwiki.domain.evaluatepost.controller.dto.EvaluatePostUpdateDto; import usw.suwiki.domain.lecture.domain.Lecture; @@ -50,8 +48,6 @@ public class EvaluatePost extends BaseTimeEntity { @JoinColumn(name = "user_idx") private User user; - @LastModifiedDate - private LocalDateTime modifiedDate; @Column(columnDefinition = "TEXT", nullable = false) private String content; //주관적인 강의평가 입력내용 diff --git a/src/main/java/usw/suwiki/domain/exampost/domain/ExamPost.java b/src/main/java/usw/suwiki/domain/exampost/domain/ExamPost.java index 08afb50b..4b2b8354 100644 --- a/src/main/java/usw/suwiki/domain/exampost/domain/ExamPost.java +++ b/src/main/java/usw/suwiki/domain/exampost/domain/ExamPost.java @@ -1,19 +1,23 @@ package usw.suwiki.domain.exampost.domain; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.data.annotation.LastModifiedDate; -import usw.suwiki.domain.exampost.controller.dto.ExamPostsSaveDto; import usw.suwiki.domain.exampost.controller.dto.ExamPostUpdateDto; +import usw.suwiki.domain.exampost.controller.dto.ExamPostsSaveDto; import usw.suwiki.domain.lecture.domain.Lecture; import usw.suwiki.domain.user.user.User; import usw.suwiki.global.BaseTimeEntity; -import javax.persistence.*; -import java.time.LocalDateTime; - @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity @@ -30,9 +34,6 @@ public class ExamPost extends BaseTimeEntity { private String examInfo; //시험방식 private String examDifficulty; //난이도 - @LastModifiedDate - private LocalDateTime modifiedDate; - @Column(columnDefinition = "TEXT", nullable = false) private String content; diff --git a/src/main/java/usw/suwiki/domain/lecture/domain/Lecture.java b/src/main/java/usw/suwiki/domain/lecture/domain/Lecture.java index b8e668a0..a93121c9 100644 --- a/src/main/java/usw/suwiki/domain/lecture/domain/Lecture.java +++ b/src/main/java/usw/suwiki/domain/lecture/domain/Lecture.java @@ -1,6 +1,5 @@ package usw.suwiki.domain.lecture.domain; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; @@ -16,7 +15,6 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.data.annotation.LastModifiedDate; import usw.suwiki.domain.evaluatepost.domain.EvaluatePost; import usw.suwiki.domain.evaluatepost.service.dto.EvaluatePostsToLecture; import usw.suwiki.global.BaseTimeEntity; @@ -52,8 +50,6 @@ public class Lecture extends BaseTimeEntity { private int postsCount = 0; - @LastModifiedDate - private LocalDateTime modifiedDate; @OneToMany(mappedBy = "lecture", cascade = CascadeType.ALL, orphanRemoval = true) private List evaluatePostList = new ArrayList<>(); diff --git a/src/main/java/usw/suwiki/domain/notice/domain/Notice.java b/src/main/java/usw/suwiki/domain/notice/domain/Notice.java index 9b12161c..1b8417c3 100644 --- a/src/main/java/usw/suwiki/domain/notice/domain/Notice.java +++ b/src/main/java/usw/suwiki/domain/notice/domain/Notice.java @@ -1,16 +1,13 @@ package usw.suwiki.domain.notice.domain; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.springframework.data.annotation.LastModifiedDate; -import usw.suwiki.domain.notice.controller.dto.NoticeSaveOrUpdateDto; -import usw.suwiki.global.BaseTimeEntity; - import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; -import java.time.LocalDateTime; +import lombok.Getter; +import lombok.NoArgsConstructor; +import usw.suwiki.domain.notice.controller.dto.NoticeSaveOrUpdateDto; +import usw.suwiki.global.BaseTimeEntity; @Getter @NoArgsConstructor @@ -20,9 +17,6 @@ public class Notice extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @LastModifiedDate - private LocalDateTime modifiedDate; - private String title; private String content; diff --git a/src/main/java/usw/suwiki/domain/version/controller/ClientAppVersionController.java b/src/main/java/usw/suwiki/domain/version/controller/ClientAppVersionController.java new file mode 100644 index 00000000..a1675a8c --- /dev/null +++ b/src/main/java/usw/suwiki/domain/version/controller/ClientAppVersionController.java @@ -0,0 +1,27 @@ +package usw.suwiki.domain.version.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import usw.suwiki.domain.version.dto.response.CheckUpdateMandatoryResponse; +import usw.suwiki.domain.version.service.ClientAppVersionService; + +@Slf4j +@RestController +@RequestMapping("/client/version") +@RequiredArgsConstructor +public class ClientAppVersionController { + private final ClientAppVersionService clientAppVersionService; + + @GetMapping("/update-mandatory") + public ResponseEntity checkIsUpdateMandatory( + @RequestParam String os, + @RequestParam Integer versionCode + ) { + return ResponseEntity.ok(clientAppVersionService.checkIsUpdateMandatory(os, versionCode)); + } +} diff --git a/src/main/java/usw/suwiki/domain/version/dto/response/CheckUpdateMandatoryResponse.java b/src/main/java/usw/suwiki/domain/version/dto/response/CheckUpdateMandatoryResponse.java new file mode 100644 index 00000000..ac1162b4 --- /dev/null +++ b/src/main/java/usw/suwiki/domain/version/dto/response/CheckUpdateMandatoryResponse.java @@ -0,0 +1,19 @@ +package usw.suwiki.domain.version.dto.response; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class CheckUpdateMandatoryResponse { + private final Boolean isUpdateMandatory; + + public static CheckUpdateMandatoryResponse from(boolean isUpdateMandatory) { + return CheckUpdateMandatoryResponse.builder() + .isUpdateMandatory(isUpdateMandatory) + .build(); + } +} diff --git a/src/main/java/usw/suwiki/domain/version/entity/ClientAppVersion.java b/src/main/java/usw/suwiki/domain/version/entity/ClientAppVersion.java new file mode 100644 index 00000000..560e8877 --- /dev/null +++ b/src/main/java/usw/suwiki/domain/version/entity/ClientAppVersion.java @@ -0,0 +1,70 @@ +package usw.suwiki.domain.version.entity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import usw.suwiki.global.BaseTimeEntity; +import usw.suwiki.global.exception.ExceptionType; +import usw.suwiki.global.exception.errortype.VersionException; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(uniqueConstraints = { + @UniqueConstraint( + name = "UNIQUE_OS_AND_VERSION_CODE", + columnNames = {"os", "version_code"} + ) +}) +public class ClientAppVersion extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "client_app_version_id") + private Long id; + + @Enumerated(EnumType.STRING) + @NotNull + @Column(name = "os") + private ClientOS os; + + @NotNull + @Min(value = 0) + @Column(name = "version_code") + private Integer versionCode; + + @NotNull + private Boolean isVital; + + @Size(max = 2000) + private String description; + + + @Builder + public ClientAppVersion(ClientOS os, Integer versionCode, Boolean isVital, String description) { + this.os = os; + this.versionCode = versionCode; + this.isVital = isVital; + this.description = description; + } + + public boolean judgeIsUpdateMandatory(ClientOS os, Integer otherVersionCode) { + if (!this.os.equals(os)) { + throw new VersionException(ExceptionType.SERVER_ERROR); + } + return this.isVital && this.versionCode > otherVersionCode; + } +} diff --git a/src/main/java/usw/suwiki/domain/version/entity/ClientOS.java b/src/main/java/usw/suwiki/domain/version/entity/ClientOS.java new file mode 100644 index 00000000..f6cd908d --- /dev/null +++ b/src/main/java/usw/suwiki/domain/version/entity/ClientOS.java @@ -0,0 +1,35 @@ +package usw.suwiki.domain.version.entity; + +import java.util.Arrays; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import usw.suwiki.global.exception.ExceptionType; +import usw.suwiki.global.exception.errortype.VersionException; +import usw.suwiki.global.util.enums.KeyValueEnumModel; + +@RequiredArgsConstructor +public enum ClientOS implements KeyValueEnumModel { + ANDROID("ANDROID"), IOS("IOS"), WEB("WEB"); + + private final String value; + + public static ClientOS ofString(String param) { + if (Objects.isNull(param)) { + throw new VersionException(ExceptionType.INVALID_CLIENT_OS); + } + return Arrays.stream(ClientOS.values()) + .filter(v -> v.getValue().equals(param.toUpperCase())) + .findFirst() + .orElseThrow(() -> new VersionException(ExceptionType.COMMON_CLIENT_ERROR)); + } + + @Override + public String getKey() { + return name(); + } + + @Override + public String getValue() { + return value; + } +} diff --git a/src/main/java/usw/suwiki/domain/version/repository/ClientAppVersionRepository.java b/src/main/java/usw/suwiki/domain/version/repository/ClientAppVersionRepository.java new file mode 100644 index 00000000..d0c864f8 --- /dev/null +++ b/src/main/java/usw/suwiki/domain/version/repository/ClientAppVersionRepository.java @@ -0,0 +1,11 @@ +package usw.suwiki.domain.version.repository; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import usw.suwiki.domain.version.entity.ClientAppVersion; +import usw.suwiki.domain.version.entity.ClientOS; + +public interface ClientAppVersionRepository extends JpaRepository { + Optional findFirstByOsAndIsVitalTrueOrderByVersionCodeDesc(ClientOS os); + +} diff --git a/src/main/java/usw/suwiki/domain/version/service/ClientAppVersionService.java b/src/main/java/usw/suwiki/domain/version/service/ClientAppVersionService.java new file mode 100644 index 00000000..a6503c74 --- /dev/null +++ b/src/main/java/usw/suwiki/domain/version/service/ClientAppVersionService.java @@ -0,0 +1,31 @@ +package usw.suwiki.domain.version.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import usw.suwiki.domain.version.dto.response.CheckUpdateMandatoryResponse; +import usw.suwiki.domain.version.entity.ClientAppVersion; +import usw.suwiki.domain.version.entity.ClientOS; +import usw.suwiki.domain.version.repository.ClientAppVersionRepository; +import usw.suwiki.global.exception.ExceptionType; +import usw.suwiki.global.exception.errortype.VersionException; + + +@Slf4j +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class ClientAppVersionService { + private final ClientAppVersionRepository clientAppVersionRepository; + + public CheckUpdateMandatoryResponse checkIsUpdateMandatory(String os, int versionCode) { + ClientOS clientOS = ClientOS.ofString(os); + ClientAppVersion clientAppVersion = clientAppVersionRepository + .findFirstByOsAndIsVitalTrueOrderByVersionCodeDesc(clientOS) + .orElseThrow(() -> new VersionException(ExceptionType.SERVER_ERROR)); + + boolean isUpdateMandatory = clientAppVersion.judgeIsUpdateMandatory(clientOS, versionCode); + return CheckUpdateMandatoryResponse.from(isUpdateMandatory); + } +} diff --git a/src/main/java/usw/suwiki/global/BaseTimeEntity.java b/src/main/java/usw/suwiki/global/BaseTimeEntity.java index ff07fd16..8ea81023 100644 --- a/src/main/java/usw/suwiki/global/BaseTimeEntity.java +++ b/src/main/java/usw/suwiki/global/BaseTimeEntity.java @@ -1,19 +1,22 @@ package usw.suwiki.global; +import java.time.LocalDateTime; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; import lombok.Getter; import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import javax.persistence.EntityListeners; -import javax.persistence.MappedSuperclass; -import java.time.LocalDateTime; - @Getter -@MappedSuperclass //Jpa Entity 클래스들이 BastTimeEntity 를 상속할 경우, 필드들도 칼럼으로 인식된다. -@EntityListeners(AuditingEntityListener.class) //Auditing 기능을 포함시킨다. +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) public abstract class BaseTimeEntity { - @CreatedDate // Entity가 생성되어 저장할 때 시간이 자동 저장된다. + @CreatedDate private LocalDateTime createDate; + + @LastModifiedDate + private LocalDateTime modifiedDate; } diff --git a/src/main/java/usw/suwiki/global/exception/ExceptionType.java b/src/main/java/usw/suwiki/global/exception/ExceptionType.java index 0f0f74c5..e565e230 100644 --- a/src/main/java/usw/suwiki/global/exception/ExceptionType.java +++ b/src/main/java/usw/suwiki/global/exception/ExceptionType.java @@ -114,6 +114,14 @@ public enum ExceptionType { INVALID_TIMETABLE_CELL_SCHEDULE("TIMETABLE210", "유효하지 않은 셀 스케줄입니다.", BAD_REQUEST), OVERLAPPED_TIMETABLE_CELL_SCHEDULE("TIMETABLE211", "시간표에 중복되는 요일-교시입니다.", CONFLICT), + + /** + * Domain : Client Version + */ + CLIENT_VERSION_NOT_FOUND("VERSION001", "존재하지 않는 클라이언트 버전입니다.", NOT_FOUND), + INVALID_CLIENT_OS("VERSION110", "유효하지 않은 클라이언트 OS 입니다.", BAD_REQUEST), + + /** * 공통 */ diff --git a/src/main/java/usw/suwiki/global/exception/errortype/VersionException.java b/src/main/java/usw/suwiki/global/exception/errortype/VersionException.java new file mode 100644 index 00000000..0cbed338 --- /dev/null +++ b/src/main/java/usw/suwiki/global/exception/errortype/VersionException.java @@ -0,0 +1,10 @@ +package usw.suwiki.global.exception.errortype; + +import usw.suwiki.global.exception.ExceptionType; + +public class VersionException extends BaseException { + public VersionException(ExceptionType exceptionType) { + super(exceptionType); + } + +} diff --git a/src/test/java/usw/suwiki/global/annotation/SuwikiJpaTest.java b/src/test/java/usw/suwiki/global/annotation/SuwikiJpaTest.java new file mode 100644 index 00000000..0d09d09f --- /dev/null +++ b/src/test/java/usw/suwiki/global/annotation/SuwikiJpaTest.java @@ -0,0 +1,23 @@ +package usw.suwiki.global.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +import usw.suwiki.config.TestJpaConfig; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) + +@DataJpaTest +@Import(TestJpaConfig.class) +@TestMethodOrder(MethodOrderer.DisplayName.class) +@AutoConfigureTestDatabase(replace = Replace.NONE) +public @interface SuwikiJpaTest { +} diff --git a/src/test/java/usw/suwiki/global/annotation/SuwikiMockitoTest.java b/src/test/java/usw/suwiki/global/annotation/SuwikiMockitoTest.java new file mode 100644 index 00000000..a52384a7 --- /dev/null +++ b/src/test/java/usw/suwiki/global/annotation/SuwikiMockitoTest.java @@ -0,0 +1,18 @@ +package usw.suwiki.global.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) + +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.DisplayName.class) +public @interface SuwikiMockitoTest { +} diff --git a/src/test/java/usw/suwiki/repository/clientappversion/ClientAppVersionRepositoryTest.java b/src/test/java/usw/suwiki/repository/clientappversion/ClientAppVersionRepositoryTest.java new file mode 100644 index 00000000..35e4ca5e --- /dev/null +++ b/src/test/java/usw/suwiki/repository/clientappversion/ClientAppVersionRepositoryTest.java @@ -0,0 +1,158 @@ +package usw.suwiki.repository.clientappversion; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Optional; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.validation.ConstraintViolationException; +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.dao.DataIntegrityViolationException; +import usw.suwiki.domain.version.entity.ClientAppVersion; +import usw.suwiki.domain.version.entity.ClientOS; +import usw.suwiki.domain.version.repository.ClientAppVersionRepository; +import usw.suwiki.global.annotation.SuwikiJpaTest; +import usw.suwiki.global.exception.errortype.VersionException; + +@SuwikiJpaTest +public class ClientAppVersionRepositoryTest { + @Autowired + ClientAppVersionRepository clientAppVersionRepository; + + @PersistenceContext + private EntityManager entityManager; + + private ClientAppVersion dummyClientAppVersionFirst; + private ClientAppVersion dummyClientAppVersionSecond; + private ClientAppVersion dummyClientAppVersionThird; + + + @BeforeEach + public void setUp() { + ClientAppVersion androidVersion1 = ClientAppVersion.builder() + .os(ClientOS.ANDROID) + .versionCode(1) + .isVital(true) + .build(); + ClientAppVersion androidVersion2 = ClientAppVersion.builder() + .os(ClientOS.ANDROID) + .versionCode(2) + .isVital(true) + .build(); + ClientAppVersion androidVersion3 = ClientAppVersion.builder() + .os(ClientOS.ANDROID) + .versionCode(3) + .isVital(false) + .build(); + + this.dummyClientAppVersionFirst = clientAppVersionRepository.save(androidVersion1); + this.dummyClientAppVersionSecond = clientAppVersionRepository.save(androidVersion2); + this.dummyClientAppVersionThird = clientAppVersionRepository.save(androidVersion3); + + entityManager.clear(); + } + + + @Test + @DisplayName("클라이언트 앱 버전 생성") + public void CLIENT_APP_VERSION_CREATE() { + // given + ClientAppVersion iosAppVersion = ClientAppVersion.builder() + .os(ClientOS.IOS) + .versionCode(1) + .isVital(true) + .build(); + + // when + ClientAppVersion saved = clientAppVersionRepository.save(iosAppVersion); + entityManager.clear(); + Optional found = clientAppVersionRepository.findById(saved.getId()); + + // then + assertThat(found).isPresent(); + assertThat(found.get().getOs()).isEqualTo(ClientOS.IOS); + assertThat(found.get().getVersionCode()).isEqualTo(1); + assertThat(found.get().getIsVital()).isTrue(); + } + + @Test + @DisplayName("클라이언트 앱 버전 생성 실패 - NOT NULL 제약 조건을 준수해야 한다.") + public void CLIENT_APP_VERSION_CREATE_FAIL_NOT_NULL_CONSTRAINT() { + // given + ClientAppVersion nullOsVersion = ClientAppVersion.builder() + .versionCode(1) + .isVital(true) + .build(); + ClientAppVersion nullCodeVersion = ClientAppVersion.builder() + .os(ClientOS.IOS) + .isVital(true) + .build(); + ClientAppVersion nullIsVitalVersion = ClientAppVersion.builder() + .os(ClientOS.IOS) + .versionCode(1) + .build(); + + // when & then + assertThatThrownBy(() -> clientAppVersionRepository.save(nullOsVersion)) + .isExactlyInstanceOf(ConstraintViolationException.class); + assertThatThrownBy(() -> clientAppVersionRepository.save(nullCodeVersion)) + .isExactlyInstanceOf(ConstraintViolationException.class); + assertThatThrownBy(() -> clientAppVersionRepository.save(nullIsVitalVersion)) + .isExactlyInstanceOf(ConstraintViolationException.class); + } + + @Test + @DisplayName("클라이언트 앱 버전 생성 실패 - UNIQUE 제약 조건 (os, versionCode)을 준수해야 한다.") + public void CLIENT_APP_VERSION_CREATE_FAIL_UNIQUE_CONSTRAINT() { + // given + ClientAppVersion first = ClientAppVersion.builder() + .os(ClientOS.IOS) + .versionCode(1) + .isVital(false) + .build(); + ClientAppVersion second = ClientAppVersion.builder() + .os(ClientOS.IOS) + .versionCode(1) + .isVital(false) + .build(); + + // when & then + assertThatNoException().isThrownBy(() -> clientAppVersionRepository.save(first)); + assertThatThrownBy(() -> clientAppVersionRepository.save(second)) + .isExactlyInstanceOf(DataIntegrityViolationException.class); + } + + @Test + @DisplayName("클라이언트 앱 버전 조회 - 특정 OS에 대한 가장 최신의 필수 버전 조회") + public void CLIENT_APP_VERSION_FIND_MOST_RECENT_VITAL_VERSION() { + // given + Optional optionalClientAppVersion = clientAppVersionRepository + .findFirstByOsAndIsVitalTrueOrderByVersionCodeDesc(ClientOS.ANDROID); + + // when & then + assertThat(optionalClientAppVersion).isPresent(); + assertThat(optionalClientAppVersion.get().getVersionCode()).isEqualTo(2); + } + + @Test + @DisplayName("클라이언트 앱 버전 조회 - 업데이트 필수 여부 확인") + public void CLIENT_APP_VERSION_CHECK_IS_UPDATE_REQUIRED() { + // given + Optional optionalClientAppVersion = clientAppVersionRepository + .findFirstByOsAndIsVitalTrueOrderByVersionCodeDesc(ClientOS.ANDROID); + + // when & then + assertThat(optionalClientAppVersion).isPresent(); + assertThat(optionalClientAppVersion.get().judgeIsUpdateMandatory(ClientOS.ANDROID, 1)) + .isTrue(); + assertThatThrownBy(() -> optionalClientAppVersion.get().judgeIsUpdateMandatory(ClientOS.IOS, 1)) + .isExactlyInstanceOf(VersionException.class); + } + + +} diff --git a/src/test/java/usw/suwiki/repository/timetable/TimetableRepositoryTest.java b/src/test/java/usw/suwiki/repository/timetable/TimetableRepositoryTest.java index c85530be..c6b27b15 100644 --- a/src/test/java/usw/suwiki/repository/timetable/TimetableRepositoryTest.java +++ b/src/test/java/usw/suwiki/repository/timetable/TimetableRepositoryTest.java @@ -13,15 +13,8 @@ import javax.validation.ConstraintViolationException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.context.annotation.Import; -import usw.suwiki.config.TestJpaConfig; import usw.suwiki.domain.timetable.entity.Semester; import usw.suwiki.domain.timetable.entity.Timetable; import usw.suwiki.domain.timetable.entity.TimetableCell; @@ -32,16 +25,14 @@ import usw.suwiki.domain.timetable.repository.TimetableRepository; import usw.suwiki.domain.user.user.User; import usw.suwiki.domain.user.user.repository.UserRepository; +import usw.suwiki.global.annotation.SuwikiJpaTest; import usw.suwiki.global.exception.ExceptionType; import usw.suwiki.global.exception.errortype.TimetableException; import usw.suwiki.template.timetable.TimetableTemplate; import usw.suwiki.template.timetablecell.TimetableCellTemplate; import usw.suwiki.template.user.UserTemplate; -@DataJpaTest -@Import(TestJpaConfig.class) -@TestMethodOrder(MethodOrderer.DisplayName.class) -@AutoConfigureTestDatabase(replace = Replace.NONE) +@SuwikiJpaTest public class TimetableRepositoryTest { @Autowired diff --git a/src/test/java/usw/suwiki/service/clientappversion/ClientAppVersionServiceTest.java b/src/test/java/usw/suwiki/service/clientappversion/ClientAppVersionServiceTest.java new file mode 100644 index 00000000..c451c438 --- /dev/null +++ b/src/test/java/usw/suwiki/service/clientappversion/ClientAppVersionServiceTest.java @@ -0,0 +1,85 @@ +package usw.suwiki.service.clientappversion; + +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.when; +import static org.mockito.Mockito.verify; + +import java.util.Optional; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import usw.suwiki.domain.version.entity.ClientAppVersion; +import usw.suwiki.domain.version.entity.ClientOS; +import usw.suwiki.domain.version.repository.ClientAppVersionRepository; +import usw.suwiki.domain.version.service.ClientAppVersionService; +import usw.suwiki.global.annotation.SuwikiMockitoTest; +import usw.suwiki.global.exception.ExceptionType; +import usw.suwiki.global.exception.errortype.VersionException; + +@SuwikiMockitoTest +public class ClientAppVersionServiceTest { + @InjectMocks + ClientAppVersionService clientAppVersionService; + + @Mock + ClientAppVersionRepository clientAppVersionRepository; + + + @Test + @DisplayName("클라이언트 앱 필수 업데이트 여부 확인") + public void CLIENT_APP_CHECK_IS_UPDATE_MANDATORY() { + // given + ClientAppVersion clientAppVersion = ClientAppVersion.builder() + .os(ClientOS.ANDROID) + .versionCode(30) + .isVital(true) + .build(); + + when(clientAppVersionRepository.findFirstByOsAndIsVitalTrueOrderByVersionCodeDesc(any(ClientOS.class))) + .thenReturn(Optional.of(clientAppVersion)); + + // when & then + assertThatNoException() + .isThrownBy(() -> clientAppVersionService.checkIsUpdateMandatory("ANDROID", 20)); + verify(clientAppVersionRepository) + .findFirstByOsAndIsVitalTrueOrderByVersionCodeDesc(any(ClientOS.class)); + } + + @Test + @DisplayName("클라이언트 앱 필수 업데이트 여부 확인 실패 - 파라미터는 모두 NOT NULL 이어야 한다.") + public void CLIENT_APP_CHECK_IS_UPDATE_MANDATORY_FAIL_NULL_PARAMETER() { + // given + + // when & then + assertThatThrownBy(() -> clientAppVersionService.checkIsUpdateMandatory(null, 20)) + .isExactlyInstanceOf(VersionException.class) + .hasMessage(ExceptionType.INVALID_CLIENT_OS.getMessage()); + } + + @Test + @DisplayName("클라이언트 앱 필수 업데이트 여부 확인 실패 - OS 마다 DB에 최소 하나의 is_vital = true인 레코드가 존재해야 한다.") + public void CLIENT_APP_CHECK_IS_UPDATE_MANDATORY_FAIL_RECORD_NOT_EXIST() { + // given + when(clientAppVersionRepository.findFirstByOsAndIsVitalTrueOrderByVersionCodeDesc(any(ClientOS.class))) + .thenReturn(Optional.empty()); + + // when & then + assertThatThrownBy(() -> clientAppVersionService.checkIsUpdateMandatory("ANDROID", 30)) + .isExactlyInstanceOf(VersionException.class); + verify(clientAppVersionRepository) + .findFirstByOsAndIsVitalTrueOrderByVersionCodeDesc(any(ClientOS.class)); + } + + @Test + @DisplayName("클라이언트 앱 필수 업데이트 여부 확인 실패 - ClientOS ENUM에 있지 않은 os는 조회될 수 없다.") + public void CLIENT_APP_CHECK_IS_UPDATE_MANDATORY_FAIL_INVALID_OS() { + // given + + // when & then + assertThatThrownBy(() -> clientAppVersionService.checkIsUpdateMandatory("linux_ubuntu", 22)) + .isExactlyInstanceOf(VersionException.class); + } +} diff --git a/src/test/java/usw/suwiki/service/lecture/LectureServiceTest.java b/src/test/java/usw/suwiki/service/lecture/LectureServiceTest.java index 1c05ad73..9da02b77 100644 --- a/src/test/java/usw/suwiki/service/lecture/LectureServiceTest.java +++ b/src/test/java/usw/suwiki/service/lecture/LectureServiceTest.java @@ -10,13 +10,9 @@ import java.util.List; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Slice; import org.springframework.data.domain.SliceImpl; @@ -24,12 +20,12 @@ import usw.suwiki.domain.lecture.domain.Lecture; import usw.suwiki.domain.lecture.domain.repository.LectureRepository; import usw.suwiki.domain.lecture.service.LectureService; +import usw.suwiki.global.annotation.SuwikiMockitoTest; import usw.suwiki.global.dto.NoOffsetPaginationResponse; import usw.suwiki.template.lecture.LectureDetailTemplate; import usw.suwiki.template.lecture.LectureTemplate; -@ExtendWith(MockitoExtension.class) -@TestMethodOrder(MethodOrderer.DisplayName.class) +@SuwikiMockitoTest public class LectureServiceTest { @InjectMocks LectureService lectureService; diff --git a/src/test/java/usw/suwiki/service/timetable/TimetableServiceTest.java b/src/test/java/usw/suwiki/service/timetable/TimetableServiceTest.java index 011f513d..f921b38b 100644 --- a/src/test/java/usw/suwiki/service/timetable/TimetableServiceTest.java +++ b/src/test/java/usw/suwiki/service/timetable/TimetableServiceTest.java @@ -16,13 +16,9 @@ import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; import usw.suwiki.domain.timetable.dto.request.CreateTimetableCellRequest; import usw.suwiki.domain.timetable.dto.request.CreateTimetableRequest; import usw.suwiki.domain.timetable.dto.request.CreateWholeTimetableRequest; @@ -40,14 +36,14 @@ import usw.suwiki.domain.timetable.service.TimetableService; import usw.suwiki.domain.user.user.User; import usw.suwiki.domain.user.user.service.UserCRUDService; +import usw.suwiki.global.annotation.SuwikiMockitoTest; import usw.suwiki.global.exception.ExceptionType; import usw.suwiki.global.exception.errortype.TimetableException; import usw.suwiki.template.timetable.TimetableTemplate; import usw.suwiki.template.timetablecell.TimetableCellTemplate; import usw.suwiki.template.user.UserTemplate; -@ExtendWith(MockitoExtension.class) -@TestMethodOrder(MethodOrderer.DisplayName.class) +@SuwikiMockitoTest public class TimetableServiceTest { @InjectMocks TimetableService timetableService;