diff --git a/README.md b/README.md index 069f3af51c..c58093a8e5 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,43 @@ - [x] question를 add 하면 question에 대한 DeleteHistory 를 만들어 리스트에 추가한다. - [x] answer을 add 하면 question에 대한 DeleteHistory를 만들어 리스트에 추가한다. + +## 수강신청 2단계(도메인 설계) 기능 목록 +### Student(학생) +- [x] 학생은 수강신청을 할 수 있다. +- [x] 수강 신청시 강의(Session)의 현재 수강 인원이 1 증가된다. +- [x] 수강신청시 최대 인원을 넘게 되면 예외를 던진다. +- [x] 수강 신청은 강의(Session) 의 상태가 `모집중` 일 때만 가능하다. + - [x] `모집중`이 아닐 경우 예외를 던진다. + +### Course(과정) +- [x] 강의를 과정에 추가하고, 기수에 따라 강의를 조회할 수 있다. +- [x] 기수가 유효하지 않다면 (음수 or 초과 기수 조회) 예외를 던진다. + +### Session(강의) +- [x] 시작일과 종료일을 가진다. +- [x] 강의 커버 이미지 정보를 가진다. +- type(타입) + - [x] 무료, 유료 강의 2가지 값을 가진다(enum) +- status(상태) + - [x] 준비중, 모집중, 종료 3가지를 가진다(enum) + +### SessionEnrollment(등록) +- [x] 이미 등록을 한 학생은 예외를 던진다. +- [x] 수용인원이 꽉 찬 강의에 등록시도를 할 경에 예외를 던진다. + - [x] 수강신청시 최대 수강인원을 넘지 않을 경우 예외를 던지지 않는다. +- [x] 학생은 수강신청시 강의의 현재 수강인원이 1이 증가한다. +- 강의 상태에 따라 등록상태가 다르다. + - [x] 강의 상태(SessionStatus) '모집중'일 경우에 수강신청이 가능하다(예외를 던지지 않는다) + - [x] 강의 상태(SessionStatus) '준비중'일 경우에 수깅신청시 예외를 던진다. + - [x] 강의 상태(SessionStatus) '종료'일 경우에 수깅신청시 예외를 던진다. + + +### 수강 신청 기능 요구사항 +- 과정(Course)은 기수 단위로 여러 개의 강의(Session)를 가질 수 있다. +- 강의는 시작일과 종료일을 가진다. +- 강의는 강의 커버 이미지 정보를 가진다. +- 강의는 무료 강의와 유료 강의로 나뉜다. +- 강의 상태는 준비중, 모집중, 종료 3가지 상태를 가진다. +- 강의 수강신청은 강의 상태가 모집중일 때만 가능하다. +- 강의는 강의 최대 수강 인원을 초과할 수 없다. \ No newline at end of file diff --git a/src/main/java/nextstep/courses/domain/AlreadyEnrollmentException.java b/src/main/java/nextstep/courses/domain/AlreadyEnrollmentException.java new file mode 100644 index 0000000000..117b58ecd4 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/AlreadyEnrollmentException.java @@ -0,0 +1,8 @@ +package nextstep.courses.domain; + +public class AlreadyEnrollmentException extends RuntimeException{ + + public AlreadyEnrollmentException(String message) { + super(message); + } +} diff --git a/src/main/java/nextstep/courses/domain/CannotEnrollException.java b/src/main/java/nextstep/courses/domain/CannotEnrollException.java new file mode 100644 index 0000000000..17d709db99 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/CannotEnrollException.java @@ -0,0 +1,8 @@ +package nextstep.courses.domain; + +public class CannotEnrollException extends RuntimeException{ + + public CannotEnrollException(String message) { + super(message); + } +} diff --git a/src/main/java/nextstep/courses/domain/Course.java b/src/main/java/nextstep/courses/domain/Course.java index 0f69716043..ad58b93788 100644 --- a/src/main/java/nextstep/courses/domain/Course.java +++ b/src/main/java/nextstep/courses/domain/Course.java @@ -1,6 +1,8 @@ package nextstep.courses.domain; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; public class Course { private Long id; @@ -9,6 +11,8 @@ public class Course { private Long creatorId; + private final List sessions = new ArrayList<>(); + private LocalDateTime createdAt; private LocalDateTime updatedAt; @@ -40,12 +44,39 @@ public LocalDateTime getCreatedAt() { return createdAt; } + public Session getSession(int generation) { + validateGeneration(generation); + return this.sessions.get(generation - 1); + } + + public void addSession(Session session) { + this.sessions.add(session); + } + + private void validateGeneration(int generation) { + validateNegative(generation); + validateRange(generation); + } + + private void validateNegative(int generation) { + if (generation <= 0) { + throw new IllegalArgumentException("기수는 1 기수 이상부터 시작합니다. 조회한 기수 = " + generation); + } + } + + private void validateRange(int generation) { + if (sessions.size() < generation) { + throw new IllegalArgumentException("해당 기수의 강의는 존재하지 않습니다. 조회한 기수 = " + generation); + } + } + @Override public String toString() { return "Course{" + "id=" + id + ", title='" + title + '\'' + ", creatorId=" + creatorId + + ", sessions=" + sessions + ", createdAt=" + createdAt + ", updatedAt=" + updatedAt + '}'; 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..aa3a6d7570 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -0,0 +1,38 @@ +package nextstep.courses.domain; + +import java.time.LocalDateTime; + +public class Session { + + private final SessionInfo sessionInfo; + private final SessionEnrollment sessionEnrollment; + private final SessionTimeLine sessionTimeLine; + + public Session(Long courseId, Long ownerId, String title, String coverImageInfo, + SessionType sessionType, SessionStatus sessionStatus, + LocalDateTime createdAt, LocalDateTime closedAt, Long maxNumOfStudent) { + + this(new SessionInfo(courseId, ownerId, title, coverImageInfo, sessionType), + new SessionEnrollment(sessionStatus, maxNumOfStudent), + new SessionTimeLine(createdAt, closedAt)); + } + + public Session(SessionInfo sessionInfo, SessionEnrollment sessionEnrollment, + SessionTimeLine sessionTimeLine){ + this.sessionInfo = sessionInfo; + this.sessionEnrollment = sessionEnrollment; + this.sessionTimeLine = sessionTimeLine; + } + + public void enroll(Student student) { + sessionEnrollment.enroll(student); + } + + public Long totalStudentNum() { + return sessionEnrollment.totalStudentNum(); + } + + public boolean isPositionFull() { + return sessionEnrollment.isPositionFull(); + } +} diff --git a/src/main/java/nextstep/courses/domain/SessionEnrollment.java b/src/main/java/nextstep/courses/domain/SessionEnrollment.java new file mode 100644 index 0000000000..e9d7f9c26d --- /dev/null +++ b/src/main/java/nextstep/courses/domain/SessionEnrollment.java @@ -0,0 +1,41 @@ +package nextstep.courses.domain; + +import java.util.HashSet; +import java.util.Set; + +public class SessionEnrollment { + + private final SessionStatus sessionStatus; + private final Set students = new HashSet<>(); + private final Long capacity; + + public SessionEnrollment(SessionStatus sessionStatus, Long capacity) { + this.sessionStatus = sessionStatus; + this.capacity = capacity; + } + + public void enroll(Student student) { + if (!sessionStatus.canJoin()) { + throw new CannotEnrollException("현재는 수강신청을 할 수 없는 강의 상태입니다. 현재 강의 상태 = " + sessionStatus.name()); + } + + if (isPositionFull()) { + throw new CannotEnrollException( + "현재 강의(Session)는 수강인원이 꽉 차서 더 이상 등록할 수 없습니다." + "최대인원 = " + capacity); + } + + if (students.contains(student)) { + throw new AlreadyEnrollmentException(student + " 학생은 이미 등록한 상태입니다."); + } + this.students.add(student); + } + + public Long totalStudentNum() { + return Long.valueOf(students.size()); + } + + public boolean isPositionFull() { + return totalStudentNum() == capacity; + } + +} diff --git a/src/main/java/nextstep/courses/domain/SessionInfo.java b/src/main/java/nextstep/courses/domain/SessionInfo.java new file mode 100644 index 0000000000..12f1ed1fd7 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/SessionInfo.java @@ -0,0 +1,18 @@ +package nextstep.courses.domain; + +public class SessionInfo { + + private final Long courseId; + private final Long ownerId; + private final String title; + private final String coverImageInfo; + private final SessionType sessionType; + + public SessionInfo(Long courseId, Long ownerId, String title, String coverImageInfo, SessionType sessionType) { + this.courseId = courseId; + this.ownerId = ownerId; + this.title = title; + this.coverImageInfo = coverImageInfo; + this.sessionType = sessionType; + } +} \ No newline at end of file diff --git a/src/main/java/nextstep/courses/domain/SessionRepository.java b/src/main/java/nextstep/courses/domain/SessionRepository.java new file mode 100644 index 0000000000..2dcb8f48d6 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/SessionRepository.java @@ -0,0 +1,12 @@ +package nextstep.courses.domain; + +import java.util.List; + +public interface SessionRepository { + + int save(String session); + + Session findById(Long id); + + List findByCourseId(Long courseId); +} 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..4b1fe92109 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/SessionStatus.java @@ -0,0 +1,20 @@ +package nextstep.courses.domain; + +public enum SessionStatus { + + READY("준비중"), + OPENED("모집중"), + CLSOED("종료") + ; + + private String name; + + SessionStatus(String name) { + this.name = name; + } + + public boolean canJoin() { + return this.equals(OPENED); + } + +} diff --git a/src/main/java/nextstep/courses/domain/SessionTimeLine.java b/src/main/java/nextstep/courses/domain/SessionTimeLine.java new file mode 100644 index 0000000000..bc4c785af5 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/SessionTimeLine.java @@ -0,0 +1,21 @@ +package nextstep.courses.domain; + +import java.time.LocalDateTime; + +public class SessionTimeLine { + + private LocalDateTime createAt; + private LocalDateTime closeAt; + + public SessionTimeLine(LocalDateTime createAt, LocalDateTime closeAt) { + validateInterval(createAt, closeAt); + this.createAt = createAt; + this.closeAt = closeAt; + } + + private void validateInterval(LocalDateTime createAt, LocalDateTime closeAt) { + if (createAt.isAfter(closeAt)) { + throw new IllegalArgumentException("강의 시작일과 마감일을 잘못 입력하였습니다."); + } + } +} diff --git a/src/main/java/nextstep/courses/domain/SessionType.java b/src/main/java/nextstep/courses/domain/SessionType.java new file mode 100644 index 0000000000..b1aa2216bc --- /dev/null +++ b/src/main/java/nextstep/courses/domain/SessionType.java @@ -0,0 +1,13 @@ +package nextstep.courses.domain; + +public enum SessionType { + + FREE("무료"), + CHANGED("유료") + ; + + private String name; + SessionType(String name) { + this.name = name; + } +} diff --git a/src/main/java/nextstep/courses/domain/Student.java b/src/main/java/nextstep/courses/domain/Student.java new file mode 100644 index 0000000000..8b71cc4589 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Student.java @@ -0,0 +1,16 @@ +package nextstep.courses.domain; + +public class Student { + + private final Long studentId; + private final Long sessionId; + + public Student(Long studentId, Long sessionId) { + this.studentId = studentId; + this.sessionId = sessionId; + } + + public void enroll(Session session) { + session.enroll(this); + } +} diff --git a/src/main/java/nextstep/courses/domain/StudentRepository.java b/src/main/java/nextstep/courses/domain/StudentRepository.java new file mode 100644 index 0000000000..4e82e98a99 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/StudentRepository.java @@ -0,0 +1,10 @@ +package nextstep.courses.domain; + +import java.util.List; + +public interface StudentRepository { + + int save(Student student); + + List findBySessionId(Long sessionId); +} diff --git a/src/main/java/nextstep/qna/domain/Answer.java b/src/main/java/nextstep/qna/domain/Answer.java index f6fff30f8d..e188d1fa80 100644 --- a/src/main/java/nextstep/qna/domain/Answer.java +++ b/src/main/java/nextstep/qna/domain/Answer.java @@ -71,8 +71,8 @@ public String toString() { public DeleteHistory delete() { isSameUser(); - this.setDeleted(true); - return DeleteHistory.createAnswer(this.id, this.writer); + this.deleted = true; + return DeleteHistory.createAnswer(this); } private void isSameUser() { @@ -80,26 +80,4 @@ private void isSameUser() { throw new CannotDeleteException("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다."); } } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Answer answer = (Answer) o; - return isDeleted() == answer.isDeleted() - && Objects.equals(getId(), answer.getId()) - && Objects.equals(getWriter(), answer.getWriter()) - && Objects.equals(question, answer.question) - && Objects.equals(getContents(), answer.getContents()) - && Objects.equals(createdDate, answer.createdDate); - } - - @Override - public int hashCode() { - return Objects.hash(getId(), getWriter(), question, getContents(), isDeleted(), createdDate); - } -} +} \ No newline at end of file diff --git a/src/main/java/nextstep/qna/domain/Answers.java b/src/main/java/nextstep/qna/domain/Answers.java index 5485ebc0e3..2c52409a66 100644 --- a/src/main/java/nextstep/qna/domain/Answers.java +++ b/src/main/java/nextstep/qna/domain/Answers.java @@ -6,7 +6,6 @@ import java.util.Objects; public class Answers implements Iterable { - private final List answers; public Answers() { @@ -24,6 +23,11 @@ public List getAnswers() { return this.answers; } + public DeleteHistories delete(DeleteHistories deleteHistories) { + answers.stream() + .forEach(answer -> deleteHistories.add(answer.delete())); + return deleteHistories; + } @Override public Iterator iterator() { return answers.iterator(); diff --git a/src/main/java/nextstep/qna/domain/DeleteHistory.java b/src/main/java/nextstep/qna/domain/DeleteHistory.java index fb58b97e57..f6e28f215f 100644 --- a/src/main/java/nextstep/qna/domain/DeleteHistory.java +++ b/src/main/java/nextstep/qna/domain/DeleteHistory.java @@ -29,12 +29,12 @@ public DeleteHistory(ContentType contentType, Long contentId, NsUser deletedBy) this.deletedBy = deletedBy; } - public static DeleteHistory createAnswer(Long id, NsUser writer) { - return new DeleteHistory(ContentType.ANSWER, id, writer); + public static DeleteHistory createAnswer(Answer answer) { + return new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter()); } - public static DeleteHistory createQuestion(Long id, NsUser writer) { - return new DeleteHistory(ContentType.QUESTION, id, writer); + public static DeleteHistory createQuestion(Question question) { + return new DeleteHistory(ContentType.QUESTION, question.getId(), question.getWriter()); } @Override diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java index 51d98e8d5a..7ddcce5aa3 100644 --- a/src/main/java/nextstep/qna/domain/Question.java +++ b/src/main/java/nextstep/qna/domain/Question.java @@ -41,10 +41,6 @@ public void addAnswer(Answer answer) { answers.add(answer); } - private void setDeleted(boolean deleted) { - this.deleted = deleted; - } - public boolean isDeleted() { return deleted; } @@ -53,15 +49,14 @@ public List getAnswers() { return answers.getAnswers(); } - @Override - public String toString() { - return "Question [id=" + getId() + ", title=" + title + ", contents=" + contents + ", writer=" + writer + "]"; - } - public DeleteHistories delete(NsUser loginUser) { isSameUser(loginUser); - this.setDeleted(true); - return documentedDeleteHistories(); + this.deleted = true; + + DeleteHistories deleteHistories = new DeleteHistories(); + deleteHistories.add(DeleteHistory.createQuestion(this)); + + return answers.delete(deleteHistories); } private void isSameUser(NsUser loginUser) { @@ -70,12 +65,8 @@ private void isSameUser(NsUser loginUser) { } } - private DeleteHistories documentedDeleteHistories() { - DeleteHistories deleteHistories = new DeleteHistories(); - deleteHistories.add(DeleteHistory.createQuestion(this.id, this.writer)); - - answers.getAnswers().stream() - .forEach(answer -> deleteHistories.add(answer.delete())); - return deleteHistories; + @Override + public String toString() { + return "Question [id=" + getId() + ", title=" + title + ", contents=" + contents + ", writer=" + writer + "]"; } } diff --git a/src/test/java/nextstep/courses/domain/CourseTest.java b/src/test/java/nextstep/courses/domain/CourseTest.java new file mode 100644 index 0000000000..6a910d13bd --- /dev/null +++ b/src/test/java/nextstep/courses/domain/CourseTest.java @@ -0,0 +1,71 @@ +package nextstep.courses.domain; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.time.LocalDateTime; + +import static org.assertj.core.api.Assertions.*; + +public class CourseTest { + + private Course course; + private Session session1; + private Session session2; + private Session session3; + + @BeforeEach + void setUp() { + this.course = new Course(); + Long courseId = 1L; + SessionInfo sessionInfo = new SessionInfo(courseId, 1L, "title1", "coverImageInfo", SessionType.FREE); + SessionStatus sessionStatus = SessionStatus.OPENED; + SessionEnrollment sessionEnrollment = new SessionEnrollment(sessionStatus, 3L); + SessionTimeLine sessionTimeLine = new SessionTimeLine(LocalDateTime.now(), LocalDateTime.now().plusDays(10)); + + session1 = new Session(sessionInfo, sessionEnrollment, sessionTimeLine); + session2 = new Session(sessionInfo, sessionEnrollment, sessionTimeLine); + session3 = new Session(sessionInfo, sessionEnrollment, sessionTimeLine); + + course.addSession(session1); + course.addSession(session2); + course.addSession(session3); + } + + @Test + @DisplayName("기수에 따른 강의를 조회할 수 있다.") + void findSession_FromCourse_ContainExactly() { + Session session = course.getSession(1); + + assertThat(session).isEqualTo(this.session1); + } + + @ParameterizedTest + @DisplayName("기수에 따른 존재하는 강의를 조회시 예외를 던지지 않는다.") + @ValueSource(ints = {1, 2, 3}) + void findSession_FromCourseInExists_NoException(int generation) { + assertThatNoException() + .isThrownBy(() -> course.getSession(generation)); + } + + @ParameterizedTest + @DisplayName("유효하지 않는 기수(음수, 0)를 조회시 예외를 던진다.") + @ValueSource(ints = {-2, -1, 0}) + void findSession_ByInvalidValue_ThrowException(int generation) { + assertThatIllegalArgumentException() + .isThrownBy(() -> course.getSession(generation)) + .withMessageContaining("기수는 1 기수 이상부터 시작합니다."); + } + + @Test + @DisplayName("존재하지 않는 기수 강의를 조회시 예의를 던진다.") + void findSession_IfNotExists_ThrowException() { + assertThatIllegalArgumentException() + .isThrownBy(() -> course.getSession(4)) + .withMessageContaining("해당 기수의 강의는 존재하지 않습니다."); + + } +} diff --git a/src/test/java/nextstep/courses/domain/SessionCreator.java b/src/test/java/nextstep/courses/domain/SessionCreator.java new file mode 100644 index 0000000000..b2f1a3c78c --- /dev/null +++ b/src/test/java/nextstep/courses/domain/SessionCreator.java @@ -0,0 +1,14 @@ +package nextstep.courses.domain; + +import java.time.LocalDateTime; + +public class SessionCreator { + + public static Session create(Long maxNumberOfStudent, SessionStatus sessionStatus) { + SessionInfo sessionInfo = new SessionInfo(1L, 0L, "titleName", + "coverImage", SessionType.FREE); + SessionEnrollment sessionEnrollment = new SessionEnrollment(sessionStatus, maxNumberOfStudent); + SessionTimeLine sessionTimeLine = new SessionTimeLine(LocalDateTime.now(), LocalDateTime.now().plusDays(10)); + return new Session(sessionInfo, sessionEnrollment, sessionTimeLine); + } +} diff --git a/src/test/java/nextstep/courses/domain/SessionEnrollmentTest.java b/src/test/java/nextstep/courses/domain/SessionEnrollmentTest.java new file mode 100644 index 0000000000..a9e003271f --- /dev/null +++ b/src/test/java/nextstep/courses/domain/SessionEnrollmentTest.java @@ -0,0 +1,98 @@ +package nextstep.courses.domain; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +public class SessionEnrollmentTest { + + Student june1; + Student june2; + Student june3; + + @BeforeEach + void setUp() { + june1 = new Student(0L, 10L); + june2 = new Student(0L, 10L); + june3 = new Student(0L, 10L); + } + + @Test + @DisplayName("학생은 수강신청시 강의의 현재 수강인원이 1이 증가한다.") + void enroll_SessionMaxNumberOfValue_PlusOne() { + SessionEnrollment sessionEnrollment = + new SessionEnrollment(SessionStatus.OPENED, 3l); + + sessionEnrollment.enroll(june1); + + assertThat(sessionEnrollment.totalStudentNum()).isEqualTo(1l); + } + + @Test + @DisplayName("수강신청시 최대 수강신청 인원을 넘을 경우 예외를 던진다.") + void enroll_OutOfMaxNumberOfStudent_ThrowException() { + SessionEnrollment sessionEnrollment = + new SessionEnrollment(SessionStatus.OPENED, 0l); + + assertThatThrownBy(() -> sessionEnrollment.enroll(june1)) + .isInstanceOf(CannotEnrollException.class) + .hasMessageContaining("현재 강의(Session)는 수강인원이 꽉 차서 더 이상 등록할 수 없습니다."); + } + + @Test + @DisplayName("수강신청시 최대 수강인원을 넘지 않을 경우 예외를 던지지 않는다.") + void enroll_LessThanMaxNumberOfStudent_NoException() { + SessionEnrollment sessionEnrollment = + new SessionEnrollment(SessionStatus.OPENED, 3l); + + assertThatNoException().isThrownBy(() -> sessionEnrollment.enroll(june1)); + assertThatNoException().isThrownBy(() -> sessionEnrollment.enroll(june2)); + assertThatNoException().isThrownBy(() -> sessionEnrollment.enroll(june3)); + } + + @Test + @DisplayName("강의 상태(SessionStatus) '모집중'일 경우에 수강신청이 가능하다(예외를 던지지 않는다).") + void enroll_SessionStatus_OPENED_NoException() { + SessionEnrollment sessionEnrollment = + new SessionEnrollment(SessionStatus.OPENED, 3l); + + assertThatNoException().isThrownBy(() -> sessionEnrollment.enroll(june1)); + } + + @Test + @DisplayName("강의 상태(SessionStatus) '준비중'일 경우에 수깅신청시 예외를 던진다.") + void enroll_SessionStatus_READY_ThrowException() { + SessionEnrollment sessionEnrollment = + new SessionEnrollment(SessionStatus.READY, 3l); + + assertThatThrownBy(() -> sessionEnrollment.enroll(june1)) + .isInstanceOf(CannotEnrollException.class) + .hasMessageContaining("현재는 수강신청을 할 수 없는 강의 상태입니다."); + } + + @Test + @DisplayName("강의 상태(SessionStatus) '종료'일 경우에 수깅신청시 예외를 던진다.") + void enroll_SessionStatus_CLOSED_ThrowException() { + SessionEnrollment sessionEnrollment = + new SessionEnrollment(SessionStatus.CLSOED, 3l); + + assertThatThrownBy(() -> sessionEnrollment.enroll(june1)) + .isInstanceOf(CannotEnrollException.class) + .hasMessageContaining("현재는 수강신청을 할 수 없는 강의 상태입니다."); + } + + @Test + @DisplayName("이미 등록된 학생의 경우 예외를 던진다.") + void enroll_AlreadyEnrollStudent_Duplicate_ThrowException() { + SessionEnrollment sessionEnrollment = + new SessionEnrollment(SessionStatus.OPENED, 3l); + + sessionEnrollment.enroll(june1); + + assertThatThrownBy(() -> sessionEnrollment.enroll(june1)) + .isInstanceOf(AlreadyEnrollmentException.class) + .hasMessageContaining("학생은 이미 등록한 상태입니다."); + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/SessionTimeLineTest.java b/src/test/java/nextstep/courses/domain/SessionTimeLineTest.java new file mode 100644 index 0000000000..3e9e4dbe0e --- /dev/null +++ b/src/test/java/nextstep/courses/domain/SessionTimeLineTest.java @@ -0,0 +1,30 @@ +package nextstep.courses.domain; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +public class SessionTimeLineTest { + + @Test + @DisplayName("강의 시작일이 강의 종료일보다 빠르면 예외를 던지지 않는다.") + void create_CreateAtBeforeThanClosedAt_NoException() { + LocalDateTime createAt = LocalDateTime.now(); + LocalDateTime closeAt = createAt.plusDays(5); + + Assertions.assertThatNoException() + .isThrownBy(() -> new SessionTimeLine(createAt, closeAt)); + } + + @Test + @DisplayName("강의 시작일이 강의 종료일보다 느리면 예외를 던진다.") + void create_CreateAtAfterThanClosedAt_NoException() { + LocalDateTime createAt = LocalDateTime.now(); + LocalDateTime closeAt = createAt.minusDays(5); + + Assertions.assertThatIllegalArgumentException() + .isThrownBy(() -> new SessionTimeLine(createAt, closeAt)); + } +} diff --git a/src/test/java/nextstep/courses/domain/StudentTest.java b/src/test/java/nextstep/courses/domain/StudentTest.java new file mode 100644 index 0000000000..1ff488b6f7 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/StudentTest.java @@ -0,0 +1,80 @@ +package nextstep.courses.domain; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +public class StudentTest { + + Student june1; + Student june2; + Student june3; + Session session; + + @BeforeEach + void setUp() { + june1 = new Student(0L, 10L); + june2 = new Student(0L, 10L); + june3 = new Student(0L, 10L); + session = SessionCreator.create(2L, SessionStatus.OPENED); + } + + @Test + @DisplayName("학생은 수강신청시 강의의 현재 수강인원이 1이 증가한다.") + void enroll_SessionMaxNumberOfValue_PlusOne() { + june1.enroll(session); + assertThat(session.totalStudentNum()).isEqualTo(1); + } + + @Test + @DisplayName("수강신청시 최대 수강신청 인원을 넘을 경우 예외를 던진다.") + void enroll_OutOfMaxNumberOfStudent_ThrowException() { + session.enroll(june1); + session.enroll(june2); + + assertThatThrownBy(() -> session.enroll(june3)) + .isInstanceOf(CannotEnrollException.class) + .hasMessageContaining("현재 강의(Session)는 수강인원이 꽉 차서 더 이상 등록할 수 없습니다."); + } + + @Test + @DisplayName("수강신청시 최대 수강인원을 넘지 않을 경우 예외를 던지지 않는다.") + void enroll_LessThanMaxNumberOfStudent_NoException() { + Session session = SessionCreator.create(3L, SessionStatus.OPENED); + + assertThatNoException().isThrownBy(() -> session.enroll(june1)); + assertThatNoException().isThrownBy(() -> session.enroll(june2)); + assertThatNoException().isThrownBy(() -> session.enroll(june3)); + } + + @Test + @DisplayName("강의 상태(SessionStatus) '모집중'일 경우에 수강신청이 가능하다(예외를 던지지 않는다.") + void enroll_SessionStatus_OPENED_NoException() { + Session session = SessionCreator.create(3L, SessionStatus.OPENED); + + assertThatNoException().isThrownBy(() -> session.enroll(june1)); + } + + @Test + @DisplayName("강의 상태(SessionStatus) '준비중'일 경우에 수깅신청시 예외를 던진다.") + void enroll_SessionStatus_READY_ThrowException() { + Session session = SessionCreator.create(3L, SessionStatus.READY); + + assertThatThrownBy(() -> session.enroll(june1)) + .isInstanceOf(CannotEnrollException.class); + + } + + @Test + @DisplayName("강의 상태(SessionStatus) '종료'일 경우에 수깅신청시 예외를 던진다.") + void enroll_SessionStatus_CLOSED_ThrowException() { + Session session = SessionCreator.create(3L, SessionStatus.CLSOED); + + assertThatThrownBy(() -> session.enroll(june1)) + .isInstanceOf(CannotEnrollException.class); + } + + +} diff --git a/src/test/java/nextstep/qna/domain/DeleteHistoriesTest.java b/src/test/java/nextstep/qna/domain/DeleteHistoriesTest.java index cbacaaa894..4ec15c617d 100644 --- a/src/test/java/nextstep/qna/domain/DeleteHistoriesTest.java +++ b/src/test/java/nextstep/qna/domain/DeleteHistoriesTest.java @@ -26,7 +26,7 @@ void setUp() { @DisplayName("Question을 추가했을 경우 Question에 대한 DeleteHistory를 만들어 리스트에 추가한다.") @Test void addQuestion_CreateDeleteHistory_And_AddDeleteHistorylist() { - deleteHistories.add(DeleteHistory.createQuestion(Q1.getId(), Q1.getWriter())); + deleteHistories.add(DeleteHistory.createQuestion(Q1)); DeleteHistory deleteHistoryQ1 = new DeleteHistory(ContentType.QUESTION, Q1.getId(), Q1.getWriter()); @@ -38,8 +38,8 @@ void addQuestion_CreateDeleteHistory_And_AddDeleteHistorylist() { @DisplayName("Answer를 추가했을 경우 Answer에 대한 DeleteHistory를 만들어 리스트에 추가한다.") @Test void addAnswer_CreateDeleteHistory_And_AddDeleteHistorylist() { - deleteHistories.add(DeleteHistory.createAnswer(A1.getId(), A1.getWriter())); - deleteHistories.add(DeleteHistory.createAnswer(A2.getId(), A2.getWriter())); + deleteHistories.add(DeleteHistory.createAnswer(A1)); + deleteHistories.add(DeleteHistory.createAnswer(A2)); DeleteHistory deleteHistoryA1 = new DeleteHistory(ContentType.ANSWER, A1.getId(), A1.getWriter()); DeleteHistory deleteHistoryA2 = new DeleteHistory(ContentType.ANSWER, A2.getId(), A2.getWriter());