diff --git a/README.md b/README.md index 10ff3704b0..c08c99e32e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ## 온라인 코드 리뷰 과정 * [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/next-step/nextstep-docs/tree/master/codereview) -## 테스트 +## 단위 테스트 ### Question - [x] 로그인 사용자와 질문 작성자가 같고 답변이 없는 경우 질문을 삭제할 수 있다 - [x] 로그인 사용자와 질문 작성자가 다른 경우 질문을 삭제할 수 없다 @@ -38,3 +38,18 @@ ### Course - [x] 과정은 여러 강의를 가질 수 있다 + +## 통합 테스트 + +### 강의 (Session) +- [x] 강의를 저장할 수 있다 +- [x] 강의를 ID로 조회할 수 있다 +- [x] 과정 ID로 강의 목록을 조회할 수 있다 + +### 커버 이미지 (CoverImage) +- [x] 커버 이미지를 저장할 수 있다 +- [x] 강의 ID로 커버 이미지를 조회할 수 있다 + +### 수강신청 (Enrollment) +- [x] 수강신청을 저장할 수 있다 +- [x] 강의 ID로 수강생 목록을 조회할 수 있다 diff --git a/src/main/java/nextstep/core/domain/BaseEntity.java b/src/main/java/nextstep/core/domain/BaseEntity.java index 15698d013a..7ea1d251a6 100644 --- a/src/main/java/nextstep/core/domain/BaseEntity.java +++ b/src/main/java/nextstep/core/domain/BaseEntity.java @@ -4,16 +4,32 @@ public class BaseEntity { private Long id; - private LocalDateTime createdDate = LocalDateTime.now(); - private LocalDateTime updatedDate; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; - protected BaseEntity() {} + protected BaseEntity() { + this.createdAt = LocalDateTime.now(); + } protected BaseEntity(Long id) { + this(id, LocalDateTime.now(), null); + } + + protected BaseEntity(Long id, LocalDateTime createdAt, LocalDateTime updatedAt) { this.id = id; + this.createdAt = createdAt; + this.updatedAt = updatedAt; } public Long getId() { return id; } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } } diff --git a/src/main/java/nextstep/core/domain/SoftDeletableBaseEntity.java b/src/main/java/nextstep/core/domain/SoftDeletableBaseEntity.java index dae0f46e13..b22261fda1 100644 --- a/src/main/java/nextstep/core/domain/SoftDeletableBaseEntity.java +++ b/src/main/java/nextstep/core/domain/SoftDeletableBaseEntity.java @@ -1,5 +1,7 @@ package nextstep.core.domain; +import java.time.LocalDateTime; + public class SoftDeletableBaseEntity extends BaseEntity { private boolean deleted = false; @@ -9,6 +11,10 @@ protected SoftDeletableBaseEntity(Long id) { super(id); } + protected SoftDeletableBaseEntity(Long id, LocalDateTime createdAt, LocalDateTime updatedAt) { + super(id, createdAt, updatedAt); + } + public boolean isDeleted() { return deleted; } diff --git a/src/main/java/nextstep/courses/domain/Course.java b/src/main/java/nextstep/courses/domain/Course.java index 33e8413438..0a7c2d82ee 100644 --- a/src/main/java/nextstep/courses/domain/Course.java +++ b/src/main/java/nextstep/courses/domain/Course.java @@ -1,16 +1,14 @@ package nextstep.courses.domain; import java.time.LocalDateTime; +import nextstep.core.domain.SoftDeletableBaseEntity; import nextstep.courses.domain.session.Session; import nextstep.courses.domain.session.Sessions; -public class Course { +public class Course extends SoftDeletableBaseEntity { private final Sessions sessions = new Sessions(); - private Long id; private String title; private Long creatorId; - private LocalDateTime createdAt; - private LocalDateTime updatedAt; public Course() { } @@ -20,11 +18,9 @@ public Course(String title, Long creatorId) { } public Course(Long id, String title, Long creatorId, LocalDateTime createdAt, LocalDateTime updatedAt) { - this.id = id; + super(id, createdAt, updatedAt); this.title = title; this.creatorId = creatorId; - this.createdAt = createdAt; - this.updatedAt = updatedAt; } public String getTitle() { @@ -35,10 +31,6 @@ public Long getCreatorId() { return creatorId; } - public LocalDateTime getCreatedAt() { - return createdAt; - } - public void addSession(Session session) { sessions.add(session); } @@ -50,11 +42,11 @@ public int sessionCount() { @Override public String toString() { return "Course{" + - "id=" + id + - ", title='" + title + '\'' + - ", creatorId=" + creatorId + - ", createdAt=" + createdAt + - ", updatedAt=" + updatedAt + - '}'; + "id=" + getId() + + ", title='" + title + '\'' + + ", creatorId=" + creatorId + + ", createdAt=" + getCreatedAt() + + ", updatedAt=" + getUpdatedAt() + + '}'; } } diff --git a/src/main/java/nextstep/courses/domain/CoverImageRepository.java b/src/main/java/nextstep/courses/domain/CoverImageRepository.java new file mode 100644 index 0000000000..cd72bb40b7 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/CoverImageRepository.java @@ -0,0 +1,10 @@ +package nextstep.courses.domain; + +import nextstep.courses.domain.image.CoverImage; + +public interface CoverImageRepository { + + int save(CoverImage coverImage); + + CoverImage findBySessionId(Long sessionId); +} diff --git a/src/main/java/nextstep/courses/domain/EnrollmentRepository.java b/src/main/java/nextstep/courses/domain/EnrollmentRepository.java new file mode 100644 index 0000000000..ae19271f3b --- /dev/null +++ b/src/main/java/nextstep/courses/domain/EnrollmentRepository.java @@ -0,0 +1,11 @@ +package nextstep.courses.domain; + +import java.util.List; +import nextstep.courses.domain.enrollment.Enrollment; + +public interface EnrollmentRepository { + + Long save(Enrollment enrollment); + + List findBySessionId(Long sessionId); +} 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..cdd1a12bc4 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/SessionRepository.java @@ -0,0 +1,14 @@ +package nextstep.courses.domain; + +import java.util.List; +import nextstep.courses.domain.session.Session; + +public interface SessionRepository { + + Long save(Session session); + + Session findById(Long id); + + List findByCourseId(Long courseId); + +} diff --git a/src/main/java/nextstep/courses/domain/enrollment/Enrollment.java b/src/main/java/nextstep/courses/domain/enrollment/Enrollment.java new file mode 100644 index 0000000000..e3b8adde7a --- /dev/null +++ b/src/main/java/nextstep/courses/domain/enrollment/Enrollment.java @@ -0,0 +1,27 @@ +package nextstep.courses.domain.enrollment; + +import java.time.LocalDateTime; +import nextstep.core.domain.BaseEntity; + +public class Enrollment extends BaseEntity { + private final Long sessionId; + private final Long userId; + + public Enrollment(Long sessionId, Long userId) { + this(null, sessionId, userId, LocalDateTime.now(), null); + } + + public Enrollment(Long id, Long sessionId, Long userId, LocalDateTime createdAt, LocalDateTime updatedAt) { + super(id, createdAt, updatedAt); + this.sessionId = sessionId; + this.userId = userId; + } + + public Long getSessionId() { + return sessionId; + } + + public Long getUserId() { + return userId; + } +} diff --git a/src/main/java/nextstep/courses/domain/image/CoverImage.java b/src/main/java/nextstep/courses/domain/image/CoverImage.java index 5636ef6335..0396aec8b0 100644 --- a/src/main/java/nextstep/courses/domain/image/CoverImage.java +++ b/src/main/java/nextstep/courses/domain/image/CoverImage.java @@ -1,21 +1,56 @@ package nextstep.courses.domain.image; -public class CoverImage { - private final long size; +import java.time.LocalDateTime; +import nextstep.core.domain.BaseEntity; + +public class CoverImage extends BaseEntity { + private final Long sessionId; + private final Long size; private final ImageType type; private final int width; private final int height; - public CoverImage(long size, ImageType type, int width, int height) { + public CoverImage(Long sessionId, long size, ImageType type, int width, int height) { + this(null, sessionId, size, type, width, height, LocalDateTime.now(), null); validateSize(size); validateDimension(width, height); validateRatio(width, height); + } + + public CoverImage(Long id, Long sessionId, long size, ImageType type, int width, int height, + LocalDateTime createdAt, LocalDateTime updatedAt) { + super(id, createdAt, updatedAt); + this.sessionId = sessionId; this.size = size; this.type = type; this.width = width; this.height = height; } + public Long getSessionId() { + return sessionId; + } + + public Long getSize() { + return size; + } + + public ImageType getType() { + return type; + } + + public String getTypeName() { + return type.name(); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + private void validateRatio(int width, int height) { if (width * 2 != height * 3) { throw new IllegalArgumentException(); diff --git a/src/main/java/nextstep/courses/domain/session/EnrollmentPolicy.java b/src/main/java/nextstep/courses/domain/session/EnrollmentPolicy.java index d4f620d270..37a8ecc3b5 100644 --- a/src/main/java/nextstep/courses/domain/session/EnrollmentPolicy.java +++ b/src/main/java/nextstep/courses/domain/session/EnrollmentPolicy.java @@ -13,6 +13,23 @@ private EnrollmentPolicy(SessionType sessionType, int capacity, long fee) { this.fee = fee; } + public SessionType getSessionType() { + return sessionType; + } + + public String getSessionTypeName() { + return sessionType.name(); + } + + public int getCapacity() { + return capacity; + } + + public long getFee() { + return fee; + } + + public static EnrollmentPolicy free() { return new EnrollmentPolicy(SessionType.FREE, Integer.MAX_VALUE, 0L); } diff --git a/src/main/java/nextstep/courses/domain/session/Period.java b/src/main/java/nextstep/courses/domain/session/Period.java index 9c029855e1..7666bf609e 100644 --- a/src/main/java/nextstep/courses/domain/session/Period.java +++ b/src/main/java/nextstep/courses/domain/session/Period.java @@ -12,6 +12,14 @@ public Period(LocalDate startDate, LocalDate endDate) { this.endDate = endDate; } + public LocalDate getStartDate() { + return startDate; + } + + public LocalDate getEndDate() { + return endDate; + } + private void validateDateOrder(LocalDate startDate, LocalDate endDate) { if (startDate.isAfter(endDate)) { throw new IllegalArgumentException(); diff --git a/src/main/java/nextstep/courses/domain/session/Session.java b/src/main/java/nextstep/courses/domain/session/Session.java index ec5980069f..f41f1f51e5 100644 --- a/src/main/java/nextstep/courses/domain/session/Session.java +++ b/src/main/java/nextstep/courses/domain/session/Session.java @@ -1,24 +1,80 @@ package nextstep.courses.domain.session; -import nextstep.courses.domain.image.CoverImage; +import java.time.LocalDate; +import java.time.LocalDateTime; +import nextstep.core.domain.SoftDeletableBaseEntity; import nextstep.payments.domain.Payment; import nextstep.users.domain.NsUser; -public class Session { - private final Students students = new Students(); - private final Period period; - private final CoverImage coverImage; - private final SessionStatus sessionStatus; - private final EnrollmentPolicy enrollmentPolicy; +public class Session extends SoftDeletableBaseEntity { + private Long courseId; + private Students students = new Students(); + private Period period; + private SessionStatus sessionStatus; + private EnrollmentPolicy enrollmentPolicy; - public Session(Period period, CoverImage coverImage, SessionStatus sessionStatus, + public Session(Long courseId, Period period, SessionStatus sessionStatus, EnrollmentPolicy enrollmentPolicy) { + this(0L, courseId, period, sessionStatus, enrollmentPolicy, LocalDateTime.now(), null); + } + + public Session(Long id, Long courseId, Period period, SessionStatus sessionStatus, + EnrollmentPolicy enrollmentPolicy, LocalDateTime createdAt, LocalDateTime updatedAt) { + super(id, createdAt, updatedAt); + this.courseId = courseId; this.period = period; - this.coverImage = coverImage; this.sessionStatus = sessionStatus; this.enrollmentPolicy = enrollmentPolicy; } + public Long getCourseId() { + return courseId; + } + + public Students getStudents() { + return students; + } + + public Period getPeriod() { + return period; + } + + public SessionStatus getSessionStatus() { + return sessionStatus; + } + + public String getSessionStatusName() { + return sessionStatus.name(); + } + + public EnrollmentPolicy getEnrollmentPolicy() { + return enrollmentPolicy; + } + + public LocalDate getStartDate() { + return period.getStartDate(); + } + + public LocalDate getEndDate() { + return period.getEndDate(); + } + + public SessionType getSessionType() { + return enrollmentPolicy.getSessionType(); + } + + public String getSessionTypeName() { + return enrollmentPolicy.getSessionTypeName(); + } + + public int getCapacity() { + return enrollmentPolicy.getCapacity(); + } + + public long getFee() { + return enrollmentPolicy.getFee(); + } + public void enroll(NsUser user) { enroll(user, null); } diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcCoverImageRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcCoverImageRepository.java new file mode 100644 index 0000000000..1c56092859 --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcCoverImageRepository.java @@ -0,0 +1,54 @@ +package nextstep.courses.infrastructure; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import nextstep.courses.domain.CoverImageRepository; +import nextstep.courses.domain.image.CoverImage; +import nextstep.courses.domain.image.ImageType; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +@Repository("coverImageRepository") +public class JdbcCoverImageRepository implements CoverImageRepository { + private JdbcTemplate jdbcTemplate; + + public JdbcCoverImageRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public int save(CoverImage coverImage) { + String sql = "insert into cover_image (session_id, size, type, width, height, created_at) values (?, ?, ?, ?, ?, ?)"; + return jdbcTemplate.update(sql, + coverImage.getSessionId(), + coverImage.getSize(), + coverImage.getTypeName(), + coverImage.getWidth(), + coverImage.getHeight(), + coverImage.getCreatedAt()); + } + + @Override + public CoverImage findBySessionId(Long sessionId) { + String sql = "select id, session_id, size, type, width, height, created_at, updated_at from cover_image where session_id = ?"; + RowMapper rowMapper = (rs, rowNum) -> new CoverImage( + rs.getLong("id"), + rs.getLong("session_id"), + rs.getLong("size"), + ImageType.valueOf(rs.getString("type")), + rs.getInt("width"), + rs.getInt("height"), + toLocalDateTime(rs.getTimestamp("created_at")), + toLocalDateTime(rs.getTimestamp("updated_at")) + ); + return jdbcTemplate.queryForObject(sql, rowMapper, sessionId); + } + + private LocalDateTime toLocalDateTime(Timestamp timestamp) { + if (timestamp == null) { + return null; + } + return timestamp.toLocalDateTime(); + } +} diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java new file mode 100644 index 0000000000..163bd84023 --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java @@ -0,0 +1,64 @@ +package nextstep.courses.infrastructure; + +import java.sql.PreparedStatement; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.List; +import nextstep.courses.domain.EnrollmentRepository; +import nextstep.courses.domain.enrollment.Enrollment; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Repository; + +@Repository("enrollmentRepository") +public class JdbcEnrollmentRepository implements EnrollmentRepository { + private JdbcTemplate jdbcTemplate; + + public JdbcEnrollmentRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public Long save(Enrollment enrollment) { + String sql = "insert into enrollment (session_id, user_id, created_at) values (?, ?, ?)"; + + KeyHolder keyHolder = new GeneratedKeyHolder(); + + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(sql, new String[]{"id"}); + ps.setLong(1, enrollment.getSessionId()); + ps.setLong(2, enrollment.getUserId()); + ps.setTimestamp(3, Timestamp.valueOf(enrollment.getCreatedAt())); + return ps; + }, keyHolder); + + Number key = keyHolder.getKey(); + if (key == null) { + throw new IllegalStateException("수강신청 저장 중 키를 가져올 수 없습니다."); + } + return key.longValue(); + } + + @Override + public List findBySessionId(Long sessionId) { + String sql = "select id, session_id, user_id, created_at from enrollment where session_id = ?"; + RowMapper rowMapper = (rs, rowNum) -> new Enrollment( + rs.getLong("id"), + rs.getLong("session_id"), + rs.getLong("user_id"), + toLocalDateTime(rs.getTimestamp("created_at")), + null + ); + return jdbcTemplate.query(sql, rowMapper, sessionId); + } + + private LocalDateTime toLocalDateTime(Timestamp timestamp) { + if (timestamp == null) { + return null; + } + return timestamp.toLocalDateTime(); + } + +} diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java new file mode 100644 index 0000000000..c0b4e70a4e --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -0,0 +1,102 @@ +package nextstep.courses.infrastructure; + +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.List; +import nextstep.courses.domain.SessionRepository; +import nextstep.courses.domain.session.EnrollmentPolicy; +import nextstep.courses.domain.session.Period; +import nextstep.courses.domain.session.Session; +import nextstep.courses.domain.session.SessionStatus; +import nextstep.courses.domain.session.SessionType; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Repository; + +@Repository("sessionRepository") +public class JdbcSessionRepository implements SessionRepository { + private JdbcTemplate jdbcTemplate; + + public JdbcSessionRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public Long save(Session session) { + String sql = "insert into session (course_id, start_date, end_date, status, session_type, capacity, fee, deleted, created_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + KeyHolder keyHolder = new GeneratedKeyHolder(); + + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(sql, new String[]{"id"}); + ps.setLong(1, session.getCourseId()); + ps.setDate(2, Date.valueOf(session.getStartDate())); + ps.setDate(3, Date.valueOf(session.getEndDate())); + ps.setString(4, session.getSessionStatusName()); + ps.setString(5, session.getSessionTypeName()); + ps.setInt(6, session.getCapacity()); + ps.setLong(7, session.getFee()); + ps.setBoolean(8, session.isDeleted()); + ps.setTimestamp(9, Timestamp.valueOf(session.getCreatedAt())); + return ps; + }, keyHolder); + + Number key = keyHolder.getKey(); + if (key == null) { + throw new IllegalStateException("세션 저장 중 키를 가져올 수 없습니다."); + } + return key.longValue(); + } + + + @Override + public Session findById(Long id) { + String sql = "select id, course_id, start_date, end_date, status, session_type, capacity, fee, deleted, created_at, updated_at from session where id = ?"; + RowMapper rowMapper = (rs, rowNum) -> new Session( + rs.getLong("id"), + rs.getLong("course_id"), + new Period(rs.getDate("start_date").toLocalDate(), rs.getDate("end_date").toLocalDate()), + SessionStatus.valueOf(rs.getString("status")), + createEnrollmentPolicy(rs), + toLocalDateTime(rs.getTimestamp("created_at")), + toLocalDateTime(rs.getTimestamp("updated_at")) + ); + return jdbcTemplate.queryForObject(sql, rowMapper, id); + } + + @Override + public List findByCourseId(Long courseId) { + String sql = "select id, course_id, start_date, end_date, status, session_type, capacity, fee, deleted, created_at, updated_at from session where course_id = ?"; + RowMapper rowMapper = (rs, rowNum) -> new Session( + rs.getLong("id"), + rs.getLong("course_id"), + new Period(rs.getDate("start_date").toLocalDate(), rs.getDate("end_date").toLocalDate()), + SessionStatus.valueOf(rs.getString("status")), + createEnrollmentPolicy(rs), + toLocalDateTime(rs.getTimestamp("created_at")), + toLocalDateTime(rs.getTimestamp("updated_at")) + ); + return jdbcTemplate.query(sql, rowMapper, courseId); + } + + private EnrollmentPolicy createEnrollmentPolicy(ResultSet rs) throws SQLException { + SessionType type = SessionType.valueOf(rs.getString("session_type")); + if (type == SessionType.FREE) { + return EnrollmentPolicy.free(); + } + return EnrollmentPolicy.paid(rs.getInt("capacity"), rs.getLong("fee")); + } + + private LocalDateTime toLocalDateTime(Timestamp timestamp) { + if (timestamp == null) { + return null; + } + return timestamp.toLocalDateTime(); + } +} diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 8d5a988c8b..39a3203e77 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -2,11 +2,47 @@ create table course ( id bigint generated by default as identity, title varchar(255) not null, creator_id bigint not null, + deleted boolean not null default false, created_at timestamp not null, updated_at timestamp, primary key (id) ); +create table session ( + id bigint generated by default as identity, + course_id bigint not null, + start_date date not null, + end_date date not null, + status varchar(20) not null, + session_type varchar(20) not null, + capacity int not null, + fee bigint not null, + deleted boolean not null default false, + created_at timestamp not null, + updated_at timestamp, + primary key (id) +); + +create table cover_image ( + id bigint generated by default as identity, + session_id bigint not null, + size bigint not null, + type varchar(10) not null, + width int not null, + height int not null, + created_at timestamp not null, + updated_at timestamp, + primary key (id) +); + +create table enrollment ( + id bigint generated by default as identity, + session_id bigint not null, + user_id bigint not null, + created_at timestamp not null, + primary key (id) +); + create table ns_user ( id bigint generated by default as identity, user_id varchar(20) not null, diff --git a/src/test/java/nextstep/courses/domain/CourseTest.java b/src/test/java/nextstep/courses/domain/CourseTest.java index 1b209a1684..1d4b20f3ac 100644 --- a/src/test/java/nextstep/courses/domain/CourseTest.java +++ b/src/test/java/nextstep/courses/domain/CourseTest.java @@ -9,7 +9,6 @@ import nextstep.courses.domain.session.Period; import nextstep.courses.domain.session.Session; import nextstep.courses.domain.session.SessionStatus; -import nextstep.courses.domain.session.SessionType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -19,7 +18,7 @@ public class CourseTest { @BeforeEach void setUp() { - coverImage = new CoverImage(1_048_576L, ImageType.JPG, 300, 200); + coverImage = new CoverImage(1L, 1_048_576L, ImageType.JPG, 300, 200); period = new Period(LocalDate.of(2025, 11, 3), LocalDate.of(2025, 12, 18)); } @@ -27,9 +26,9 @@ void setUp() { void 과정은_여러_강의를_가질_수_있다() { Course course = new Course("TDD, 클린 코드 with Java", 1L); - Session session1 = new Session(period, coverImage, SessionStatus.RECRUITING, + Session session1 = new Session(1L, period, SessionStatus.RECRUITING, EnrollmentPolicy.free()); - Session session2 = new Session(period, coverImage, SessionStatus.PREPARING, + Session session2 = new Session(1L, period, SessionStatus.PREPARING, EnrollmentPolicy.paid(15, 50000L)); course.addSession(session1); diff --git a/src/test/java/nextstep/courses/domain/image/CoverImageTest.java b/src/test/java/nextstep/courses/domain/image/CoverImageTest.java index b675a14c4b..0e46045e8e 100644 --- a/src/test/java/nextstep/courses/domain/image/CoverImageTest.java +++ b/src/test/java/nextstep/courses/domain/image/CoverImageTest.java @@ -9,31 +9,31 @@ public class CoverImageTest { @Test void 유효한_이미지는_생성할_수_있다() { assertThatNoException().isThrownBy(() -> - new CoverImage(1_048_576L, ImageType.JPG, 300, 200) + new CoverImage(1L, 1_048_576L, ImageType.JPG, 300, 200) ); } @Test void 이미지_크기가_1MB를_초과하면_생성할_수_없다() { - assertThatThrownBy(() -> new CoverImage(1_048_577L, ImageType.JPG, 300, 200)) + assertThatThrownBy(() -> new CoverImage(1L, 1_048_577L, ImageType.JPG, 300, 200)) .isInstanceOf(IllegalArgumentException.class); } @Test void 이미지_width가_300픽셀_미만이면_생성할_수_없다() { - assertThatThrownBy(() -> new CoverImage(1_048_576L, ImageType.JPG, 299, 200)) + assertThatThrownBy(() -> new CoverImage(1L, 1_048_576L, ImageType.JPG, 299, 200)) .isInstanceOf(IllegalArgumentException.class); } @Test void 이미지_height가_200픽셀_미만이면_생성할_수_없다() { - assertThatThrownBy(() -> new CoverImage(1_048_576L, ImageType.JPG, 300, 199)) + assertThatThrownBy(() -> new CoverImage(1L, 1_048_576L, ImageType.JPG, 300, 199)) .isInstanceOf(IllegalArgumentException.class); } @Test void 이미지_비율이_3대2가_아니면_생성할_수_없다() { - assertThatThrownBy(() -> new CoverImage(1_048_576L, ImageType.JPG, 300, 100)) + assertThatThrownBy(() -> new CoverImage(1L, 1_048_576L, ImageType.JPG, 300, 100)) .isInstanceOf(IllegalArgumentException.class); } } diff --git a/src/test/java/nextstep/courses/domain/session/SessionTest.java b/src/test/java/nextstep/courses/domain/session/SessionTest.java index cc4d862c0a..55a5affd9f 100644 --- a/src/test/java/nextstep/courses/domain/session/SessionTest.java +++ b/src/test/java/nextstep/courses/domain/session/SessionTest.java @@ -6,19 +6,15 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.time.LocalDate; -import nextstep.courses.domain.image.CoverImage; -import nextstep.courses.domain.image.ImageType; import nextstep.payments.domain.Payment; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class SessionTest { - private CoverImage coverImage; private Period period; @BeforeEach void setUp() { - coverImage = new CoverImage(1_048_576L, ImageType.JPG, 300, 200); period = new Period(LocalDate.of(2025, 11, 3), LocalDate.of(2025, 12, 18)); } @@ -26,8 +22,8 @@ void setUp() { void 강의를_생성할_수_있다() { assertThatNoException().isThrownBy(() -> new Session( + 1L, period, - coverImage, SessionStatus.RECRUITING, EnrollmentPolicy.free() ) @@ -37,8 +33,8 @@ void setUp() { @Test void 강의_상태가_모집중이_아니면_수강신청할_수_없다() { Session session = new Session( + 1L, period, - coverImage, SessionStatus.PREPARING, EnrollmentPolicy.free() ); @@ -50,8 +46,8 @@ void setUp() { @Test void 같은_사용자는_중복_수강신청할_수_없다() { Session session = new Session( + 1L, period, - coverImage, SessionStatus.RECRUITING, EnrollmentPolicy.free() ); @@ -65,8 +61,8 @@ void setUp() { @Test void 무료_강의는_수강_인원_제한_없이_수강신청할_수_있다() { Session session = new Session( + 1L, period, - coverImage, SessionStatus.RECRUITING, EnrollmentPolicy.free() ); @@ -80,8 +76,8 @@ void setUp() { @Test void 유료_강의는_최대_수강_인원을_초과하면_수강신청할_수_없다() { Session session = new Session( + 1L, period, - coverImage, SessionStatus.RECRUITING, EnrollmentPolicy.paid(1, 50000L) ); @@ -97,8 +93,8 @@ void setUp() { @Test void 유료_강의는_결제_금액과_수강료가_일치하지_않으면_수강신청할_수_없다() { Session session = new Session( + 1L, period, - coverImage, SessionStatus.RECRUITING, EnrollmentPolicy.paid(1, 50000L) ); @@ -112,8 +108,8 @@ void setUp() { @Test void 유료_강의는_결제_금액과_수강료가_일치하면_수강신청할_수_있다() { Session session = new Session( + 1L, period, - coverImage, SessionStatus.RECRUITING, EnrollmentPolicy.paid(1, 50000L) ); diff --git a/src/test/java/nextstep/courses/infrastructure/CoverImageRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/CoverImageRepositoryTest.java new file mode 100644 index 0000000000..350e6fe096 --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/CoverImageRepositoryTest.java @@ -0,0 +1,49 @@ +package nextstep.courses.infrastructure; + +import static org.assertj.core.api.Assertions.assertThat; + +import nextstep.courses.domain.CoverImageRepository; +import nextstep.courses.domain.image.CoverImage; +import nextstep.courses.domain.image.ImageType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.jdbc.core.JdbcTemplate; + +@JdbcTest +public class CoverImageRepositoryTest { + private static final Logger LOGGER = LoggerFactory.getLogger(CoverImageRepositoryTest.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + + private CoverImageRepository coverImageRepository; + + @BeforeEach + void setUp() { + coverImageRepository = new JdbcCoverImageRepository(jdbcTemplate); + } + + @Test + void 커버_이미지를_저장할_수_있다() { + CoverImage coverImage = new CoverImage(1L, 1_048_576L, ImageType.JPG, 300, 200); + + int count = coverImageRepository.save(coverImage); + + assertThat(count).isEqualTo(1); + } + + @Test + void 강의_ID로_커버_이미지를_조회할_수_있다() { + CoverImage coverImage = new CoverImage(1L, 1_048_576L, ImageType.JPG, 300, 200); + coverImageRepository.save(coverImage); + + CoverImage savedCoverImage = coverImageRepository.findBySessionId(1L); + + assertThat(savedCoverImage.getSessionId()).isEqualTo(1L); + assertThat(savedCoverImage.getType()).isEqualTo(ImageType.JPG); + } +} diff --git a/src/test/java/nextstep/courses/infrastructure/EnrollmentRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/EnrollmentRepositoryTest.java new file mode 100644 index 0000000000..a22bc23ff1 --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/EnrollmentRepositoryTest.java @@ -0,0 +1,48 @@ +package nextstep.courses.infrastructure; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import nextstep.courses.domain.EnrollmentRepository; +import nextstep.courses.domain.enrollment.Enrollment; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.jdbc.core.JdbcTemplate; + +@JdbcTest +public class EnrollmentRepositoryTest { + private static final Logger LOGGER = LoggerFactory.getLogger(EnrollmentRepositoryTest.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + + private EnrollmentRepository enrollmentRepository; + + @BeforeEach + void setUp() { + enrollmentRepository = new JdbcEnrollmentRepository(jdbcTemplate); + } + + @Test + void 수강신청을_저장할_수_있다() { + Enrollment enrollment = new Enrollment(1L, 1L); + + Long savedId = enrollmentRepository.save(enrollment); + + assertThat(savedId).isNotNull(); + } + + @Test + void 강의_ID로_수강신청_목록을_조회할_수_있다() { + enrollmentRepository.save(new Enrollment(1L, 1L)); + enrollmentRepository.save(new Enrollment(1L, 2L)); + + List enrollments = enrollmentRepository.findBySessionId(1L); + + assertThat(enrollments).hasSize(2); + } +} diff --git a/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java new file mode 100644 index 0000000000..92873f7644 --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java @@ -0,0 +1,66 @@ +package nextstep.courses.infrastructure; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.LocalDate; +import java.util.List; +import nextstep.courses.domain.SessionRepository; +import nextstep.courses.domain.session.EnrollmentPolicy; +import nextstep.courses.domain.session.Period; +import nextstep.courses.domain.session.Session; +import nextstep.courses.domain.session.SessionStatus; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.jdbc.core.JdbcTemplate; + +@JdbcTest +public class SessionRepositoryTest { + private static final Logger LOGGER = LoggerFactory.getLogger(SessionRepositoryTest.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + + private SessionRepository sessionRepository; + + @BeforeEach + void setUp() { + sessionRepository = new JdbcSessionRepository(jdbcTemplate); + } + + @Test + void 강의를_저장할_수_있다() { + Period period = new Period(LocalDate.of(2025, 11, 18), LocalDate.of(2025, 12, 18)); + Session session = new Session(1L, period, SessionStatus.RECRUITING, EnrollmentPolicy.free()); + + Long savedId = sessionRepository.save(session); + + assertThat(savedId).isNotNull(); + } + + @Test + void 강의를_ID로_조회할_수_있다() { + Period period = new Period(LocalDate.of(2025, 11, 18), LocalDate.of(2025, 12, 18)); + Session session = new Session(1L, period, SessionStatus.RECRUITING, EnrollmentPolicy.free()); + Long savedId = sessionRepository.save(session); + + Session savedSession = sessionRepository.findById(savedId); + + assertThat(savedSession.getCourseId()).isEqualTo(1L); + assertThat(savedSession.getSessionStatus()).isEqualTo(SessionStatus.RECRUITING); + } + + @Test + void 과정_ID로_강의_목록을_조회할_수_있다() { + Period period = new Period(LocalDate.of(2025, 11, 18), LocalDate.of(2025, 12, 18)); + Session session = new Session(1L, period, SessionStatus.RECRUITING, EnrollmentPolicy.free()); + sessionRepository.save(session); + + List sessions = sessionRepository.findByCourseId(1L); + + assertThat(sessions).hasSize(1); + } +}