diff --git a/src/main/java/nextstep/courses/domain/Course.java b/src/main/java/nextstep/courses/domain/Course.java index 0f69716043..eb708c73bd 100644 --- a/src/main/java/nextstep/courses/domain/Course.java +++ b/src/main/java/nextstep/courses/domain/Course.java @@ -9,6 +9,8 @@ public class Course { private Long creatorId; + private final Sessions sessions = new Sessions(); + private LocalDateTime createdAt; private LocalDateTime updatedAt; @@ -40,6 +42,11 @@ public LocalDateTime getCreatedAt() { return createdAt; } + public void addSession(Session session) { + session.toCourse(this); + sessions.add(session); + } + @Override public String toString() { return "Course{" + diff --git a/src/main/java/nextstep/courses/domain/CoverImage.java b/src/main/java/nextstep/courses/domain/CoverImage.java new file mode 100644 index 0000000000..b640e02a7a --- /dev/null +++ b/src/main/java/nextstep/courses/domain/CoverImage.java @@ -0,0 +1,14 @@ +package nextstep.courses.domain; + +public class CoverImage { + + private final CoverImageFileSize size; + private final CoverImageType type; + private final CoverImageResolution resolution; + + CoverImage(CoverImageType type, long size, int width, int height) { + this.size = new CoverImageFileSize(size); + this.type = type; + this.resolution = new CoverImageResolution(width, height); + } +} diff --git a/src/main/java/nextstep/courses/domain/CoverImageFactory.java b/src/main/java/nextstep/courses/domain/CoverImageFactory.java new file mode 100644 index 0000000000..28466e1363 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/CoverImageFactory.java @@ -0,0 +1,9 @@ +package nextstep.courses.domain; + +public class CoverImageFactory { + + public static CoverImage ofGif(long size, int width, int height) { + return new CoverImage(CoverImageType.GIF, size, width, height); + } + +} diff --git a/src/main/java/nextstep/courses/domain/CoverImageFileSize.java b/src/main/java/nextstep/courses/domain/CoverImageFileSize.java new file mode 100644 index 0000000000..f2899ac5c5 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/CoverImageFileSize.java @@ -0,0 +1,21 @@ +package nextstep.courses.domain; + +public class CoverImageFileSize { + private static final long MAX_SIZE = 1024 * 1024; // 1MB + private final long size; + + public CoverImageFileSize(long size) { + validate(size); + this.size = size; + } + + private static void validate(long size) { + if (size <= 0 || size > MAX_SIZE) { + throw new IllegalArgumentException("이미지 크기는 0보다 크고 1MB 이하여야 합니다."); + } + } + + public long getSize() { + return size; + } +} diff --git a/src/main/java/nextstep/courses/domain/CoverImageResolution.java b/src/main/java/nextstep/courses/domain/CoverImageResolution.java new file mode 100644 index 0000000000..2ff63cec9c --- /dev/null +++ b/src/main/java/nextstep/courses/domain/CoverImageResolution.java @@ -0,0 +1,38 @@ +package nextstep.courses.domain; + +public class CoverImageResolution { + private static final int MIN_WIDTH = 300; + private static final int MIN_HEIGHT = 200; + private static final int ASPECT_RATIO_WIDTH = 3; + private static final int ASPECT_RATIO_HEIGHT = 2; + + private final int width; + private final int height; + + public CoverImageResolution(int width, int height) { + validate(width, height); + this.width = width; + this.height = height; + } + + private static void validate(int width, int height) { + validateWidthAndHeight(width, height); + validateAspectRatioValid(width, height); + } + + private static void validateWidthAndHeight(int width, int height) { + boolean isValid = width >= MIN_WIDTH && height >= MIN_HEIGHT; + + if (!isValid) { + throw new IllegalArgumentException("이미지의 width는 300픽셀, height는 200픽셀 이상이어야 합니다."); + } + } + + private static void validateAspectRatioValid(int width, int height) { + boolean isValid = width * ASPECT_RATIO_HEIGHT == height * ASPECT_RATIO_WIDTH; + + if (!isValid) { + throw new IllegalArgumentException("width와 height의 비율은 3:2여야 합니다."); + } + } +} diff --git a/src/main/java/nextstep/courses/domain/CoverImageType.java b/src/main/java/nextstep/courses/domain/CoverImageType.java new file mode 100644 index 0000000000..1bc185ec41 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/CoverImageType.java @@ -0,0 +1,8 @@ +package nextstep.courses.domain; + +public enum CoverImageType { + GIF, + JPG, + PNG, + SVG +} diff --git a/src/main/java/nextstep/courses/domain/FreeRegistrationPolicy.java b/src/main/java/nextstep/courses/domain/FreeRegistrationPolicy.java new file mode 100644 index 0000000000..bde34b9eb1 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/FreeRegistrationPolicy.java @@ -0,0 +1,9 @@ +package nextstep.courses.domain; + +public class FreeRegistrationPolicy implements RegistrationPolicy { + + @Override + public void validateRegistration(Session session, Money paymentAmount) { + // 무조건 패스 + } +} diff --git a/src/main/java/nextstep/courses/domain/Money.java b/src/main/java/nextstep/courses/domain/Money.java new file mode 100644 index 0000000000..e165eae831 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Money.java @@ -0,0 +1,28 @@ +package nextstep.courses.domain; + +import java.util.Objects; + +public class Money { + private final NaturalNumber value; + + public Money(long amount) { + this.value = new NaturalNumber(amount); + } + + public long getAmount() { + return value.getValue(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Money money = (Money) o; + return Objects.equals(value, money.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } +} diff --git a/src/main/java/nextstep/courses/domain/NaturalNumber.java b/src/main/java/nextstep/courses/domain/NaturalNumber.java new file mode 100644 index 0000000000..33521c8965 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/NaturalNumber.java @@ -0,0 +1,50 @@ +package nextstep.courses.domain; + +import java.util.Objects; + +public class NaturalNumber implements Comparable<NaturalNumber> { + + private final long value; + + public NaturalNumber(long value) { + validate(value); + this.value = value; + } + + private static void validate(long value) { + if (value < 0) { + throw new IllegalArgumentException("0을 포함한 자연수만 허용 가능합니다."); + } + } + + public long getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NaturalNumber that = (NaturalNumber) o; + return value == that.value; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public String toString() { + return getValue() + ""; + } + + @Override + public int compareTo(NaturalNumber o) { + return Long.compare(getValue(), o.getValue()); + } + + public int compareTo(int o) { + return compareTo(new NaturalNumber(o)); + } +} diff --git a/src/main/java/nextstep/courses/domain/PaidRegistrationPolicy.java b/src/main/java/nextstep/courses/domain/PaidRegistrationPolicy.java new file mode 100644 index 0000000000..8e870435cd --- /dev/null +++ b/src/main/java/nextstep/courses/domain/PaidRegistrationPolicy.java @@ -0,0 +1,24 @@ +package nextstep.courses.domain; + +public class PaidRegistrationPolicy implements RegistrationPolicy { + + private final Money sessionFee; + private final NaturalNumber maxStudentCount; + + public PaidRegistrationPolicy(int sessionFee, int maxStudentCount) { + this.sessionFee = new Money(sessionFee); + this.maxStudentCount = new NaturalNumber(maxStudentCount); + } + + @Override + public void validateRegistration(Session session, Money paymentAmount) { + if (!session.isStudentCountLessThan((int) maxStudentCount.getValue())) { + throw new IllegalArgumentException("강의 최대 수강 인원을 초과할 수 없습니다."); + } + + if (!sessionFee.equals(paymentAmount)) { + throw new IllegalArgumentException("수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능합니다."); + } + } + +} diff --git a/src/main/java/nextstep/courses/domain/RegistrationPolicy.java b/src/main/java/nextstep/courses/domain/RegistrationPolicy.java new file mode 100644 index 0000000000..a40824812d --- /dev/null +++ b/src/main/java/nextstep/courses/domain/RegistrationPolicy.java @@ -0,0 +1,5 @@ +package nextstep.courses.domain; + +public interface RegistrationPolicy { + void validateRegistration(Session session, Money paymentAmount); +} diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java new file mode 100644 index 0000000000..03cef7eaf4 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -0,0 +1,45 @@ +package nextstep.courses.domain; + +import nextstep.payments.domain.Payment; +import nextstep.users.domain.NsUser; +import nextstep.users.domain.NsUsers; + +public class Session { + private Long id; + private Course course; + private final NsUsers nsUsers = new NsUsers(); + private CoverImage coverImage; + private SessionStatus sessionStatus; + private RegistrationPolicy registrationPolicy; + private SessionPeriod sessionPeriod; + + Session(long id, CoverImage coverImage, SessionStatus sessionStatus, RegistrationPolicy registrationPolicy, SessionPeriod sessionPeriod) { + this.id = id; + this.coverImage = coverImage; + this.sessionStatus = sessionStatus; + this.registrationPolicy = registrationPolicy; + this.sessionPeriod = sessionPeriod; + } + + public void toCourse(Course course) { + this.course = course; + } + + public boolean isStudentCountLessThan(int count) { + return nsUsers.getSize() < count; + } + + public Payment register(NsUser nsUser, Money paymentAmount) { + if (!sessionStatus.isRegistrable()) { + throw new IllegalStateException("수강신청이 불가능한 상태입니다."); + } + + registrationPolicy.validateRegistration(this, paymentAmount); + + nsUsers.add(nsUser); + + return new Payment("", this, nsUser, paymentAmount); + } + + +} diff --git a/src/main/java/nextstep/courses/domain/SessionPeriod.java b/src/main/java/nextstep/courses/domain/SessionPeriod.java new file mode 100644 index 0000000000..5e2de2af48 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/SessionPeriod.java @@ -0,0 +1,15 @@ +package nextstep.courses.domain; + +import java.time.LocalDateTime; + +public class SessionPeriod { + + private final LocalDateTime startedAt; + private final LocalDateTime endedAt; + + public SessionPeriod(LocalDateTime startedAt, LocalDateTime endedAt) { + this.startedAt = startedAt; + this.endedAt = endedAt; + } + +} diff --git a/src/main/java/nextstep/courses/domain/SessionStatus.java b/src/main/java/nextstep/courses/domain/SessionStatus.java new file mode 100644 index 0000000000..cd7412ac9c --- /dev/null +++ b/src/main/java/nextstep/courses/domain/SessionStatus.java @@ -0,0 +1,11 @@ +package nextstep.courses.domain; + +public enum SessionStatus { + PREPARING, + RECRUITING, + ENDED; + + public boolean isRegistrable() { + return this == RECRUITING; + } +} diff --git a/src/main/java/nextstep/courses/domain/Sessions.java b/src/main/java/nextstep/courses/domain/Sessions.java new file mode 100644 index 0000000000..7b94288b8b --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Sessions.java @@ -0,0 +1,24 @@ +package nextstep.courses.domain; + +import java.util.ArrayList; +import java.util.List; + +public class Sessions { + private final List<Session> sessions; + + public Sessions(List<Session> sessions) { + this.sessions = sessions; + } + + public Sessions() { + this(new ArrayList<>()); + } + + public void add(Session session) { + sessions.add(session); + } + + public List<Session> getSessions() { + return sessions; + } +} diff --git a/src/main/java/nextstep/payments/domain/Payment.java b/src/main/java/nextstep/payments/domain/Payment.java index 57d833f851..3fe953b815 100644 --- a/src/main/java/nextstep/payments/domain/Payment.java +++ b/src/main/java/nextstep/payments/domain/Payment.java @@ -2,28 +2,44 @@ import java.time.LocalDateTime; +import nextstep.courses.domain.Money; +import nextstep.courses.domain.Session; +import nextstep.users.domain.NsUser; + public class Payment { private String id; - // 결제한 강의 아이디 - private Long sessionId; + // 결제한 강의 + private Session session; - // 결제한 사용자 아이디 - private Long nsUserId; + // 결제한 사용자 + private NsUser nsUser; // 결제 금액 - private Long amount; + private Money amount; private LocalDateTime createdAt; public Payment() { } - public Payment(String id, Long sessionId, Long nsUserId, Long amount) { + public Payment(String id, Session session, NsUser nsUser, Money amount) { this.id = id; - this.sessionId = sessionId; - this.nsUserId = nsUserId; + this.session = session; + this.nsUser = nsUser; this.amount = amount; this.createdAt = LocalDateTime.now(); } + + public Session getSession() { + return session; + } + + public NsUser getNsUser() { + return nsUser; + } + + public Money getAmount() { + return amount; + } } diff --git a/src/main/java/nextstep/qna/domain/Answer.java b/src/main/java/nextstep/qna/domain/Answer.java index 04070abf19..333a3730d6 100644 --- a/src/main/java/nextstep/qna/domain/Answer.java +++ b/src/main/java/nextstep/qna/domain/Answer.java @@ -75,7 +75,7 @@ public DeleteHistory delete(NsUser loginUser) { validateDelete(loginUser); this.deleted = true; this.updatedDate = LocalDateTime.now(); - return new DeleteHistory(ContentType.ANSWER, getId(), getWriter(), LocalDateTime.now()); + return DeleteHistoryFactory.ofAnswer(getId(), loginUser, LocalDateTime.now()); } @Override diff --git a/src/main/java/nextstep/qna/domain/Answers.java b/src/main/java/nextstep/qna/domain/Answers.java new file mode 100644 index 0000000000..89b86b7fec --- /dev/null +++ b/src/main/java/nextstep/qna/domain/Answers.java @@ -0,0 +1,24 @@ +package nextstep.qna.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import nextstep.users.domain.NsUser; + +public class Answers { + private final List<Answer> answers = new ArrayList<>(); + + public void add(Answer answer) { + answers.add(answer); + } + + public List<DeleteHistory> delete(NsUser loginUser) { + return answers + .stream() + .map(answer -> answer.delete(loginUser)) + .collect(Collectors.toList()); + } + +} diff --git a/src/main/java/nextstep/qna/domain/DeleteHistory.java b/src/main/java/nextstep/qna/domain/DeleteHistory.java index 596b6ee9c4..d5b84ce75d 100644 --- a/src/main/java/nextstep/qna/domain/DeleteHistory.java +++ b/src/main/java/nextstep/qna/domain/DeleteHistory.java @@ -16,7 +16,7 @@ public class DeleteHistory { private LocalDateTime createdDate = LocalDateTime.now(); - public DeleteHistory(ContentType contentType, Long contentId, NsUser deletedBy, LocalDateTime createdDate) { + DeleteHistory(ContentType contentType, Long contentId, NsUser deletedBy, LocalDateTime createdDate) { this.contentType = contentType; this.contentId = contentId; this.deletedBy = deletedBy; diff --git a/src/main/java/nextstep/qna/domain/DeleteHistoryFactory.java b/src/main/java/nextstep/qna/domain/DeleteHistoryFactory.java new file mode 100644 index 0000000000..10b8fbbcdd --- /dev/null +++ b/src/main/java/nextstep/qna/domain/DeleteHistoryFactory.java @@ -0,0 +1,17 @@ +package nextstep.qna.domain; + +import java.time.LocalDateTime; + +import nextstep.users.domain.NsUser; + +public class DeleteHistoryFactory { + + public static DeleteHistory ofQuestion(Long contentId, NsUser deletedBy, LocalDateTime createdDate) { + return new DeleteHistory(ContentType.QUESTION, contentId, deletedBy, createdDate); + } + + public static DeleteHistory ofAnswer(Long contentId, NsUser deletedBy, LocalDateTime createdDate) { + return new DeleteHistory(ContentType.ANSWER, contentId, deletedBy, createdDate); + } + +} diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java index 3984b80466..e94d2c9b88 100644 --- a/src/main/java/nextstep/qna/domain/Question.java +++ b/src/main/java/nextstep/qna/domain/Question.java @@ -6,7 +6,6 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; public class Question { private Long id; @@ -17,7 +16,7 @@ public class Question { private NsUser writer; - private List<Answer> answers = new ArrayList<>(); + private final Answers answers = new Answers(); private boolean deleted = false; @@ -65,7 +64,7 @@ public boolean isDeleted() { return deleted; } - public List<Answer> getAnswers() { + public Answers getAnswers() { return answers; } @@ -73,13 +72,7 @@ public DeleteHistories delete(NsUser loginUser) { List<DeleteHistory> deleteHistories = new ArrayList<>(); deleteHistories.add(deleteQuestion(loginUser)); - - List<DeleteHistory> answerHistories = getAnswers() - .stream() - .map(ans -> ans.delete(loginUser)) - .collect(Collectors.toList()); - - deleteHistories.addAll(answerHistories); + deleteHistories.addAll(getAnswers().delete(loginUser)); return new DeleteHistories(deleteHistories); } @@ -87,7 +80,7 @@ public DeleteHistories delete(NsUser loginUser) { private DeleteHistory deleteQuestion(NsUser loginUser) { validateDelete(loginUser); this.deleted = true; - return new DeleteHistory(ContentType.QUESTION, getId(), getWriter(), LocalDateTime.now()); + return DeleteHistoryFactory.ofQuestion(getId(), loginUser, LocalDateTime.now()); } private void validateDelete(NsUser loginUser) { diff --git a/src/main/java/nextstep/users/domain/NsUsers.java b/src/main/java/nextstep/users/domain/NsUsers.java new file mode 100644 index 0000000000..fdc4000ddd --- /dev/null +++ b/src/main/java/nextstep/users/domain/NsUsers.java @@ -0,0 +1,29 @@ +package nextstep.users.domain; + +import java.util.ArrayList; +import java.util.List; + +public class NsUsers { + + private final List<NsUser> nsUsers; + + public NsUsers() { + this(new ArrayList<>()); + } + + public NsUsers(List<NsUser> nsUsers) { + this.nsUsers = nsUsers; + } + + public List<NsUser> getNsUsers() { + return nsUsers; + } + + public void add(NsUser nsUser) { + nsUsers.add(nsUser); + } + + public int getSize() { + return nsUsers.size(); + } +} diff --git a/src/test/java/nextstep/courses/domain/CoverImageFileSizeTest.java b/src/test/java/nextstep/courses/domain/CoverImageFileSizeTest.java new file mode 100644 index 0000000000..e2a6bb60f8 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/CoverImageFileSizeTest.java @@ -0,0 +1,27 @@ +package nextstep.courses.domain; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.catchIllegalArgumentException; +import static org.junit.jupiter.api.Assertions.*; + +class CoverImageFileSizeTest { + + @Test + void 사이즈가_1MB가_초과되면_예외가_발생한다() { + IllegalArgumentException e = catchIllegalArgumentException( + () -> new CoverImageFileSize(1024 * 1024 + 1)); + + Assertions.assertThat(e).hasMessageContaining("이미지 크기는 0보다 크고 1MB 이하여야 합니다."); + } + + @Test + void 사이즈가_0보다_작으면_예외가_발생한다() { + IllegalArgumentException e = catchIllegalArgumentException( + () -> new CoverImageFileSize(-1)); + + Assertions.assertThat(e).hasMessageContaining("이미지 크기는 0보다 크고 1MB 이하여야 합니다."); + } + +} diff --git a/src/test/java/nextstep/courses/domain/CoverImageResolutionTest.java b/src/test/java/nextstep/courses/domain/CoverImageResolutionTest.java new file mode 100644 index 0000000000..028c6ff24c --- /dev/null +++ b/src/test/java/nextstep/courses/domain/CoverImageResolutionTest.java @@ -0,0 +1,34 @@ +package nextstep.courses.domain; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.catchIllegalArgumentException; + +class CoverImageResolutionTest { + + @ParameterizedTest + @CsvSource(value = { + "1, 200", + "300, 1", + "1, 1"}) + void 이미지의_width는_300픽셀_height는_200픽셀_이상이어야_합니다(int width, int height) { + IllegalArgumentException e + = catchIllegalArgumentException(() -> new CoverImageResolution(width, height)); + Assertions.assertThat(e).hasMessage("이미지의 width는 300픽셀, height는 200픽셀 이상이어야 합니다."); + } + + @ParameterizedTest + @CsvSource(value = { + "300, 201", + "301, 200", + "500, 1000"}) + void 이미지의_width와_height의_비율은_3대2여야_합니다(int width, int height) { + IllegalArgumentException e + = catchIllegalArgumentException(() -> new CoverImageResolution(width, height)); + Assertions.assertThat(e).hasMessage("width와 height의 비율은 3:2여야 합니다."); + } + +} diff --git a/src/test/java/nextstep/courses/domain/CoverImageTest.java b/src/test/java/nextstep/courses/domain/CoverImageTest.java new file mode 100644 index 0000000000..ea53d45a8e --- /dev/null +++ b/src/test/java/nextstep/courses/domain/CoverImageTest.java @@ -0,0 +1,11 @@ +package nextstep.courses.domain; + +import static org.junit.jupiter.api.Assertions.*; + +class CoverImageTest { + + public static CoverImage createCoverImage1() { + return CoverImageFactory.ofGif(1024 * 1024, 300, 200); + } + +} diff --git a/src/test/java/nextstep/courses/domain/NaturalNumberTest.java b/src/test/java/nextstep/courses/domain/NaturalNumberTest.java new file mode 100644 index 0000000000..327ba85e7f --- /dev/null +++ b/src/test/java/nextstep/courses/domain/NaturalNumberTest.java @@ -0,0 +1,26 @@ +package nextstep.courses.domain; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.*; + + +class NaturalNumberTest { + + @ParameterizedTest + @ValueSource(ints = {-1, -2, -3}) + void _0을_포함한_자연수가_아닐_경우_예외가_발생한다(int number) { + IllegalArgumentException e + = catchIllegalArgumentException(() -> new NaturalNumber(number)); + + assertThat(e).hasMessageContaining("0을 포함한 자연수만 허용 가능합니다."); + } + + @ParameterizedTest + @ValueSource(ints = {0, 1}) + void _0을_포함한_자연수만_생성이_가능하다(int number) { + new NaturalNumber(number); + } + +} diff --git a/src/test/java/nextstep/courses/domain/PaidRegistrationPolicyTest.java b/src/test/java/nextstep/courses/domain/PaidRegistrationPolicyTest.java new file mode 100644 index 0000000000..ee14e33a72 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/PaidRegistrationPolicyTest.java @@ -0,0 +1,50 @@ +package nextstep.courses.domain; + +import java.time.LocalDateTime; + +import org.junit.jupiter.api.Test; + +import nextstep.users.domain.NsUserTest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchIllegalArgumentException; +import static org.junit.jupiter.api.Assertions.*; + +class PaidRegistrationPolicyTest { + + @Test + void 유료_강의는_강의_최대_수강_인원을_초과할_수_없다() { + int sessionFee = 20000; + int maxStudentCount = 1; + + Session session = new SessionBuilder() + .paid(sessionFee, maxStudentCount) + .sessionStatus(SessionStatus.RECRUITING) + .build(); + + IllegalArgumentException e = catchIllegalArgumentException(() -> { + session.register(NsUserTest.JAVAJIGI, new Money(sessionFee)); + session.register(NsUserTest.SANJIGI, new Money(sessionFee)); + }); + + assertThat(e).hasMessage("강의 최대 수강 인원을 초과할 수 없습니다."); + } + + @Test + void 유료_강의는_수강생이_결제한_금액과_수강료가_일치할_때_수강_신청이_가능하다() { + int sessionFee = 20000; + int maxStudentCount = 1; + + Session session = new SessionBuilder() + .paid(sessionFee, maxStudentCount) + .sessionStatus(SessionStatus.RECRUITING) + .build(); + + IllegalArgumentException e = catchIllegalArgumentException(() -> { + session.register(NsUserTest.JAVAJIGI, new Money(sessionFee - 1)); + }); + + assertThat(e).hasMessage("수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능합니다."); + } + +} diff --git a/src/test/java/nextstep/courses/domain/SessionBuilder.java b/src/test/java/nextstep/courses/domain/SessionBuilder.java new file mode 100644 index 0000000000..2c9065ee41 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/SessionBuilder.java @@ -0,0 +1,61 @@ +package nextstep.courses.domain; + +public class SessionBuilder { + private Long id = 1L; + private Course course; + private CoverImage coverImage = CoverImageTest.createCoverImage1(); + private SessionStatus sessionStatus; + private RegistrationPolicy registrationPolicy; + private SessionPeriod sessionPeriod = SessionPeriodTest.createSessionPeriod1(); + + public SessionBuilder id(Long id) { + this.id = id; + return this; + } + + public SessionBuilder course(Course course) { + this.course = course; + return this; + } + + public SessionBuilder coverImage(CoverImage coverImage) { + this.coverImage = coverImage; + return this; + } + + public SessionBuilder sessionStatus(SessionStatus sessionStatus) { + this.sessionStatus = sessionStatus; + return this; + } + + public SessionBuilder paid(int sessionFee, int maxStudentCount) { + this.registrationPolicy = new PaidRegistrationPolicy(sessionFee, maxStudentCount); + return this; + } + + public SessionBuilder free() { + this.registrationPolicy = new FreeRegistrationPolicy(); + return this; + } + + public SessionBuilder sessionPeriod(SessionPeriod sessionPeriod) { + this.sessionPeriod = sessionPeriod; + return this; + } + + public Session build() { + Session session = new Session( + id, + coverImage, + sessionStatus, + registrationPolicy, + sessionPeriod + ); + + if (course != null) { + session.toCourse(course); + } + + return session; + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/SessionPeriodTest.java b/src/test/java/nextstep/courses/domain/SessionPeriodTest.java new file mode 100644 index 0000000000..c7dc871c0d --- /dev/null +++ b/src/test/java/nextstep/courses/domain/SessionPeriodTest.java @@ -0,0 +1,15 @@ +package nextstep.courses.domain; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +class SessionPeriodTest { + + public static SessionPeriod createSessionPeriod1() { + LocalDateTime startedAt = LocalDateTime.of(2023, 10, 1, 0, 0, 0); + LocalDateTime endedAt = LocalDateTime.of(2023, 10, 1, 23, 0, 0); + return new SessionPeriod(startedAt, endedAt); + } + +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/SessionTest.java b/src/test/java/nextstep/courses/domain/SessionTest.java new file mode 100644 index 0000000000..df34768844 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/SessionTest.java @@ -0,0 +1,58 @@ +package nextstep.courses.domain; + +import java.time.LocalDateTime; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +import nextstep.payments.domain.Payment; +import nextstep.users.domain.NsUser; +import nextstep.users.domain.NsUserTest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class SessionTest { + + @ParameterizedTest + @EnumSource(value = SessionStatus.class, mode = EnumSource.Mode.EXCLUDE, names = "RECRUITING") + void 강의_수강신청은_강의_상태가_모집중일_때만_가능하다(SessionStatus sessionStatus) { + int sessionFee = 20000; + int maxStudentCount = 1; + + Session session = new SessionBuilder() + .paid(sessionFee, maxStudentCount) + .sessionStatus(sessionStatus) + .build(); + + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> session.register(NsUserTest.JAVAJIGI, new Money(sessionFee))); + + assertThat(e).hasMessage("수강신청이 불가능한 상태입니다."); + } + + @Test + void register하면_Payment객체를_응답한다() { + int sessionFee = 20000; + int maxStudentCount = 1; + NsUser loginUser = NsUserTest.JAVAJIGI; + + Session session = new SessionBuilder() + .paid(sessionFee, maxStudentCount) + .sessionStatus(SessionStatus.RECRUITING) + .build(); + + Payment payment = session.register(loginUser, new Money(sessionFee)); + + assertThat(payment.getSession()).isEqualTo(session); + assertThat(payment.getNsUser()).isEqualTo(loginUser); + assertThat(payment.getAmount()).isEqualTo(new Money(sessionFee)); + } + + + +} diff --git a/src/test/java/nextstep/qna/service/QnaServiceTest.java b/src/test/java/nextstep/qna/service/QnaServiceTest.java index cb5a60a448..23227010d4 100644 --- a/src/test/java/nextstep/qna/service/QnaServiceTest.java +++ b/src/test/java/nextstep/qna/service/QnaServiceTest.java @@ -83,8 +83,8 @@ public void setUp() throws Exception { private void verifyDeleteHistories() { List<DeleteHistory> deleteHistories = Arrays.asList( - new DeleteHistory(ContentType.QUESTION, question.getId(), question.getWriter(), LocalDateTime.now()), - new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now())); + DeleteHistoryFactory.ofQuestion(question.getId(), question.getWriter(), LocalDateTime.now()), + DeleteHistoryFactory.ofAnswer(answer.getId(), answer.getWriter(), LocalDateTime.now())); verify(deleteHistoryService).saveAll(deleteHistories); } }