diff --git a/src/main/java/nextstep/common/domain/BaseEntity.java b/src/main/java/nextstep/common/domain/BaseEntity.java index ef5afe1e5..0a4d20543 100644 --- a/src/main/java/nextstep/common/domain/BaseEntity.java +++ b/src/main/java/nextstep/common/domain/BaseEntity.java @@ -8,20 +8,24 @@ public abstract class BaseEntity { private LocalDateTime createdDate; private LocalDateTime updatedDate; - protected BaseEntity(Long id) { - this(id, LocalDateTime.now(), LocalDateTime.now()); - } - - public BaseEntity(Long id, LocalDateTime createdDate, LocalDateTime updatedDate) { + protected BaseEntity(Long id, LocalDateTime createdDate, LocalDateTime updatedDate) { this.id = id; this.createdDate = createdDate; this.updatedDate = updatedDate; } - public Long getId() { + protected Long getId() { return id; } + protected LocalDateTime getCreatedDate() { + return createdDate; + } + + protected LocalDateTime getUpdatedDate() { + return updatedDate; + } + @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { diff --git a/src/main/java/nextstep/courses/cohort/domain/Cohort.java b/src/main/java/nextstep/courses/cohort/domain/Cohort.java new file mode 100644 index 000000000..2890ca32b --- /dev/null +++ b/src/main/java/nextstep/courses/cohort/domain/Cohort.java @@ -0,0 +1,151 @@ +package nextstep.courses.cohort.domain; + +import static java.util.Objects.isNull; + +import java.time.LocalDateTime; +import java.util.Objects; +import nextstep.common.domain.BaseEntity; +import nextstep.courses.cohort.domain.enumeration.CohortStateType; + +public class Cohort extends BaseEntity { + + private final Long courseId; + private int cohortCount; + private CohortStudentCount cohortStudentCount; + private CohortState cohortState2; + + public Cohort( + Long courseId, + int cohortCount, + int maxStudentCount, + int presentStudentCount, + CohortStateType cohortStateType, + LocalDateTime registerStartDate, + LocalDateTime registerEndDate, + LocalDateTime cohortStartDate, + LocalDateTime cohortEndDate + ) { + this(0L, courseId, cohortCount, maxStudentCount, presentStudentCount, + cohortStateType, registerStartDate, registerEndDate, cohortStartDate, + cohortEndDate, null, null); + } + + public Cohort( + Long courseId, + int cohortCount, + int maxStudentCount, + int presentStudentCount, + LocalDateTime registerStartDate, + LocalDateTime registerEndDate, + LocalDateTime cohortStartDate, + LocalDateTime cohortEndDate + ) { + this(0L, courseId, cohortCount, maxStudentCount, presentStudentCount, + CohortStateType.PREPARE, registerStartDate, registerEndDate, cohortStartDate, + cohortEndDate, null, null); + } + + public Cohort( + Long id, + Long courseId, + int cohortCount, + int maxStudentCount, + int presentStudentCount, + CohortStateType cohortStateType, + LocalDateTime registerStartDate, + LocalDateTime registerEndDate, + LocalDateTime cohortStartDate, + LocalDateTime cohortEndDate, + LocalDateTime createdDate, + LocalDateTime updatedDate + ) { + super(id, createdDate, updatedDate); + if (courseId <= 0L) { + throw new IllegalArgumentException("기수는 관련 코스정보가 필수 입니다."); + } + + if (cohortCount <= 0) { + throw new IllegalArgumentException("기수는 회차정보가 필수 입니다."); + } + + this.courseId = courseId; + this.cohortCount = cohortCount; + this.cohortStudentCount = new CohortStudentCount(maxStudentCount, presentStudentCount); + this.cohortState2 = new CohortState(cohortStateType, + new Period(registerStartDate, registerEndDate), + new Period(cohortStartDate, cohortEndDate)); + } + + public boolean isCanResist() { + if (!this.cohortState2.isSameState(CohortStateType.RECRUIT)) { + return false; + } + + return this.cohortStudentCount.isNotOverMax(); + } + + public boolean putOnRecruitEnd(LocalDateTime now) { + if (isNull(now)) { + return false; + } + + if (this.cohortState2.isBeforeRecruitPeriod(now)) { + return false; + } + + if (!this.cohortState2.isSameState(CohortStateType.RECRUIT)) { + return false; + } + + if (this.cohortState2.isInRecruitPeriod(now) && this.cohortStudentCount.isNotOverMax()) { + return false; + } + + this.cohortState2.changeRecruitEnd(); + return true; + } + + public boolean isSameCourseId(Long courseId) { + if (isNull(courseId)) { + return false; + } + + return this.courseId.equals(courseId); + } + + public void registerStudent() { + if (!isCanResist()) { + return; + } + + this.cohortStudentCount.plusOneCountAtPresent(); + } + + public boolean isCohortStateType(CohortStateType cohortStateType) { + return this.cohortState2.isSameState(cohortStateType); + } + + public Long getId() { + return super.getId(); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + Cohort cohort = (Cohort) o; + return cohortCount == cohort.cohortCount && Objects.equals(courseId, cohort.courseId) + && Objects.equals(cohortStudentCount, cohort.cohortStudentCount) + && Objects.equals(cohortState2, cohort.cohortState2); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), courseId, cohortCount, cohortStudentCount, + cohortState2); + } +} diff --git a/src/main/java/nextstep/courses/cohort/domain/CohortState.java b/src/main/java/nextstep/courses/cohort/domain/CohortState.java new file mode 100644 index 000000000..e2a3cf98d --- /dev/null +++ b/src/main/java/nextstep/courses/cohort/domain/CohortState.java @@ -0,0 +1,38 @@ +package nextstep.courses.cohort.domain; + +import static java.util.Objects.isNull; + +import java.time.LocalDateTime; +import nextstep.courses.cohort.domain.enumeration.CohortStateType; + +public class CohortState { + private CohortStateType cohortStateType; + private Period registerPeriod; + private Period cohortPeriod; + + public CohortState(CohortStateType cohortStateType, Period registerPeriod, Period cohortPeriod) { + this.cohortStateType = cohortStateType; + this.registerPeriod = registerPeriod; + this.cohortPeriod = cohortPeriod; + } + + public boolean isSameState(CohortStateType cohortStateType) { + if (isNull(cohortStateType)) { + return false; + } + + return this.cohortStateType.equals(cohortStateType); + } + + public boolean isInRecruitPeriod(LocalDateTime now) { + return this.registerPeriod.isPeriodIn(now); + } + + public boolean isBeforeRecruitPeriod(LocalDateTime now) { + return this.registerPeriod.isBeforeStartDate(now); + } + + public void changeRecruitEnd() { + this.cohortStateType = CohortStateType.RECRUIT_END; + } +} diff --git a/src/main/java/nextstep/courses/cohort/domain/CohortStudentCount.java b/src/main/java/nextstep/courses/cohort/domain/CohortStudentCount.java new file mode 100644 index 000000000..656de36dc --- /dev/null +++ b/src/main/java/nextstep/courses/cohort/domain/CohortStudentCount.java @@ -0,0 +1,32 @@ +package nextstep.courses.cohort.domain; + +public class CohortStudentCount { + + private int maxStudentCount; + private int presentStudentCount; + + public CohortStudentCount(int maxStudentCount, int presentStudentCount) { + if (maxStudentCount < 1) { + throw new IllegalArgumentException("수강가능 총인원은 1이상이어야 합니다"); + } + + if (presentStudentCount < 0) { + throw new IllegalArgumentException("현재 신청인원은 음수일수 없습니다"); + } + + this.maxStudentCount = maxStudentCount; + this.presentStudentCount = presentStudentCount; + } + + public boolean isNotOverMax() { + return this.maxStudentCount > this.presentStudentCount; + } + + public void plusOneCountAtPresent() { + if (!isNotOverMax()) { + return; + } + + this.presentStudentCount++; + } +} diff --git a/src/main/java/nextstep/courses/cohort/domain/Cohorts.java b/src/main/java/nextstep/courses/cohort/domain/Cohorts.java new file mode 100644 index 000000000..8ed597e3c --- /dev/null +++ b/src/main/java/nextstep/courses/cohort/domain/Cohorts.java @@ -0,0 +1,11 @@ +package nextstep.courses.cohort.domain; + +import java.util.List; + +public class Cohorts { + private List cohorts; + + public Cohorts(List cohorts) { + this.cohorts = cohorts; + } +} diff --git a/src/main/java/nextstep/courses/cohort/domain/Period.java b/src/main/java/nextstep/courses/cohort/domain/Period.java new file mode 100644 index 000000000..e9a811442 --- /dev/null +++ b/src/main/java/nextstep/courses/cohort/domain/Period.java @@ -0,0 +1,38 @@ +package nextstep.courses.cohort.domain; + +import static java.util.Objects.isNull; + +import java.time.LocalDateTime; + +public class Period { + private final LocalDateTime startDate; + private final LocalDateTime endDate; + + public Period(LocalDateTime startDate, LocalDateTime endDate) { + if (isNull(startDate) || isNull(endDate)) { + throw new IllegalArgumentException("수강신청 시작일과 종료일은 필수값 입니다."); + } + + if (startDate.isAfter(endDate)) { + throw new IllegalArgumentException("수강신청 시작일이 종료일보다 미래일수는 없습니다"); + } + + this.startDate = startDate; + this.endDate = endDate; + } + + public boolean isPeriodIn(LocalDateTime targetDate) { + boolean startDateAfter = this.startDate.isBefore(targetDate); + boolean endDateBefore = this.endDate.isAfter(targetDate); + + return startDateAfter && endDateBefore; + } + + public boolean isOverEndDate(LocalDateTime targetDate) { + return this.endDate.isBefore(targetDate); + } + + public boolean isBeforeStartDate(LocalDateTime targetDate) { + return this.startDate.isAfter(targetDate); + } +} diff --git a/src/main/java/nextstep/courses/cohort/domain/enumeration/CohortStateType.java b/src/main/java/nextstep/courses/cohort/domain/enumeration/CohortStateType.java new file mode 100644 index 000000000..c58ea58b2 --- /dev/null +++ b/src/main/java/nextstep/courses/cohort/domain/enumeration/CohortStateType.java @@ -0,0 +1,18 @@ +package nextstep.courses.cohort.domain.enumeration; + +public enum CohortStateType { + PREPARE("준비중"), + RECRUIT("모집중"), + RECRUIT_END("모집마감"), + ACTIVE("진행중"), + END("종료"), + + ; + + private final String desc; + + CohortStateType(String desc) { + this.desc = desc; + } + +} diff --git a/src/main/java/nextstep/courses/cohort/domain/service/CohortDomainService.java b/src/main/java/nextstep/courses/cohort/domain/service/CohortDomainService.java new file mode 100644 index 000000000..274ae867a --- /dev/null +++ b/src/main/java/nextstep/courses/cohort/domain/service/CohortDomainService.java @@ -0,0 +1,34 @@ +package nextstep.courses.cohort.domain.service; + +import static java.util.Objects.isNull; + +import java.time.LocalDateTime; +import nextstep.courses.cohort.domain.Cohort; +import nextstep.courses.enrollment.domain.Enrollment; +import nextstep.qna.exception.unchecked.WrongRequestException; + +public class CohortDomainService { + + public Enrollment registerEnrollment(Cohort cohort, Long studentId, Long courseId) { + if (!cohort.isCanResist()) { + throw new WrongRequestException("해당기수는 수강신청 할 수 없는 상태입니다"); + } + if (!cohort.isSameCourseId(courseId)) { + throw new IllegalArgumentException("결제정보와 강의정보가 상이합니다."); + } + + cohort.registerStudent(); + + // Cohort에서 studentId를 전달받아 Enrollment 반환 vs 외부에서 조합해서 Enrollment 반환하는 대신 id getter 오픈 + // Cohort에서 Enrollment를 생성하는 동작을 가지고 있는것 보다 외부에서 하는며 두객체의 결합도를 낮추는게 더 좋다고 생각합니다 + return new Enrollment(studentId, cohort.getId()); + } + + public void updateStateToRecruitEnd(Cohort cohort) { + if (isNull(cohort)) { + return; + } + + cohort.putOnRecruitEnd(LocalDateTime.now()); + } +} diff --git a/src/main/java/nextstep/courses/cohort/service/repository/CohortRepository.java b/src/main/java/nextstep/courses/cohort/service/repository/CohortRepository.java new file mode 100644 index 000000000..aec383612 --- /dev/null +++ b/src/main/java/nextstep/courses/cohort/service/repository/CohortRepository.java @@ -0,0 +1,13 @@ +package nextstep.courses.cohort.service.repository; + +import java.util.Optional; +import nextstep.courses.cohort.domain.Cohort; +import org.springframework.stereotype.Repository; + +@Repository +public interface CohortRepository { + + Optional findById(Long id); + + void update(Cohort cohort); +} diff --git a/src/main/java/nextstep/courses/course/domain/Course.java b/src/main/java/nextstep/courses/course/domain/Course.java new file mode 100644 index 000000000..af5df1097 --- /dev/null +++ b/src/main/java/nextstep/courses/course/domain/Course.java @@ -0,0 +1,95 @@ +package nextstep.courses.course.domain; + +import static java.util.Objects.isNull; +import static org.springframework.util.StringUtils.hasText; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import nextstep.common.domain.BaseEntity; +import nextstep.courses.cohort.domain.Cohorts; +import nextstep.courses.course.domain.enumaration.CourseChargeType; + +public class Course extends BaseEntity { + + private String title; + private Long creatorId; + private CourseChargeType courseChargeType; + private Cohorts cohorts; + + public Course(String title, Long creatorId) { + this(0L, title, creatorId, LocalDateTime.now(), null, CourseChargeType.PAID, + new Cohorts(new ArrayList<>())); + } + + public Course(String title, Long creatorId, CourseChargeType courseChargeType) { + this(0L, title, creatorId, LocalDateTime.now(), null, courseChargeType, + new Cohorts(new ArrayList<>())); + } + + public Course( + Long id, + String title, + Long creatorId, + LocalDateTime createdAt, + LocalDateTime updatedAt + ) { + this(id, title, creatorId, createdAt, updatedAt, CourseChargeType.PAID, + new Cohorts(new ArrayList<>())); + } + + public Course( + Long id, + String title, + Long creatorId, + LocalDateTime createdAt, + LocalDateTime updatedAt, + CourseChargeType courseChargeType, + Cohorts cohorts + ) { + super(id, createdAt, updatedAt); + + if (!hasText(title)) { + throw new IllegalArgumentException("강의제목은 필수값 입니다."); + } + + if (isNull(creatorId) || creatorId <= 0L) { + throw new IllegalArgumentException("강의 생성자 정보는 필수 값 입니다."); + } + + if (isNull(courseChargeType)) { + throw new IllegalArgumentException("강의 결제타입은 필수 값 입니다."); + } + + this.title = title; + this.creatorId = creatorId; + this.courseChargeType = courseChargeType; + this.cohorts = cohorts; + } + + public boolean isPaid() { + return this.courseChargeType.equals(CourseChargeType.PAID); + } + + public boolean isFree() { + return this.courseChargeType.equals(CourseChargeType.FREE); + } + + public String getTitle() { + return title; + } + + public Long getCreatorId() { + return creatorId; + } + + + @Override + public String toString() { + return "Course{" + + "id='" + super.getId() + '\'' + + "title='" + title + '\'' + + ", creatorId=" + creatorId + + ", courseChargeType=" + courseChargeType + + '}'; + } +} diff --git a/src/main/java/nextstep/courses/course/domain/enumaration/CourseChargeType.java b/src/main/java/nextstep/courses/course/domain/enumaration/CourseChargeType.java new file mode 100644 index 000000000..90353a16e --- /dev/null +++ b/src/main/java/nextstep/courses/course/domain/enumaration/CourseChargeType.java @@ -0,0 +1,13 @@ +package nextstep.courses.course.domain.enumaration; + +public enum CourseChargeType { + FREE("무료강의"), + PAID("유료강의"), + ; + + private final String desc; + + CourseChargeType(String desc) { + this.desc = desc; + } +} diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java b/src/main/java/nextstep/courses/course/infrastructure/JdbcCourseRepository.java similarity index 77% rename from src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java rename to src/main/java/nextstep/courses/course/infrastructure/JdbcCourseRepository.java index f9122cbe3..c3a6ebfd1 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java +++ b/src/main/java/nextstep/courses/course/infrastructure/JdbcCourseRepository.java @@ -1,7 +1,8 @@ -package nextstep.courses.infrastructure; +package nextstep.courses.course.infrastructure; -import nextstep.courses.domain.Course; -import nextstep.courses.domain.CourseRepository; +import java.util.Optional; +import nextstep.courses.course.domain.Course; +import nextstep.courses.course.service.repository.CourseRepository; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; @@ -20,11 +21,11 @@ public JdbcCourseRepository(JdbcOperations jdbcTemplate) { @Override public int save(Course course) { String sql = "insert into course (title, creator_id, created_at) values(?, ?, ?)"; - return jdbcTemplate.update(sql, course.getTitle(), course.getCreatorId(), course.getCreatedAt()); + return jdbcTemplate.update(sql, course.getTitle(), course.getCreatorId(), LocalDateTime.now()); } @Override - public Course findById(Long id) { + public Optional findById(Long id) { String sql = "select id, title, creator_id, created_at, updated_at from course where id = ?"; RowMapper rowMapper = (rs, rowNum) -> new Course( rs.getLong(1), @@ -32,7 +33,7 @@ public Course findById(Long id) { rs.getLong(3), toLocalDateTime(rs.getTimestamp(4)), toLocalDateTime(rs.getTimestamp(5))); - return jdbcTemplate.queryForObject(sql, rowMapper, id); + return Optional.of(jdbcTemplate.queryForObject(sql, rowMapper, id)); } private LocalDateTime toLocalDateTime(Timestamp timestamp) { diff --git a/src/main/java/nextstep/courses/course/service/repository/CourseRepository.java b/src/main/java/nextstep/courses/course/service/repository/CourseRepository.java new file mode 100644 index 000000000..209602b42 --- /dev/null +++ b/src/main/java/nextstep/courses/course/service/repository/CourseRepository.java @@ -0,0 +1,12 @@ +package nextstep.courses.course.service.repository; + +import java.util.Optional; +import nextstep.courses.course.domain.Course; +import org.springframework.stereotype.Repository; + +@Repository +public interface CourseRepository { + int save(Course course); + + Optional findById(Long id); +} diff --git a/src/main/java/nextstep/courses/domain/Course.java b/src/main/java/nextstep/courses/domain/Course.java deleted file mode 100644 index 0f6971604..000000000 --- a/src/main/java/nextstep/courses/domain/Course.java +++ /dev/null @@ -1,53 +0,0 @@ -package nextstep.courses.domain; - -import java.time.LocalDateTime; - -public class Course { - private Long id; - - private String title; - - private Long creatorId; - - private LocalDateTime createdAt; - - private LocalDateTime updatedAt; - - public Course() { - } - - public Course(String title, Long creatorId) { - this(0L, title, creatorId, LocalDateTime.now(), null); - } - - public Course(Long id, String title, Long creatorId, LocalDateTime createdAt, LocalDateTime updatedAt) { - this.id = id; - this.title = title; - this.creatorId = creatorId; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - } - - public String getTitle() { - return title; - } - - public Long getCreatorId() { - return creatorId; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - @Override - public String toString() { - return "Course{" + - "id=" + id + - ", title='" + title + '\'' + - ", creatorId=" + creatorId + - ", createdAt=" + createdAt + - ", updatedAt=" + updatedAt + - '}'; - } -} diff --git a/src/main/java/nextstep/courses/domain/CourseRepository.java b/src/main/java/nextstep/courses/domain/CourseRepository.java deleted file mode 100644 index 6aaeb638d..000000000 --- a/src/main/java/nextstep/courses/domain/CourseRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package nextstep.courses.domain; - -public interface CourseRepository { - int save(Course course); - - Course findById(Long id); -} diff --git a/src/main/java/nextstep/courses/enrollment/domain/Enrollment.java b/src/main/java/nextstep/courses/enrollment/domain/Enrollment.java new file mode 100644 index 000000000..57b672936 --- /dev/null +++ b/src/main/java/nextstep/courses/enrollment/domain/Enrollment.java @@ -0,0 +1,29 @@ +package nextstep.courses.enrollment.domain; + +import java.time.LocalDateTime; +import nextstep.common.domain.BaseEntity; + +public class Enrollment extends BaseEntity { + + private Long studentId; + private Long cohortId; + + public Enrollment( + Long studentId, + Long cohortId + ) { + this(0L, LocalDateTime.now(), LocalDateTime.now(), studentId, cohortId); + } + + public Enrollment( + Long id, + LocalDateTime createdDate, + LocalDateTime updatedDate, + Long studentId, + Long cohortId + ) { + super(id, createdDate, updatedDate); + this.studentId = studentId; + this.cohortId = cohortId; + } +} diff --git a/src/main/java/nextstep/courses/enrollment/service/EnrollmentService.java b/src/main/java/nextstep/courses/enrollment/service/EnrollmentService.java new file mode 100644 index 000000000..0dc761ea1 --- /dev/null +++ b/src/main/java/nextstep/courses/enrollment/service/EnrollmentService.java @@ -0,0 +1,61 @@ +package nextstep.courses.enrollment.service; + +import nextstep.courses.cohort.domain.Cohort; +import nextstep.courses.cohort.domain.service.CohortDomainService; +import nextstep.courses.cohort.service.repository.CohortRepository; +import nextstep.courses.course.domain.Course; +import nextstep.courses.course.service.repository.CourseRepository; +import nextstep.courses.enrollment.domain.Enrollment; +import nextstep.courses.enrollment.service.dto.EnrollmentSaveRequest; +import nextstep.courses.enrollment.service.repository.EnrollmentRepository; +import nextstep.payments.domain.Payment; +import nextstep.payments.service.repository.PaymentRepository; +import nextstep.qna.exception.unchecked.NotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class EnrollmentService { + + private final CourseRepository courseRepository; + private final EnrollmentRepository enrollmentRepository; + private final PaymentRepository paymentRepository; + private final CohortRepository cohortRepository; + + @Autowired + public EnrollmentService( + CourseRepository courseRepository, + EnrollmentRepository enrollmentRepository, + PaymentRepository paymentRepository, + CohortRepository cohortRepository + ) { + this.courseRepository = courseRepository; + this.enrollmentRepository = enrollmentRepository; + this.paymentRepository = paymentRepository; + this.cohortRepository = cohortRepository; + } + + @Transactional + public void saveEnrollment(EnrollmentSaveRequest request) { + Course course = courseRepository.findById(request.getCourseId()) + .orElseThrow(NotFoundException::new); + if (course.isPaid()) { + Payment payment = paymentRepository.findById(request.getPaymentId()) + .orElseThrow(NotFoundException::new); + if (!payment.isPayedCohort(request.getCohortId())) { + throw new IllegalArgumentException("결제정보와 강의정보가 상이합니다."); + } + } + + Cohort cohort = cohortRepository.findById(request.getCohortId()) + .orElseThrow(NotFoundException::new); + + CohortDomainService cohortDomainService = new CohortDomainService(); + Enrollment enrollment = cohortDomainService.registerEnrollment(cohort, request.getStudentId(), request.getCourseId()); + cohortDomainService.updateStateToRecruitEnd(cohort); + + enrollmentRepository.save(enrollment); + cohortRepository.update(cohort); + } +} diff --git a/src/main/java/nextstep/courses/enrollment/service/dto/EnrollmentSaveRequest.java b/src/main/java/nextstep/courses/enrollment/service/dto/EnrollmentSaveRequest.java new file mode 100644 index 000000000..674cf838c --- /dev/null +++ b/src/main/java/nextstep/courses/enrollment/service/dto/EnrollmentSaveRequest.java @@ -0,0 +1,38 @@ +package nextstep.courses.enrollment.service.dto; + +public class EnrollmentSaveRequest { + private final String paymentId; + private final Long courseId; + private final Long cohortId; + private final Long studentId; + private final Long sessionId; + + public EnrollmentSaveRequest(String paymentId, Long courseId, Long cohortId, Long studentId, + Long sessionId) { + this.paymentId = paymentId; + this.courseId = courseId; + this.cohortId = cohortId; + this.studentId = studentId; + this.sessionId = sessionId; + } + + public String getPaymentId() { + return paymentId; + } + + public Long getCourseId() { + return courseId; + } + + public Long getCohortId() { + return cohortId; + } + + public Long getStudentId() { + return studentId; + } + + public Long getSessionId() { + return sessionId; + } +} diff --git a/src/main/java/nextstep/courses/enrollment/service/repository/EnrollmentRepository.java b/src/main/java/nextstep/courses/enrollment/service/repository/EnrollmentRepository.java new file mode 100644 index 000000000..3df1d3f4c --- /dev/null +++ b/src/main/java/nextstep/courses/enrollment/service/repository/EnrollmentRepository.java @@ -0,0 +1,10 @@ +package nextstep.courses.enrollment.service.repository; + +import nextstep.courses.enrollment.domain.Enrollment; +import org.springframework.stereotype.Repository; + +@Repository +public interface EnrollmentRepository { + + Enrollment save(Enrollment enrollment); +} diff --git a/src/main/java/nextstep/courses/session/domain/Session.java b/src/main/java/nextstep/courses/session/domain/Session.java new file mode 100644 index 000000000..6eec25d7f --- /dev/null +++ b/src/main/java/nextstep/courses/session/domain/Session.java @@ -0,0 +1,30 @@ +package nextstep.courses.session.domain; + +public class Session { + + private Long id; + private String title; + private String url; + private SessionImage image; + + + public Session(String title, String url, SessionImage sessionImage) { + this(0L, title, url, sessionImage); + } + + public Session( + String title, + String url, + String imageUrl, String imageName, + long size, int width, int height + ) { + this(0L, title, url, new SessionImage(imageUrl, size, imageName, width, height)); + } + + public Session(Long id, String title, String url, SessionImage image) { + this.id = id; + this.title = title; + this.url = url; + this.image = image; + } +} diff --git a/src/main/java/nextstep/courses/session/domain/SessionImage.java b/src/main/java/nextstep/courses/session/domain/SessionImage.java new file mode 100644 index 000000000..bf1055d3d --- /dev/null +++ b/src/main/java/nextstep/courses/session/domain/SessionImage.java @@ -0,0 +1,54 @@ +package nextstep.courses.session.domain; + +import static org.springframework.util.StringUtils.hasText; + +public class SessionImage { + public static final long MB = 1024 * 1024; + public static final int MIN_WIDTH = 300; + public static final int MIN_HEIGHT = 200; + private static final String EXTENSION_REGEX = ".*\\.(jpg|jpeg|png|gif|svg)$";; + + private String url; + private String name; + private long size; + private int width; + private int height; + + + public SessionImage(String url, long size, String name, int width, int height) { + if (!hasText(url)) { + throw new IllegalArgumentException("이미지 주소값은 필수 입니다"); + } + + if (size <= 0 || size > MB) { + throw new IllegalArgumentException("강의 이미지 크기는 1MB 이하여야 합니다"); + } + + if (!hasText(name) || !name.matches(EXTENSION_REGEX)) { + throw new IllegalArgumentException("강의 이미지 크기는 1MB 이하여야 합니다"); + } + + if (width < MIN_WIDTH || height < MIN_HEIGHT) { + throw new IllegalArgumentException("강의 이미지 너비는 300, 높이는 200 픽셀 이상여야 합니다"); + } + + if (isRatioThreeToTwo(width, height)) { + throw new IllegalArgumentException("강의 이미지 너비, 높이 비율은 3:2 여야 합니다"); + } + + this.url = url; + this.size = size; + this.name = name; + this.width = width; + this.height = height; + } + + + private boolean isRatioThreeToTwo(int width, int height) { + if (width <= 0 || height <= 0) { + return false; + } + + return width * 2 == height * 3; + } +} diff --git a/src/main/java/nextstep/payments/domain/Payment.java b/src/main/java/nextstep/payments/domain/Payment.java index 57d833f85..fd18a0a32 100644 --- a/src/main/java/nextstep/payments/domain/Payment.java +++ b/src/main/java/nextstep/payments/domain/Payment.java @@ -6,7 +6,7 @@ public class Payment { private String id; // 결제한 강의 아이디 - private Long sessionId; + private Long cohortId; // 결제한 사용자 아이디 private Long nsUserId; @@ -19,11 +19,15 @@ public class Payment { public Payment() { } - public Payment(String id, Long sessionId, Long nsUserId, Long amount) { + public Payment(String id, Long cohortId, Long nsUserId, Long amount) { this.id = id; - this.sessionId = sessionId; + this.cohortId = cohortId; this.nsUserId = nsUserId; this.amount = amount; this.createdAt = LocalDateTime.now(); } + + public boolean isPayedCohort(Long cohortId) { + return this.cohortId.equals(cohortId); + } } diff --git a/src/main/java/nextstep/payments/service/PaymentService.java b/src/main/java/nextstep/payments/service/PaymentService.java index 372019abb..7c700078c 100644 --- a/src/main/java/nextstep/payments/service/PaymentService.java +++ b/src/main/java/nextstep/payments/service/PaymentService.java @@ -1,10 +1,14 @@ package nextstep.payments.service; import nextstep.payments.domain.Payment; +import org.springframework.stereotype.Service; +@Service public class PaymentService { public Payment payment(String id) { // PG사 API를 통해 id에 해당하는 결제 정보를 반환 return new Payment(); } + + } diff --git a/src/main/java/nextstep/payments/service/repository/PaymentRepository.java b/src/main/java/nextstep/payments/service/repository/PaymentRepository.java new file mode 100644 index 000000000..00dd06664 --- /dev/null +++ b/src/main/java/nextstep/payments/service/repository/PaymentRepository.java @@ -0,0 +1,10 @@ +package nextstep.payments.service.repository; + +import java.util.Optional; +import nextstep.payments.domain.Payment; +import org.springframework.stereotype.Repository; + +@Repository +public interface PaymentRepository { + Optional findById(String id); +} diff --git a/src/test/java/nextstep/courses/cohort/domain/CohortStateTest.java b/src/test/java/nextstep/courses/cohort/domain/CohortStateTest.java new file mode 100644 index 000000000..6eb171844 --- /dev/null +++ b/src/test/java/nextstep/courses/cohort/domain/CohortStateTest.java @@ -0,0 +1,59 @@ +package nextstep.courses.cohort.domain; + +import static java.time.LocalDateTime.now; +import static java.time.LocalDateTime.of; +import static nextstep.courses.cohort.domain.enumeration.CohortStateType.RECRUIT; +import static org.assertj.core.api.Assertions.assertThat; + +import nextstep.courses.cohort.domain.enumeration.CohortStateType; +import org.junit.jupiter.api.Test; + +class CohortStateTest { + + @Test + void 주어진상태와_기수의상태가_동일한지_확인_할_수_있다() { + CohortState cohortState = new CohortState(RECRUIT, new Period(now(), now()), + new Period(now(), now())); + assertThat(cohortState.isSameState(RECRUIT)).isTrue(); + } + + @Test + void 현재시점이_기수의_모집중_기간인지_확인_할_수_있다() { + CohortState cohortState = new CohortState( + RECRUIT, + new Period( + of(2025, 1, 1, 0, 0, 0), + of(2025, 1, 10, 23, 59, 59) + ), + new Period( + of(2025, 1, 17, 0, 0, 0), + of(2025, 2, 25, 23, 59, 59) + ) + ); + + assertThat( + cohortState.isInRecruitPeriod(of(2025, 1, 1, 0, 0, 1)) + ).isTrue(); + } + + @Test + void 기수의_상태를_모집마감으로_변경할_수_있다() { + CohortState cohortState = new CohortState( + RECRUIT, + new Period( + of(2025, 1, 1, 0, 0, 0), + of(2025, 1, 10, 23, 59, 59) + ), + new Period( + of(2025, 1, 17, 0, 0, 0), + of(2025, 2, 25, 23, 59, 59) + ) + ); + + cohortState.changeRecruitEnd(); + + assertThat(cohortState.isSameState(CohortStateType.RECRUIT_END)).isTrue(); + } + + +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/cohort/domain/CohortStudentCountTest.java b/src/test/java/nextstep/courses/cohort/domain/CohortStudentCountTest.java new file mode 100644 index 000000000..f31517f65 --- /dev/null +++ b/src/test/java/nextstep/courses/cohort/domain/CohortStudentCountTest.java @@ -0,0 +1,40 @@ +package nextstep.courses.cohort.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class CohortStudentCountTest { + + @Test + void 기수의_수강가능인원이_1명미만이면_예외처리_할_수_있다() { + assertThatThrownBy( + () -> new CohortStudentCount(0, 1) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 기수의_현재수강인원이_0명미만이면_예외처리_할_수_있다() { + assertThatThrownBy( + () -> new CohortStudentCount(1, -1) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 현재수강인원이_수강가능인원보다_적은상태인것을_확인_할_수_있다() { + CohortStudentCount cohortStudentCount = new CohortStudentCount(10, 9); + + assertThat(cohortStudentCount.isNotOverMax()).isTrue(); + } + + @Test + void 현재수강인원을_1명_늘릴수_있다() { + CohortStudentCount cohortStudentCount = new CohortStudentCount(10, 9); + + assertThat(cohortStudentCount.isNotOverMax()).isTrue(); + cohortStudentCount.plusOneCountAtPresent(); + + assertThat(cohortStudentCount.isNotOverMax()).isFalse(); + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/cohort/domain/CohortTest.java b/src/test/java/nextstep/courses/cohort/domain/CohortTest.java new file mode 100644 index 000000000..e5a51a7e5 --- /dev/null +++ b/src/test/java/nextstep/courses/cohort/domain/CohortTest.java @@ -0,0 +1,141 @@ +package nextstep.courses.cohort.domain; + +import static nextstep.courses.cohort.domain.enumeration.CohortStateType.RECRUIT; +import static nextstep.courses.cohort.domain.enumeration.CohortStateType.RECRUIT_END; +import static nextstep.courses.cohort.domain.fixture.CohortFixture.식별자를_전달받아_기수픽스처를_생성한다; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.time.LocalDateTime; +import nextstep.courses.cohort.domain.enumeration.CohortStateType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class CohortTest { + + @Test + void 기수생성시_코스식별자가_비정상이면_예외처리_할_수_있다() { + assertThatThrownBy(() -> new Cohort(0L, 5, 1, 0, LocalDateTime.now(), LocalDateTime.now(), + LocalDateTime.now(), LocalDateTime.now()) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 기수생성시_수강신청기간이_수강기간보다_미래면_예외처리_할_수_있다() { + // 조금 생각이 필요한 기능. 시작 시점을 기준으로 처리할지, 종료시점을 기준으로 할지, 각각의 기간을 기준으로 할지 + // 이건 요구사항 기준으로 달라질거 같긴한데... 시작시간을 기준으로하자. + // 신청종료시점이 수강시작기간보다 뒤에있어도 된다 = ot기간까지 수강신청 받는경우도 있으니 ㅇㅇ ==> 이건 인터페이스로 빼도 되겠다. + } + + @ParameterizedTest + @CsvSource({"19, true", "20, false"}) + void 현재_수강인원을_더_받을수_있는지_확인할_수_있다(int presentStudentCount, boolean result) { + // 최대 수강인원, 현재 수강인원 전부 필요할듯. + Cohort cohort = new Cohort(1L, 5, 20, presentStudentCount, + RECRUIT, + LocalDateTime.now(), LocalDateTime.now(), + LocalDateTime.now(), LocalDateTime.now()); + + assertThat(cohort.isCanResist()).isEqualTo(result); + } + + @Test + void 코스의_식별자가_같은지_식별할_수_있다() { + Cohort cohort = 식별자를_전달받아_기수픽스처를_생성한다(1L); + + assertThat(cohort.isSameCourseId(2L)).isTrue(); + } + + @Test + void 기수의_현재수강인원을_1만큼_증가시킬_수_있다() { + Cohort cohort = new Cohort(1L, 5, 20, 19, + CohortStateType.RECRUIT, + LocalDateTime.now(), LocalDateTime.now(), + LocalDateTime.now(), LocalDateTime.now()); + assertThat(cohort.isCanResist()).isTrue(); + + cohort.registerStudent(); + + assertThat(cohort.isCanResist()).isFalse(); + } + + @Test + void 현시점이_수강신청기간보다_미래면_기수의_상태를_수강신청종료로_변경할_수_있다() { + Cohort cohort = new Cohort(1L, 5, 20, 0, + RECRUIT, + LocalDateTime.of(2025, 1, 1, 0, 0, 0), + LocalDateTime.of(2025, 1, 10, 23, 59, 59), + LocalDateTime.of(2025, 1, 17, 0, 0, 0), + LocalDateTime.of(2025, 2, 25, 23, 59, 59) + ); + + assertThat( + cohort.putOnRecruitEnd(LocalDateTime.of(2025, 1, 11, 0, 0, 0)) + ).isTrue(); + assertThat(cohort.isCohortStateType(RECRUIT_END)).isTrue(); + } + + @Test + void 현시점이_수강신청기간이라도_기수의_최대인원이_모집됐으면_수강신청종료로_변경할_수_있다() { + Cohort cohort = new Cohort(1L, 5, 20, 20, + RECRUIT, + LocalDateTime.of(2025, 1, 1, 0, 0, 0), + LocalDateTime.of(2025, 1, 10, 23, 59, 59), + LocalDateTime.of(2025, 1, 17, 0, 0, 0), + LocalDateTime.of(2025, 2, 25, 23, 59, 59) + ); + + assertThat( + cohort.putOnRecruitEnd(LocalDateTime.of(2025, 1, 9, 0, 0, 0)) + ).isTrue(); + assertThat(cohort.isCohortStateType(RECRUIT_END)).isTrue(); + } + + @Test + void 현시점이_수강신청기간이고_모집인원의_정원이_초과되지_않았을떄_수강신청종료로_변경할_수_없다() { + Cohort cohort = new Cohort(1L, 5, 20, 0, + CohortStateType.RECRUIT, + LocalDateTime.of(2025, 1, 1, 0, 0, 0), + LocalDateTime.of(2025, 1, 10, 23, 59, 59), + LocalDateTime.of(2025, 1, 17, 0, 0, 0), + LocalDateTime.of(2025, 2, 25, 23, 59, 59) + ); + + assertThat( + cohort.putOnRecruitEnd(LocalDateTime.of(2025, 1, 9, 0, 0, 0)) + ).isFalse(); + assertThat(cohort.isCohortStateType(RECRUIT_END)).isFalse(); + } + + @Test + void 현시점이_수강신청이전이면_수강신청종료로_변경할_수_없다() { + Cohort cohort = new Cohort(1L, 5, 20, 0, + LocalDateTime.of(2025, 1, 1, 0, 0, 1), + LocalDateTime.of(2025, 1, 10, 23, 59, 59), + LocalDateTime.of(2025, 1, 17, 0, 0, 0), + LocalDateTime.of(2025, 2, 25, 23, 59, 59) + ); + + assertThat( + cohort.putOnRecruitEnd(LocalDateTime.of(2025, 1, 1, 0, 0, 0)) + ).isFalse(); + assertThat(cohort.isCohortStateType(RECRUIT_END)).isFalse(); + } + + @Test + void 현시점파라미터가_NULL인경우_예외처리_할_수_있다() { + Cohort cohort = new Cohort(1L, 5, 20, 0, + LocalDateTime.of(2025, 1, 1, 0, 0, 1), + LocalDateTime.of(2025, 1, 10, 23, 59, 59), + LocalDateTime.of(2025, 1, 17, 0, 0, 0), + LocalDateTime.of(2025, 2, 25, 23, 59, 59) + ); + + assertThat( + cohort.putOnRecruitEnd(null) + ).isFalse(); + assertThat(cohort.isCohortStateType(RECRUIT_END)).isFalse(); + } + +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/cohort/domain/PeriodTest.java b/src/test/java/nextstep/courses/cohort/domain/PeriodTest.java new file mode 100644 index 000000000..dbc35368c --- /dev/null +++ b/src/test/java/nextstep/courses/cohort/domain/PeriodTest.java @@ -0,0 +1,68 @@ +package nextstep.courses.cohort.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.time.LocalDateTime; +import org.junit.jupiter.api.Test; + +class PeriodTest { + + @Test + void 시작일이_NULL이면_예외처리_할_수_있다() { + assertThatThrownBy( + () -> new Period(null, LocalDateTime.now()) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 종료일이_NULL이면_예외처리_할_수_있다() { + assertThatThrownBy( + () -> new Period(LocalDateTime.now(), null) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 시작일이_종료일보다_미래이면_예외처리_할_수_있다() { + LocalDateTime endDate = LocalDateTime.of(2025, 1, 1, 0, 0, 0); + LocalDateTime startDate = endDate.plusSeconds(1); + + assertThatThrownBy( + () -> new Period(startDate, endDate) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 특정시점이_기간에_포함되는지_확인_할_수_있다() { + LocalDateTime startDate = LocalDateTime.of(2025, 1, 1, 0, 29, 28); + LocalDateTime endDate = LocalDateTime.of(2025, 1, 1, 0, 29, 30); + LocalDateTime target = LocalDateTime.of(2025, 1, 1, 0, 29, 29); + + assertThat( + new Period(startDate, endDate).isPeriodIn(target) + ).isTrue(); + } + + @Test + void 특정시점이_종료일보다_미래인지_확인_할_수_있다() { + LocalDateTime startDate = LocalDateTime.of(2025, 1, 1, 0, 29, 28); + LocalDateTime endDate = LocalDateTime.of(2025, 1, 1, 0, 29, 30); + LocalDateTime target = LocalDateTime.of(2025, 1, 1, 0, 29, 31); + + assertThat( + new Period(startDate, endDate).isOverEndDate(target) + ).isTrue(); + } + + @Test + void 특정시점이_시작일보다_과거인지_확인_할_수_있다() { + LocalDateTime startDate = LocalDateTime.of(2025, 1, 1, 0, 29, 28); + LocalDateTime endDate = LocalDateTime.of(2025, 1, 1, 0, 29, 30); + LocalDateTime target = LocalDateTime.of(2025, 1, 1, 0, 29, 27); + + assertThat( + new Period(startDate, endDate).isBeforeStartDate(target) + ).isTrue(); + } + +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/cohort/domain/fixture/CohortFixture.java b/src/test/java/nextstep/courses/cohort/domain/fixture/CohortFixture.java new file mode 100644 index 000000000..cca0aee86 --- /dev/null +++ b/src/test/java/nextstep/courses/cohort/domain/fixture/CohortFixture.java @@ -0,0 +1,48 @@ +package nextstep.courses.cohort.domain.fixture; + +import java.time.LocalDateTime; +import nextstep.courses.cohort.domain.Cohort; +import nextstep.courses.cohort.domain.enumeration.CohortStateType; + +public class CohortFixture { + + public static Cohort 식별자를_전달받아_기수픽스처를_생성한다(Long id) { + LocalDateTime registerStartDate = LocalDateTime.of(2025, 1, 1, 0, 0, 0); + LocalDateTime registerEndDate = LocalDateTime.of(2025, 1, 2, 0, 0, 0); + + LocalDateTime cohortStartDate = LocalDateTime.of(2025, 1, 3, 0, 0, 0); + LocalDateTime cohortEndDate = LocalDateTime.of(2025, 1, 4, 0, 0, 0); + + LocalDateTime fixDateTime = LocalDateTime.of(2025, 1, 5, 0, 0, 0); + + return new Cohort( + id, 2L, 5, 1, 0, CohortStateType.PREPARE, + registerStartDate, + registerEndDate, + cohortStartDate, + cohortEndDate, + fixDateTime, + fixDateTime + ); + } + + public static Cohort 식별자와_상태를_전달받아_기수픽스처를_생성한다(Long id, CohortStateType cohortStateType) { + LocalDateTime registerStartDate = LocalDateTime.of(2025, 1, 1, 0, 0, 0); + LocalDateTime registerEndDate = LocalDateTime.of(2025, 1, 2, 0, 0, 0); + + LocalDateTime cohortStartDate = LocalDateTime.of(2025, 1, 3, 0, 0, 0); + LocalDateTime cohortEndDate = LocalDateTime.of(2025, 1, 4, 0, 0, 0); + + LocalDateTime fixDateTime = LocalDateTime.of(2025, 1, 5, 0, 0, 0); + + return new Cohort( + id, 2L, 5, 1, 0, cohortStateType, + registerStartDate, + registerEndDate, + cohortStartDate, + cohortEndDate, + fixDateTime, + fixDateTime + ); + } +} diff --git a/src/test/java/nextstep/courses/course/domain/CourseTest.java b/src/test/java/nextstep/courses/course/domain/CourseTest.java new file mode 100644 index 000000000..dadfa3d37 --- /dev/null +++ b/src/test/java/nextstep/courses/course/domain/CourseTest.java @@ -0,0 +1,62 @@ +package nextstep.courses.course.domain; + +import static nextstep.courses.cohort.domain.fixture.CohortFixture.식별자와_상태를_전달받아_기수픽스처를_생성한다; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import nextstep.courses.cohort.domain.Cohorts; +import nextstep.courses.cohort.domain.enumeration.CohortStateType; +import nextstep.courses.course.domain.enumaration.CourseChargeType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +class CourseTest { + + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = {" "}) + void 강의제목이_없이_강의를_생성하면_예외처리_할_수_있다(String title) { + assertThatThrownBy( + () -> new Course(title, 1L) + ).isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @NullSource + @ValueSource(longs = {0L}) + void 강의생성자_정보없이_강의를_생성하면_예외처리_할_수_있다(Long creatorId) { + assertThatThrownBy( + () -> new Course("TDD, 객체지향 과정", creatorId) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 강의_결제타입_없이_강의를_생성하면_예외처리_할_수_있다() { + assertThatThrownBy( + () -> new Course("TDD, 객체지향 과정", 1L, null) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 유료강의인지_확인할_수_있다() { + Course course = new Course("TDD, 객체지향 과정", 1L, CourseChargeType.PAID); + + assertThat( + course.isPaid() + ).isTrue(); + } + + @Test + void 무료강의인지_확인할_수_있다() { + Course course = new Course("TDD, 객체지향 과정", 1L, CourseChargeType.FREE); + + assertThat( + course.isFree() + ).isTrue(); + } + +} \ 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 f087fc0ad..e12dea1fb 100644 --- a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java +++ b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java @@ -1,7 +1,8 @@ package nextstep.courses.infrastructure; -import nextstep.courses.domain.Course; -import nextstep.courses.domain.CourseRepository; +import nextstep.courses.course.domain.Course; +import nextstep.courses.course.service.repository.CourseRepository; +import nextstep.courses.course.infrastructure.JdbcCourseRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -31,7 +32,7 @@ void crud() { Course course = new Course("TDD, 클린 코드 with Java", 1L); int count = courseRepository.save(course); assertThat(count).isEqualTo(1); - Course savedCourse = courseRepository.findById(1L); + Course savedCourse = courseRepository.findById(1L).get(); assertThat(course.getTitle()).isEqualTo(savedCourse.getTitle()); LOGGER.debug("Course: {}", savedCourse); } diff --git a/src/test/java/nextstep/courses/session/domain/SessionImageTest.java b/src/test/java/nextstep/courses/session/domain/SessionImageTest.java new file mode 100644 index 000000000..e2def3acc --- /dev/null +++ b/src/test/java/nextstep/courses/session/domain/SessionImageTest.java @@ -0,0 +1,67 @@ +package nextstep.courses.session.domain; + + +import static nextstep.courses.session.domain.SessionImage.MB; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +class SessionImageTest { + + @ParameterizedTest + @NullAndEmptySource + void 강의이미지_저장소주소가_없으면_예외처리_할_수_있다(String wrongUrl) { + + assertThatThrownBy( + () -> new SessionImage(wrongUrl, 1024, "name.jpg", 300, 200) + ).isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @ValueSource(longs = {-1, MB + 1}) + void 강의이미지가_1MB_이상이면_예외처리_할_수_있다(long wrongSize) { + assertThatThrownBy( + () -> new SessionImage("url", wrongSize, "name.jpg", 300, 200) + ).isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @ValueSource(strings = {"", " ", "name.", "name.txt", "name.pdf", "name.mp3", "name.smi"}) + void 강의이미지_확장자가_정해진_확장자가_아니면_예외처리_할_수_있다(String name) { + assertThatThrownBy( + () -> new SessionImage("url", 1024, name, 300, 200) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 강의이미지의_너비가_300픽셀_이하면_예외처리_할_수_있다() { + int wrongWidth = 299; + + assertThatThrownBy( + () -> new SessionImage("url", 1024, "name.jpg", wrongWidth, 200) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 강의이미지의_높이가_200픽셀_이하면_예외처리_할_수_있다() { + int wrongHeight = 199; + + assertThatThrownBy( + () -> new SessionImage("url", 1024, "name.jpg", 300, wrongHeight) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 강의이미지의_너비와_높이의_비율이_3대2가_아니면_예외처리_할_수_있다() { + int wrongWidth = 200; + int wrongHeight = 100; + + assertThatThrownBy( + () -> new SessionImage("url", 1024, "name.jpg", wrongWidth, wrongHeight) + ).isInstanceOf(IllegalArgumentException.class); + } + +} \ No newline at end of file