diff --git a/README.md b/README.md index 4985061663..0532101524 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,25 @@ * 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다. ## 온라인 코드 리뷰 과정 -* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/next-step/nextstep-docs/tree/master/codereview) \ No newline at end of file +* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/next-step/nextstep-docs/tree/master/codereview) + +## 2단계 요구사항 +- 강의 +- [ ] 과정(Course)은 기수 단위로 운영하며, 여러 개의 강의(Session)를 가질 수 있다. + - [x] 강의는 시작일과 종료일을 가진다. + - [x] 강의는 강의 커버 이미지 정보를 가진다. + - [ ] 강의는 무료 강의와 유료 강의로 나뉜다. + - [ ] 무료 강의는 최대 수강 인원 제한이 없다. + - [x] 유료 강의는 강의 최대 수강 인원을 초과할 수 없다. + - [x] 유료 강의는 수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능하다. + - [x] 강의 상태는 준비중, 모집중, 종료 3가지 상태를 가진다. + - [x] 강의 수강신청은 강의 상태가 모집중일 때만 가능하다. + - [ ] 유료 강의의 경우 결제는 이미 완료한 것으로 가정하고 이후 과정을 구현한다. + +- 이미지 +- [x] 이미지 크기는 1MB 이하여야 한다. +- [x] 이미지 타입은 gif, jpg(jpeg 포함), png, svg만 허용한다. +- [x] 이미지의 width는 300픽셀, height는 200픽셀 이상이어야 하며, width와 height의 비율은 3:2여야 한다. + +- 결제 + - [ ] 결제를 완료한 결제 정보는 payments 모듈을 통해 관리되며, 결제 정보는 Payment 객체에 담겨 반한된다. \ No newline at end of file diff --git a/src/main/java/nextstep/courses/CannotEnrollException.java b/src/main/java/nextstep/courses/CannotEnrollException.java new file mode 100644 index 0000000000..4bea8e801c --- /dev/null +++ b/src/main/java/nextstep/courses/CannotEnrollException.java @@ -0,0 +1,7 @@ +package nextstep.courses; + +public class CannotEnrollException extends RuntimeException { + public CannotEnrollException(String message) { + super(message); + } +} diff --git a/src/main/java/nextstep/courses/InvalidImageException.java b/src/main/java/nextstep/courses/InvalidImageException.java new file mode 100644 index 0000000000..417c1d74f2 --- /dev/null +++ b/src/main/java/nextstep/courses/InvalidImageException.java @@ -0,0 +1,11 @@ +package nextstep.courses; + +public class InvalidImageException extends RuntimeException { + public InvalidImageException(String message) { + super(message); + } + + public InvalidImageException(){ + + } +} diff --git a/src/main/java/nextstep/courses/domain/Course.java b/src/main/java/nextstep/courses/domain/course/Course.java similarity index 96% rename from src/main/java/nextstep/courses/domain/Course.java rename to src/main/java/nextstep/courses/domain/course/Course.java index 0f69716043..1c935f2fcc 100644 --- a/src/main/java/nextstep/courses/domain/Course.java +++ b/src/main/java/nextstep/courses/domain/course/Course.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.course; import java.time.LocalDateTime; diff --git a/src/main/java/nextstep/courses/domain/CourseRepository.java b/src/main/java/nextstep/courses/domain/course/CourseRepository.java similarity index 71% rename from src/main/java/nextstep/courses/domain/CourseRepository.java rename to src/main/java/nextstep/courses/domain/course/CourseRepository.java index 6aaeb638d1..28180d25e0 100644 --- a/src/main/java/nextstep/courses/domain/CourseRepository.java +++ b/src/main/java/nextstep/courses/domain/course/CourseRepository.java @@ -1,4 +1,4 @@ -package nextstep.courses.domain; +package nextstep.courses.domain.course; public interface CourseRepository { int save(Course course); diff --git a/src/main/java/nextstep/courses/domain/session/Enrollment.java b/src/main/java/nextstep/courses/domain/session/Enrollment.java new file mode 100644 index 0000000000..f57d740335 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/Enrollment.java @@ -0,0 +1,51 @@ +package nextstep.courses.domain.session; + +import nextstep.users.domain.NsUser; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class Enrollment { + private final List users; + private final SessionCapacity sessionCapacity; + + public Enrollment(List users, SessionCapacity sessionCapacity) { + this.users = users; + this.sessionCapacity = sessionCapacity; + } + + public Enrollment(SessionCapacity sessionCapacity) { + this(new ArrayList<>(), sessionCapacity); + } + + public Enrollment(int capacity) { + this(new ArrayList<>(), new SessionCapacity(capacity)); + } + + public Enrollment(List users) { + this(users, new SessionCapacity(0)); + } + + public void enroll(NsUser user) { + sessionCapacity.increase(); + this.users.add(user); + } + + @Override + public int hashCode() { + return Objects.hashCode(users); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + Enrollment enrollment = (Enrollment) object; + return Objects.equals(enrollment.users, this.users); + } +} diff --git a/src/main/java/nextstep/courses/domain/session/Session.java b/src/main/java/nextstep/courses/domain/session/Session.java new file mode 100644 index 0000000000..5704d53917 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/Session.java @@ -0,0 +1,43 @@ +package nextstep.courses.domain.session; + +import nextstep.courses.CannotEnrollException; +import nextstep.courses.strategy.PaymentStrategy; +import nextstep.payments.domain.Payment; +import nextstep.users.domain.NsUser; + +import java.time.LocalDate; + +public class Session { + private final SessionImage image; + private final SessionDate date; + private final SessionState state; + private final Enrollment enrollment; + private final PaymentStrategy paymentStrategy; + + public Session(SessionImage image, SessionDate date, SessionState state, Enrollment enrollment, PaymentStrategy paymentStrategy) { + this.image = image; + this.date = date; + this.state = state; + this.enrollment = enrollment; + this.paymentStrategy = paymentStrategy; + } + + public void applySession(NsUser user, LocalDate enrollDate, Payment payment) { + if (!canApply(enrollDate, payment)) { + throw new CannotEnrollException("등록 불가능한 상태입니다."); + } + this.enrollment.enroll(user); + } + + private boolean canApply(LocalDate enrollDate, Payment payment) { + if (!state.canRecruit()) { + return false; + } + + if (!date.isBefore(enrollDate)) { + return false; + } + + return paymentStrategy.payable(payment); + } +} diff --git a/src/main/java/nextstep/courses/domain/session/SessionCapacity.java b/src/main/java/nextstep/courses/domain/session/SessionCapacity.java new file mode 100644 index 0000000000..55203bc996 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/SessionCapacity.java @@ -0,0 +1,57 @@ +package nextstep.courses.domain.session; + +import nextstep.courses.CannotEnrollException; + +import java.util.Objects; + +public class SessionCapacity { + private static final int MIN_CAPACITY = 1; + + private int capacity; + private final int maxCapacity; + + public SessionCapacity(int capacity, int maxCapacity) { + this.capacity = capacity; + this.maxCapacity = maxCapacity; + checkValidCapacity(); + } + + public SessionCapacity(int maxCapacity) { + this(0, maxCapacity); + } + + private void checkValidCapacity() { + if (maxCapacity < MIN_CAPACITY) { + throw new IllegalArgumentException(); + } + isValidCapacity(); + } + + public void increase() { + capacity++; + isValidCapacity(); + } + + @Override + public int hashCode() { + return Objects.hash(capacity, maxCapacity); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + SessionCapacity sessionCapacity = (SessionCapacity) object; + return sessionCapacity.capacity == this.capacity && sessionCapacity.maxCapacity == this.maxCapacity; + } + + private void isValidCapacity(){ + if(this.capacity > this.maxCapacity) { + throw new CannotEnrollException("최대 수용 인원을 현재 인원이 초과할 수 없다."); + } + } +} diff --git a/src/main/java/nextstep/courses/domain/session/SessionDate.java b/src/main/java/nextstep/courses/domain/session/SessionDate.java new file mode 100644 index 0000000000..c3ed184630 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/SessionDate.java @@ -0,0 +1,17 @@ +package nextstep.courses.domain.session; + +import java.time.LocalDate; + +public class SessionDate { + private final LocalDate startDate; + private final LocalDate endDate; + + public SessionDate(LocalDate startDate, LocalDate endDate) { + this.startDate = startDate; + this.endDate = endDate; + } + + public boolean isBefore(LocalDate date) { + return date.isBefore(startDate); + } +} diff --git a/src/main/java/nextstep/courses/domain/session/SessionImage.java b/src/main/java/nextstep/courses/domain/session/SessionImage.java new file mode 100644 index 0000000000..6ea79ab1ae --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/SessionImage.java @@ -0,0 +1,59 @@ +package nextstep.courses.domain.session; + +import nextstep.courses.InvalidImageException; + +import java.util.Objects; + +public class SessionImage { + private static final int MAX_IMAGE_SIZE = 1000000; + private static final int MIN_WIDTH = 300; + private static final int MIN_HEIGHT = 200; + private static final double WIDTH_PER_HEIGHT = 1.5; + + private final int width; + private final int height; + private final int size; + private final SessionImageType imageType; + + public SessionImage(int width, int height, int size, String imageType) { + this(width, height, size, SessionImageType.of(imageType)); + } + + public SessionImage(int width, int height, int size, SessionImageType imageType) { + this.width = width; + this.height = height; + this.size = size; + this.imageType = imageType; + checkValidSessionImage(); + } + + private void checkValidSessionImage() { + if (this.width < MIN_WIDTH || this.height < MIN_HEIGHT) { + throw new InvalidImageException("이미지의 width는 300픽셀, height는 200픽셀 이상이어야 한다."); + } + if ((double) this.width / this.height != WIDTH_PER_HEIGHT) { + throw new InvalidImageException("이미지의 width, height의 비율은 3:2여야 한다."); + } + + if (this.size > MAX_IMAGE_SIZE) { + throw new InvalidImageException("이미지의 크기는 1MB 이하여야 한다."); + } + } + + @Override + public int hashCode() { + return Objects.hash(width, height, size, imageType); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + SessionImage image = (SessionImage) object; + return Objects.equals(image.imageType, imageType) && image.width == width && image.height == height && image.size == size; + } +} diff --git a/src/main/java/nextstep/courses/domain/session/SessionImageType.java b/src/main/java/nextstep/courses/domain/session/SessionImageType.java new file mode 100644 index 0000000000..28fbe90d32 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/SessionImageType.java @@ -0,0 +1,23 @@ +package nextstep.courses.domain.session; + +import nextstep.courses.InvalidImageException; + +import java.util.Arrays; + +public enum SessionImageType { + gif("gif"), jpg("jpg"), jpeg("jpeg"), png("png"), svg("svg"); + + private final String name; + + SessionImageType(String name) { + this.name = name; + } + + public static SessionImageType of(String name) { + return Arrays.stream(values()) + .filter(type -> type.name.equals(name)) + .findFirst() + .orElseThrow(InvalidImageException::new); + + } +} diff --git a/src/main/java/nextstep/courses/domain/session/SessionState.java b/src/main/java/nextstep/courses/domain/session/SessionState.java new file mode 100644 index 0000000000..973c431ab9 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/session/SessionState.java @@ -0,0 +1,11 @@ +package nextstep.courses.domain.session; + +public enum SessionState { + PREPARING, + RECRUITING, + CLOSED; + + public boolean canRecruit(){ + return this == RECRUITING; + } +} diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java index f9122cbe33..7489afc31d 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java @@ -1,7 +1,7 @@ package nextstep.courses.infrastructure; -import nextstep.courses.domain.Course; -import nextstep.courses.domain.CourseRepository; +import nextstep.courses.domain.course.Course; +import nextstep.courses.domain.course.CourseRepository; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; diff --git a/src/main/java/nextstep/courses/strategy/FreePaymentStrategy.java b/src/main/java/nextstep/courses/strategy/FreePaymentStrategy.java new file mode 100644 index 0000000000..be3ce88741 --- /dev/null +++ b/src/main/java/nextstep/courses/strategy/FreePaymentStrategy.java @@ -0,0 +1,10 @@ +package nextstep.courses.strategy; + +import nextstep.payments.domain.Payment; + +public class FreePaymentStrategy implements PaymentStrategy { + @Override + public boolean payable(Payment payment) { + return true; + } +} diff --git a/src/main/java/nextstep/courses/strategy/PaidPaymentStrategy.java b/src/main/java/nextstep/courses/strategy/PaidPaymentStrategy.java new file mode 100644 index 0000000000..640a89fc7b --- /dev/null +++ b/src/main/java/nextstep/courses/strategy/PaidPaymentStrategy.java @@ -0,0 +1,18 @@ +package nextstep.courses.strategy; + +import nextstep.payments.domain.Payment; + +public class PaidPaymentStrategy implements PaymentStrategy { + private final long price; + + public PaidPaymentStrategy(long price) { + this.price = price; + } + + @Override + public boolean payable(Payment payment) { + if (payment.isSameAmount(price)) + return true; + return false; + } +} diff --git a/src/main/java/nextstep/courses/strategy/PaymentStrategy.java b/src/main/java/nextstep/courses/strategy/PaymentStrategy.java new file mode 100644 index 0000000000..0ee22fe4df --- /dev/null +++ b/src/main/java/nextstep/courses/strategy/PaymentStrategy.java @@ -0,0 +1,7 @@ +package nextstep.courses.strategy; + +import nextstep.payments.domain.Payment; + +public interface PaymentStrategy { + boolean payable(Payment payment); +} diff --git a/src/main/java/nextstep/payments/domain/Payment.java b/src/main/java/nextstep/payments/domain/Payment.java index 57d833f851..5733696bf9 100644 --- a/src/main/java/nextstep/payments/domain/Payment.java +++ b/src/main/java/nextstep/payments/domain/Payment.java @@ -26,4 +26,8 @@ public Payment(String id, Long sessionId, Long nsUserId, Long amount) { this.amount = amount; this.createdAt = LocalDateTime.now(); } + + public boolean isSameAmount(long amount){ + return amount == this.amount; + } } diff --git a/src/main/java/nextstep/qna/domain/Answer.java b/src/main/java/nextstep/qna/domain/Answer.java index ccf00f8e3f..0f8afb8bd9 100644 --- a/src/main/java/nextstep/qna/domain/Answer.java +++ b/src/main/java/nextstep/qna/domain/Answer.java @@ -1,5 +1,6 @@ package nextstep.qna.domain; +import nextstep.qna.CannotDeleteException; import nextstep.qna.NotFoundException; import nextstep.qna.UnAuthorizedException; import nextstep.users.domain.NsUser; @@ -51,7 +52,7 @@ public boolean isDeleted() { return deleted; } - public boolean isOwner(NsUser writer) { + private boolean isOwner(NsUser writer) { return this.writer.equals(writer); } @@ -68,8 +69,15 @@ public String toString() { return "Answer [id=" + getId() + ", writer=" + writer + ", contents=" + contents + "]"; } - public DeleteHistory delete() { + public DeleteHistory delete(NsUser loginUser) throws CannotDeleteException { + canDeleteBy(loginUser); this.deleted = true; - return new DeleteHistory(ContentType.ANSWER, id, writer, LocalDateTime.now()); + return DeleteHistory.deleteAnswer(id, writer); + } + + private void canDeleteBy(NsUser loginUser) throws CannotDeleteException { + if (!isOwner(loginUser)) { + throw new CannotDeleteException("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다."); + } } } diff --git a/src/main/java/nextstep/qna/domain/Answers.java b/src/main/java/nextstep/qna/domain/Answers.java index e074a2354a..a41addb555 100644 --- a/src/main/java/nextstep/qna/domain/Answers.java +++ b/src/main/java/nextstep/qna/domain/Answers.java @@ -1,10 +1,10 @@ package nextstep.qna.domain; +import nextstep.qna.CannotDeleteException; import nextstep.users.domain.NsUser; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; public class Answers { private List answers; @@ -21,14 +21,11 @@ public void add(Answer answer) { answers.add(answer); } - public boolean isAllAnswerOwner(NsUser loginUser) { - return answers.stream() - .allMatch(answer -> answer.isOwner(loginUser)); - } - - public List deleteAll() { - return answers.stream() - .map(Answer::delete) - .collect(Collectors.toList()); + public List deleteAll(NsUser loginUser) throws CannotDeleteException { + List deleteHistories = new ArrayList<>(); + for (Answer answer : answers) { + deleteHistories.add(answer.delete(loginUser)); + } + return deleteHistories; } } diff --git a/src/main/java/nextstep/qna/domain/DeleteHistory.java b/src/main/java/nextstep/qna/domain/DeleteHistory.java index 43c37e5e5c..28df822498 100644 --- a/src/main/java/nextstep/qna/domain/DeleteHistory.java +++ b/src/main/java/nextstep/qna/domain/DeleteHistory.java @@ -47,4 +47,12 @@ public String toString() { return "DeleteHistory [id=" + id + ", contentType=" + contentType + ", contentId=" + contentId + ", deletedBy=" + deletedBy + ", createdDate=" + createdDate + "]"; } + + public static DeleteHistory deleteAnswer(Long id, NsUser writer) { + return new DeleteHistory(ContentType.ANSWER, id, writer, LocalDateTime.now()); + } + + public static DeleteHistory deleteQuestion(Long id, NsUser writer) { + return new DeleteHistory(ContentType.QUESTION, id, writer, LocalDateTime.now()); + } } diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java index ed79697773..c276d5310f 100644 --- a/src/main/java/nextstep/qna/domain/Question.java +++ b/src/main/java/nextstep/qna/domain/Question.java @@ -42,13 +42,6 @@ public Long getId() { return id; } - public String getTitle() { - return title; - } - - public String getContents() { - return contents; - } public NsUser getWriter() { return writer; @@ -76,8 +69,8 @@ public List delete(NsUser loginUser) throws CannotDeleteException checkValidDeleteUser(loginUser); this.deleted = true; List deleteHistories = new ArrayList<>(); - deleteHistories.add(new DeleteHistory(ContentType.QUESTION, id, writer, LocalDateTime.now())); - deleteHistories.addAll(answers.deleteAll()); + deleteHistories.add(DeleteHistory.deleteQuestion(id, writer)); + deleteHistories.addAll(answers.deleteAll(loginUser)); return deleteHistories; } @@ -85,9 +78,5 @@ private void checkValidDeleteUser(NsUser loginUser) throws CannotDeleteException if (!isOwner(loginUser)) { throw new CannotDeleteException("질문을 삭제할 권한이 없습니다."); } - - if (!answers.isAllAnswerOwner(loginUser)) { - throw new CannotDeleteException("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다."); - } } } diff --git a/src/test/java/nextstep/courses/domain/session/EnrollmentTest.java b/src/test/java/nextstep/courses/domain/session/EnrollmentTest.java new file mode 100644 index 0000000000..f28f1f645e --- /dev/null +++ b/src/test/java/nextstep/courses/domain/session/EnrollmentTest.java @@ -0,0 +1,31 @@ +package nextstep.courses.domain.session; + +import nextstep.courses.CannotEnrollException; +import nextstep.users.domain.NsUserTest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class EnrollmentTest { + + @Test + @DisplayName("등록에 성공한다.") + void enroll() { + Enrollment enrollment = new Enrollment(10); + enrollment.enroll(NsUserTest.JAVAJIGI); + assertEquals(enrollment, new Enrollment(List.of(NsUserTest.JAVAJIGI), new SessionCapacity(10))); + } + + @Test + @DisplayName("인원 초과로 인해 등록에 실패한다.") + void enroll_fail() { + Enrollment enrollment = new Enrollment(1); + enrollment.enroll(NsUserTest.JAVAJIGI); + assertThatThrownBy(() -> enrollment.enroll(NsUserTest.SANJIGI)) + .isInstanceOf(CannotEnrollException.class); + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/session/SessionCapacityTest.java b/src/test/java/nextstep/courses/domain/session/SessionCapacityTest.java new file mode 100644 index 0000000000..b8c956f610 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/session/SessionCapacityTest.java @@ -0,0 +1,34 @@ +package nextstep.courses.domain.session; + +import nextstep.courses.CannotEnrollException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class SessionCapacityTest { + + @Test + @DisplayName("등록하면 하나가 찬다.") + void increase_success() { + SessionCapacity capacity = new SessionCapacity(10); + capacity.increase(); + assertEquals(capacity, new SessionCapacity(1, 10)); + } + + @Test + @DisplayName("maxCapacity보다 더 수강하려면 에러를 반환한다.") + void increase_fail() { + SessionCapacity capacity = new SessionCapacity(10, 10); + assertThatThrownBy(capacity::increase) + .isInstanceOf(CannotEnrollException.class); + } + + @Test + @DisplayName("초기화 시 수강인원이 최대 인원보다 크면 에러를 반환한다.") + void init() { + assertThatThrownBy(() -> new SessionCapacity(11, 10)) + .isInstanceOf(CannotEnrollException.class); + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/session/SessionImageTest.java b/src/test/java/nextstep/courses/domain/session/SessionImageTest.java new file mode 100644 index 0000000000..8bc07d7e15 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/session/SessionImageTest.java @@ -0,0 +1,39 @@ +package nextstep.courses.domain.session; + +import nextstep.courses.InvalidImageException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class SessionImageTest { + + @Test + @DisplayName("이미지의 width는 300 이상, height는 200이면, size는 1mb 보다 작으면 정상적으로 생성된다.") + public void init() { + SessionImage image = new SessionImage(300, 200, 10, "jpg"); + assertEquals(image, new SessionImage(300, 200, 10, "jpg")); + } + + @Test + @DisplayName("이미지의 최소 크기를 넘지 못하면 에러가 발생한다.") + public void init_invalid_heightwidth() { + assertThatThrownBy(() -> new SessionImage(30, 20, 10, "jgp")) + .isInstanceOf(InvalidImageException.class); + } + + @Test + @DisplayName("이미지의 비율이 맞지 않으면 에러가 발생한다.") + public void init_invalid_ratio() { + assertThatThrownBy(() -> new SessionImage(20, 20, 10, "jpg")) + .isInstanceOf(InvalidImageException.class); + } + + @Test + @DisplayName("이미지의 크기가 1mb보다 크면 에러가 발생한다.") + public void init_invalid_size() { + assertThatThrownBy(() -> new SessionImage(30, 20, 10000000, "jgp")) + .isInstanceOf(InvalidImageException.class); + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/session/SessionImageTypeTest.java b/src/test/java/nextstep/courses/domain/session/SessionImageTypeTest.java new file mode 100644 index 0000000000..2668aa1d1b --- /dev/null +++ b/src/test/java/nextstep/courses/domain/session/SessionImageTypeTest.java @@ -0,0 +1,25 @@ +package nextstep.courses.domain.session; + +import nextstep.courses.InvalidImageException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static nextstep.courses.domain.session.SessionImageType.gif; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class SessionImageTypeTest { + + @Test + @DisplayName("이름이 똑같은 확장자를 찾아 리턴한다.") + void of() { + assertEquals(SessionImageType.of("gif"), gif); + } + + @Test + @DisplayName("등록되지 않은 확장자를 넣으면 에러를 반환한다.") + void of_fail() { + assertThatThrownBy(() -> SessionImageType.of("xls")) + .isInstanceOf(InvalidImageException.class); + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/session/SessionStateTest.java b/src/test/java/nextstep/courses/domain/session/SessionStateTest.java new file mode 100644 index 0000000000..167cb448ca --- /dev/null +++ b/src/test/java/nextstep/courses/domain/session/SessionStateTest.java @@ -0,0 +1,27 @@ +package nextstep.courses.domain.session; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class SessionStateTest { + + @Test + @DisplayName("준비중 상태에는 등록할 수 있다.") + void canApply() { + SessionState state = SessionState.RECRUITING; + assertTrue(state.canRecruit()); + } + + @ParameterizedTest + @EnumSource(value = SessionState.class, mode = EnumSource.Mode.EXCLUDE, names = {"RECRUITING"}) + @DisplayName("준비중이 아닌 상태에서는 등록할 수 없다.") + void cannotApply(SessionState state) { + assertFalse(state.canRecruit()); + } + +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/session/SessionTest.java b/src/test/java/nextstep/courses/domain/session/SessionTest.java new file mode 100644 index 0000000000..2d05d568aa --- /dev/null +++ b/src/test/java/nextstep/courses/domain/session/SessionTest.java @@ -0,0 +1,72 @@ +package nextstep.courses.domain.session; + +import nextstep.courses.CannotEnrollException; +import nextstep.courses.strategy.FreePaymentStrategy; +import nextstep.courses.strategy.PaidPaymentStrategy; +import nextstep.payments.domain.Payment; +import nextstep.users.domain.NsUserTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class SessionTest { + private SessionImage image; + private SessionDate date; + private SessionState state; + private Enrollment enrollment; + + @BeforeEach + void init() { + image = new SessionImage(300, 200, 10, "jpg"); + date = new SessionDate(LocalDate.now(), LocalDate.now().plusDays(7)); + state = SessionState.RECRUITING; + enrollment = new Enrollment(10); + } + + @Test + @DisplayName("무료 강의를 등록한다.") + void applyFreeSession() { + FreePaymentStrategy freePaymentStrategy = new FreePaymentStrategy(); + Session session = new Session(image, date, state, enrollment, freePaymentStrategy); + session.applySession(NsUserTest.JAVAJIGI, LocalDate.now().minusDays(1), new Payment()); + } + + @Test + @DisplayName("유료 강의를 등록한다.") + void applyPaidSession() { + PaidPaymentStrategy paidPaymentStrategy = new PaidPaymentStrategy(1000L); + Session session = new Session(image, date, state, enrollment, paidPaymentStrategy); + session.applySession(NsUserTest.JAVAJIGI, LocalDate.now().minusDays(1), new Payment("1L", 1L, 1L, 1000L)); + } + + @Test + @DisplayName("모집 상태가 아니면 등록할 수 없다.") + void applySessionFailByState() { + FreePaymentStrategy freePaymentStrategy = new FreePaymentStrategy(); + Session session = new Session(image, date, SessionState.PREPARING, enrollment, freePaymentStrategy); + assertThatThrownBy(() -> session.applySession(NsUserTest.JAVAJIGI, LocalDate.now(), new Payment())) + .isInstanceOf(CannotEnrollException.class); + } + + @Test + @DisplayName("강의 시작 중에는 등록할 수 없다.") + void applySessionFailByDate() { + FreePaymentStrategy freePaymentStrategy = new FreePaymentStrategy(); + Session session = new Session(image, date, state, enrollment, freePaymentStrategy); + assertThatThrownBy(() -> session.applySession(NsUserTest.JAVAJIGI, LocalDate.now().plusDays(10), new Payment())) + .isInstanceOf(CannotEnrollException.class); + } + + @Test + @DisplayName("돈이 맞지 않으면 유료 강의에 등록할 수 없다.") + void applySessionFailByPayment() { + PaidPaymentStrategy paidPaymentStrategy = new PaidPaymentStrategy(1000L); + Session session = new Session(image, date, state, enrollment, paidPaymentStrategy); + assertThatThrownBy(() -> session.applySession(NsUserTest.JAVAJIGI, LocalDate.now(), new Payment("1L", 1L, 1L, 2000L))) + .isInstanceOf(CannotEnrollException.class); + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java index f087fc0ad2..530375d3e1 100644 --- a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java +++ b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java @@ -1,7 +1,7 @@ package nextstep.courses.infrastructure; -import nextstep.courses.domain.Course; -import nextstep.courses.domain.CourseRepository; +import nextstep.courses.domain.course.Course; +import nextstep.courses.domain.course.CourseRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; diff --git a/src/test/java/nextstep/courses/strategy/PaymentStrategyTest.java b/src/test/java/nextstep/courses/strategy/PaymentStrategyTest.java new file mode 100644 index 0000000000..927fe2bd61 --- /dev/null +++ b/src/test/java/nextstep/courses/strategy/PaymentStrategyTest.java @@ -0,0 +1,24 @@ +package nextstep.courses.strategy; + +import nextstep.payments.domain.Payment; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class PaymentStrategyTest { + + @Test + @DisplayName("무료인 경기는 금액을 내지 않아도 된다.") + public void free(){ + FreePaymentStrategy freePaymentStrategy = new FreePaymentStrategy(); + assertTrue(freePaymentStrategy.payable(new Payment())); + } + + @Test + @DisplayName("유료인 경기는 낸 가격과 동일한 가격이다.") + public void paid() { + PaidPaymentStrategy paidPaymentStrategy = new PaidPaymentStrategy(1000L); + assertTrue(paidPaymentStrategy.payable(new Payment("1", 1L, 1L, 1000L))); + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/qna/domain/AnswerTest.java b/src/test/java/nextstep/qna/domain/AnswerTest.java index e5b5da8292..eceb9f1f95 100644 --- a/src/test/java/nextstep/qna/domain/AnswerTest.java +++ b/src/test/java/nextstep/qna/domain/AnswerTest.java @@ -1,5 +1,6 @@ package nextstep.qna.domain; +import nextstep.qna.CannotDeleteException; import nextstep.users.domain.NsUserTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -20,8 +21,8 @@ public void init() { @Test @DisplayName("delete를 통해 answer은 삭제 상태가 된다.") - public void delete() { - A1.delete(); + public void delete() throws CannotDeleteException { + A1.delete(NsUserTest.JAVAJIGI); assertThat(A1.isDeleted()).isTrue(); } } diff --git a/src/test/java/nextstep/qna/domain/AnswersTest.java b/src/test/java/nextstep/qna/domain/AnswersTest.java index 352ebfee4c..02f66b82ab 100644 --- a/src/test/java/nextstep/qna/domain/AnswersTest.java +++ b/src/test/java/nextstep/qna/domain/AnswersTest.java @@ -1,5 +1,6 @@ package nextstep.qna.domain; +import nextstep.qna.CannotDeleteException; import nextstep.users.domain.NsUserTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -7,7 +8,7 @@ import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; class AnswersTest { Answer A1; @@ -24,16 +25,17 @@ public void init() { } @Test - @DisplayName("답변의 작성자가 전부 같지 않으면 false을 반환한다.") + @DisplayName("답변의 작성자가 전부 같지 않으면 에러를 반환한다.") void isAllAnswerOwner_false() { Answers answers = new Answers(List.of(A1, A2, A3)); - assertThat(answers.isAllAnswerOwner(NsUserTest.JAVAJIGI)).isFalse(); + assertThatThrownBy(() -> answers.deleteAll(NsUserTest.JAVAJIGI)) + .isInstanceOf(CannotDeleteException.class); } @Test @DisplayName("답변의 작성자가 전부 같으면 true 반환한다.") - void isAllAnswerOwner_true() { + void isAllAnswerOwner_true() throws CannotDeleteException { Answers answers = new Answers(List.of(A1, A3)); - assertThat(answers.isAllAnswerOwner(NsUserTest.JAVAJIGI)).isTrue(); + answers.deleteAll(NsUserTest.JAVAJIGI); } } \ No newline at end of file