diff --git a/README.md b/README.md index 00ce3d2e4..c3a1b8e36 100644 --- a/README.md +++ b/README.md @@ -68,4 +68,9 @@ - boolean validateFileSize() - boolean validateFileType() - boolean validateRatio() - \ No newline at end of file + + +## ๐Ÿš€ 4๋‹จ๊ณ„ - ์ˆ˜๊ฐ•์‹ ์ฒญ(์š”๊ตฌ์‚ฌํ•ญ ๋ณ€๊ฒฝ) +- [x] SessionStatus ์ง„ํ–‰์ƒํƒœ์™€ ๋ชจ์ง‘์ƒํƒœ๋กœ ๋ถ„๋ฆฌ +- [x] coverImage ์—ฌ๋Ÿฌ๊ฐœ ๊ฐ€๋Šฅํ•˜๋„๋ก ๊ธฐ๋Šฅ ์ถ”๊ฐ€ +- [x] ์ˆ˜๊ฐ• ์Šน์ธ ๋ฐ ์ˆ˜๊ฐ• ์ทจ์†Œ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ \ No newline at end of file diff --git a/src/main/java/nextstep/courses/domain/Enrollment.java b/src/main/java/nextstep/courses/domain/Enrollment.java new file mode 100644 index 000000000..599fcb251 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Enrollment.java @@ -0,0 +1,43 @@ +package nextstep.courses.domain; + +public class Enrollment { + private Member student; + private Session session; + private EnrollmentStatus status; // PENDING, APPROVED, REJECTED + + public Enrollment(Member student, Session session) { + this.student = student; + this.session = session; + this.status = EnrollmentStatus.PENDING; + } + + public void approve() { + if (this.status == EnrollmentStatus.APPROVED) { + throw new IllegalStateException("์ด๋ฏธ ์Šน์ธ๋œ ์ˆ˜๊ฐ• ์‹ ์ฒญ์ž…๋‹ˆ๋‹ค."); + } + + this.status = EnrollmentStatus.APPROVED; + session.accept(); + } + + public void reject() { + this.status = EnrollmentStatus.REJECTED; + } + + public boolean isApproved() { + return status == EnrollmentStatus.APPROVED; + } + + public Member getStudent() { + return student; + } + + public EnrollmentStatus getStatus() { + return status; + } + + public Session getSession() { + return session; + } +} + diff --git a/src/main/java/nextstep/courses/domain/EnrollmentStatus.java b/src/main/java/nextstep/courses/domain/EnrollmentStatus.java new file mode 100644 index 000000000..9e04b16b2 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/EnrollmentStatus.java @@ -0,0 +1,5 @@ +package nextstep.courses.domain; + +public enum EnrollmentStatus { + PENDING, APPROVED, REJECTED; +} diff --git a/src/main/java/nextstep/courses/domain/Enrollments.java b/src/main/java/nextstep/courses/domain/Enrollments.java new file mode 100644 index 000000000..a0a320137 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Enrollments.java @@ -0,0 +1,29 @@ +package nextstep.courses.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Enrollments { + private final List values = new ArrayList<>(); + + public void addEnrollment(Enrollment enrollment) { + values.add(enrollment); + } + + public boolean isEnrolledBy(Member student) { + return values.stream() + .anyMatch(e -> e.getStudent().equals(student)); + } + + public Enrollment findByMember(Member student) { + return values.stream() + .filter(e -> e.getStudent().equals(student)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("ํ•ด๋‹น ํšŒ์›์˜ ์ˆ˜๊ฐ• ์‹ ์ฒญ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")); + } + + public List getValues() { + return Collections.unmodifiableList(values); + } +} diff --git a/src/main/java/nextstep/courses/domain/Image.java b/src/main/java/nextstep/courses/domain/Image.java index 4454a011f..c981bdf7c 100644 --- a/src/main/java/nextstep/courses/domain/Image.java +++ b/src/main/java/nextstep/courses/domain/Image.java @@ -6,11 +6,18 @@ public class Image { private static final Set ALLOWED_FILE_FORMAT = new HashSet<>(List.of("gif", "jpg", "jpeg", "png", "svg")); + private Long id; private final File file; private String imageUrl; private int width; private int height; + // DB์šฉ ์ƒ์„ฑ์ž (id ํฌํ•จ, ๊ฒ€์ฆ ์ƒ๋žต ๊ฐ€๋Šฅ) + public Image(Long id, float fileSize, String fileType, String imageUrl, int width, int height) { + this(fileSize, fileType, imageUrl, width, height); + this.id = id; + } + public Image(float fileSize, String fileType, String imageUrl, int width, int height) { this.file = new File(ALLOWED_FILE_FORMAT, fileSize, fileType); this.imageUrl = imageUrl; @@ -19,6 +26,7 @@ public Image(float fileSize, String fileType, String imageUrl, int width, int he validate(); } + private void validate() { validateFileSize(); validateFileType(); diff --git a/src/main/java/nextstep/courses/domain/Images.java b/src/main/java/nextstep/courses/domain/Images.java new file mode 100644 index 000000000..ef8daae52 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Images.java @@ -0,0 +1,16 @@ +package nextstep.courses.domain; + +import java.util.Collections; +import java.util.List; + +public class Images { + private List images; + + public Images(List images) { + this.images = List.copyOf(images); + } + + public List getImages() { + return Collections.unmodifiableList(images); + } +} diff --git a/src/main/java/nextstep/courses/domain/Member.java b/src/main/java/nextstep/courses/domain/Member.java new file mode 100644 index 000000000..03a131dfd --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Member.java @@ -0,0 +1,41 @@ +package nextstep.courses.domain; + +import java.util.Objects; + +public class Member { + private final Long id; + private final String name; + private final String email; + + public Member(Long id, String name, String email) { + this.id = id; + this.name = name; + this.email = email; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getEmail() { + return email; + } + + // Member ์‹๋ณ„ ๋น„๊ต (์˜ˆ: Set ์‚ฌ์šฉ ์‹œ ํ•„์š”) + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Member)) return false; + Member member = (Member) o; + return Objects.equals(id, member.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} \ No newline at end of file diff --git a/src/main/java/nextstep/courses/domain/RecruitmentStatus.java b/src/main/java/nextstep/courses/domain/RecruitmentStatus.java new file mode 100644 index 000000000..77e1512c2 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/RecruitmentStatus.java @@ -0,0 +1,6 @@ +package nextstep.courses.domain; + +public enum RecruitmentStatus { + NOT_RECRUITING, + RECRUITING +} diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index e7f925551..b04fbbce9 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -3,7 +3,6 @@ import nextstep.payments.domain.Payment; import java.time.LocalDateTime; -import java.util.regex.Pattern; public class Session { private CapacityInfo capacityInfo; @@ -11,23 +10,26 @@ public class Session { private String title; private int id; private Long tuition; - private Image coverImage; + private Images coverImages; + private RecruitmentStatus recruitmentStatus; private SessionStatus sessionStatus; private JoinStrategy joinStrategy; + private final Enrollments enrollments = new Enrollments(); - public Session(String title, int id, LocalDateTime startDate, LocalDateTime endDate, Long tuition, int currentCount, int capacity, Image coverImage, SessionStatus sessionStatus) { - this(title, id, startDate, endDate, tuition, currentCount, capacity, coverImage, sessionStatus, tuition == 0 ? new FreeJoinStrategy() : new PaidJoinStrategy()); + public Session(String title, int id, LocalDateTime startDate, LocalDateTime endDate, Long tuition, int currentCount, int capacity, Images coverImages, SessionStatus sessionStatus, RecruitmentStatus recruitmentStatus) { + this(title, id, startDate, endDate, tuition, currentCount, capacity, coverImages, sessionStatus, recruitmentStatus, tuition == 0 ? new FreeJoinStrategy() : new PaidJoinStrategy()); } - public Session(String title, int id, LocalDateTime startDate, LocalDateTime endDate, Long tuition, int currentCount, int capacity, Image coverImage, SessionStatus sessionStatus, JoinStrategy joinStrategy) { + public Session(String title, int id, LocalDateTime startDate, LocalDateTime endDate, Long tuition, int currentCount, int capacity, Images coverImages, SessionStatus sessionStatus, RecruitmentStatus recruitmentStatus, JoinStrategy joinStrategy) { this.title = title; this.id = id; this.sessionPeriod = new SessionPeriod(startDate, endDate); this.tuition = tuition; this.capacityInfo = new CapacityInfo(currentCount, capacity); - this.coverImage = coverImage; + this.coverImages = coverImages; this.sessionStatus = sessionStatus; + this.recruitmentStatus = recruitmentStatus; this.joinStrategy = joinStrategy; } @@ -36,7 +38,7 @@ boolean joinable(Payment pay) { } public boolean recruiting() { - return sessionStatus == SessionStatus.RECRUITING; + return sessionStatus == SessionStatus.ONGOING && recruitmentStatus == RecruitmentStatus.RECRUITING; } public boolean underCapacity() { @@ -51,14 +53,32 @@ public boolean hasId(long id) { return this.id == id; } - public void enroll(Payment pay) { + public void enroll(Payment pay, Member member) { if (!joinable(pay)) { throw new IllegalStateException("์ˆ˜๊ฐ• ์‹ ์ฒญ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."); } + if (enrollments.isEnrolledBy(member)) { + throw new IllegalStateException("์ด๋ฏธ ์ˆ˜๊ฐ• ์‹ ์ฒญํ•œ ํšŒ์›์ž…๋‹ˆ๋‹ค."); + } + + enrollments.addEnrollment(new Enrollment(member, this)); + } + + public void approveEnrollment(Member member) { + Enrollment enrollment = enrollments.findByMember(member); + enrollment.approve(); + } + + public void accept(){ this.capacityInfo.increaseCurrentCount(); } + public void rejectEnrollment(Member member) { + Enrollment enrollment = enrollments.findByMember(member); + enrollment.reject(); + } + public String getTitle() { return title; } @@ -87,11 +107,23 @@ public int getCapacity() { return capacityInfo.getCapacity(); } - public Image getCoverImage() { - return coverImage; + public Images getCoverImages() { + return coverImages; + } + + public Image getMainCoverImage() { + return coverImages.getImages().get(0); } - public SessionStatus getStatus() { + public SessionStatus getSessionStatus() { return sessionStatus; } + + public RecruitmentStatus getRecruitmentStatus() { + return recruitmentStatus; + } + + public Enrollments getEnrollments() { + return enrollments; + } } diff --git a/src/main/java/nextstep/courses/domain/SessionRepository.java b/src/main/java/nextstep/courses/domain/SessionRepository.java index f1d74ae1d..d84e70ff8 100644 --- a/src/main/java/nextstep/courses/domain/SessionRepository.java +++ b/src/main/java/nextstep/courses/domain/SessionRepository.java @@ -2,6 +2,6 @@ public interface SessionRepository { int save(Session session, Long courseId); - + void saveImage(int sessionId, Image image); Session findById(Long id); } diff --git a/src/main/java/nextstep/courses/domain/SessionStatus.java b/src/main/java/nextstep/courses/domain/SessionStatus.java index d4ce621fa..43cdd62a8 100644 --- a/src/main/java/nextstep/courses/domain/SessionStatus.java +++ b/src/main/java/nextstep/courses/domain/SessionStatus.java @@ -2,6 +2,6 @@ public enum SessionStatus { PREPARING, - RECRUITING, + ONGOING, CLOSED } 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 000000000..524c22739 --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcEnrollmentRepository.java @@ -0,0 +1,60 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.Enrollment; +import nextstep.courses.domain.EnrollmentStatus; +import nextstep.courses.domain.Member; +import nextstep.courses.domain.Session; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class JdbcEnrollmentRepository { + + private final JdbcOperations jdbc; + private final JdbcSessionRepository jdbcSessionRepository; + + public JdbcEnrollmentRepository(JdbcOperations jdbc, JdbcSessionRepository jdbcSessionRepository) { + this.jdbc = jdbc; + this.jdbcSessionRepository = jdbcSessionRepository; + } + + public void save(Long sessionId, Long memberId, EnrollmentStatus status) { + String sql = "INSERT INTO enrollment (session_id, member_id, status) VALUES (?, ?, ?)"; + jdbc.update(sql, sessionId, memberId, status.name()); + } + + public void updateStatus(Long sessionId, Long memberId, EnrollmentStatus status) { + String sql = "UPDATE enrollment SET status = ? WHERE session_id = ? AND member_id = ?"; + jdbc.update(sql, status.name(), sessionId, memberId); + } + + public List findBySessionId(Long sessionId) { + String sql = + "SELECT e.*, m.id as member_id, m.name, m.email " + + "FROM enrollment e " + + "JOIN member m ON e.member_id = m.id " + + "WHERE e.session_id = ?"; + + + return jdbc.query(sql, (rs, rowNum) -> { + Member member = new Member( + rs.getLong("member_id"), + rs.getString("name"), + rs.getString("email") + ); + + Session session = jdbcSessionRepository.findById(sessionId); + Enrollment enrollment = new Enrollment(member, session); + EnrollmentStatus status = EnrollmentStatus.valueOf(rs.getString("status")); + if (status == EnrollmentStatus.APPROVED) { + enrollment.approve(); + } else if (status == EnrollmentStatus.REJECTED) { + enrollment.reject(); + } + + return enrollment; + }, sessionId); + } +} diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java index 1b692b2ac..6b854afc7 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -1,13 +1,12 @@ package nextstep.courses.infrastructure; -import nextstep.courses.domain.Image; -import nextstep.courses.domain.Session; -import nextstep.courses.domain.SessionRepository; -import nextstep.courses.domain.SessionStatus; +import nextstep.courses.domain.*; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public class JdbcSessionRepository implements SessionRepository { @@ -19,29 +18,57 @@ public JdbcSessionRepository(JdbcOperations jdbcTemplate) { @Override public int save(Session session, Long courseId) { - String sql = "insert into session " + + String sql = "INSERT INTO session " + "(title, start_date, end_date, tuition, current_count, capacity, " + - "image_file_size, image_file_type, image_url, image_width, image_height, " + - "status, course_id) " + - "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - - return jdbcTemplate.update(sql, - session.getTitle(), - session.getStartDate(), - session.getEndDate(), - session.getTuition(), - session.getCurrentCount(), - session.getCapacity(), - session.getCoverImage().getSize(), - session.getCoverImage().getType(), - session.getCoverImage().getImageUrl(), - session.getCoverImage().getWidth(), - session.getCoverImage().getHeight(), - session.getStatus().name(), - courseId + "status, recruitment_status, course_id) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + var keyHolder = new org.springframework.jdbc.support.GeneratedKeyHolder(); + + jdbcTemplate.update(connection -> { + var ps = connection.prepareStatement(sql, new String[]{"id"}); + ps.setString(1, session.getTitle()); + ps.setTimestamp(2, java.sql.Timestamp.valueOf(session.getStartDate())); + ps.setTimestamp(3, java.sql.Timestamp.valueOf(session.getEndDate())); + ps.setLong(4, session.getTuition()); + ps.setInt(5, session.getCurrentCount()); + ps.setInt(6, session.getCapacity()); + ps.setString(7, session.getSessionStatus().name()); + ps.setString(8, session.getRecruitmentStatus().name()); + ps.setLong(9, courseId); + return ps; + }, keyHolder); + + var generatedId = keyHolder.getKey(); + if (generatedId == null) { + throw new IllegalStateException("Session ์ €์žฅ ์‹คํŒจ - id ์ƒ์„ฑ ์‹คํŒจ"); + } + + int sessionId = generatedId.intValue(); + + // ์ด๋ฏธ์ง€ ์ €์žฅ + for (Image image : session.getCoverImages().getImages()) { + saveImage(sessionId, image); + } + + return sessionId; + } + + + @Override + public void saveImage(int sessionId, Image image){ + String sql = "INSERT INTO image (session_id, url, file_type, file_size, width, height) VALUES (?, ?, ?, ?, ?, ?)"; + jdbcTemplate.update(sql, + sessionId, + image.getImageUrl(), + image.getType(), + image.getSize(), + image.getWidth(), + image.getHeight() ); } + @Override public Session findById(Long id) { String sql = "select * from session where id = ?"; @@ -50,22 +77,34 @@ public Session findById(Long id) { } private RowMapper sessionRowMapper() { - return (rs, rowNum) -> new Session( - rs.getString("title"), - rs.getInt("id"), - rs.getTimestamp("start_date").toLocalDateTime(), - rs.getTimestamp("end_date").toLocalDateTime(), - rs.getLong("tuition"), - rs.getInt("current_count"), - rs.getInt("capacity"), - new Image( - rs.getFloat("image_file_size"), - rs.getString("image_file_type"), - rs.getString("image_url"), - rs.getInt("image_width"), - rs.getInt("image_height") - ), - SessionStatus.valueOf(rs.getString("status")) - ); + return (rs, rowNum) -> { + int sessionId = rs.getInt("id"); + + List images = jdbcTemplate.query( + "SELECT * FROM image WHERE session_id = ?", + (irs, irow) -> new Image( + irs.getLong("id"), + irs.getFloat("file_size"), + irs.getString("file_type"), + irs.getString("url"), + irs.getInt("width"), + irs.getInt("height") + ), + sessionId + ); + + return new Session( + rs.getString("title"), + sessionId, + rs.getTimestamp("start_date").toLocalDateTime(), + rs.getTimestamp("end_date").toLocalDateTime(), + rs.getLong("tuition"), + rs.getInt("current_count"), + rs.getInt("capacity"), + new Images(images), + SessionStatus.valueOf(rs.getString("status")), + RecruitmentStatus.valueOf(rs.getString("recruitment_status")) + ); + }; } } diff --git a/src/main/java/nextstep/courses/service/EnrollService.java b/src/main/java/nextstep/courses/service/EnrollService.java index 0f68163e3..cc9bc514f 100644 --- a/src/main/java/nextstep/courses/service/EnrollService.java +++ b/src/main/java/nextstep/courses/service/EnrollService.java @@ -1,9 +1,6 @@ package nextstep.courses.service; -import nextstep.courses.domain.Course; -import nextstep.courses.domain.CourseRepository; -import nextstep.courses.domain.Session; -import nextstep.courses.domain.Sessions; +import nextstep.courses.domain.*; import nextstep.payments.domain.Payment; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -12,14 +9,31 @@ @Service("enrollService") public class EnrollService { + @Resource(name = "courseRepository") private CourseRepository courseRepository; @Transactional - public void enrollCourse(long courseId, long sessionId, Payment payment) { + public void enrollCourse(long courseId, long sessionId, Payment payment, Member member) { + Session session = findSession(courseId, sessionId); + session.enroll(payment, member); // ์กฐ๊ฑด ๋งŒ์กฑํ•ด์•ผ๋งŒ Enrollment(PENDING) ์ƒ์„ฑ + } + + @Transactional + public void approveEnrollment(long courseId, long sessionId, Member member) { + Session session = findSession(courseId, sessionId); + session.approveEnrollment(member); + } + + @Transactional + public void rejectEnrollment(long courseId, long sessionId, Member member) { + Session session = findSession(courseId, sessionId); + session.rejectEnrollment(member); + } + + private Session findSession(long courseId, long sessionId) { Course course = courseRepository.findById(courseId); - Sessions sessions = course.getSessions(); - Session session = sessions.findById(sessionId); - session.enroll(payment); + return course.getSessions().findById(sessionId); } } + diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 04723cb30..ca3e49c42 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -11,28 +11,57 @@ create table course CREATE TABLE session ( - id BIGINT AUTO_INCREMENT PRIMARY KEY, - title VARCHAR(255) NOT NULL, - start_date DATETIME NOT NULL, - end_date DATETIME NOT NULL, - tuition BIGINT NOT NULL, - current_count INT NOT NULL, - capacity INT NOT NULL, + id BIGINT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255) NOT NULL, + start_date DATETIME NOT NULL, + end_date DATETIME NOT NULL, + tuition BIGINT NOT NULL, + current_count INT NOT NULL, + capacity INT NOT NULL, - -- Embedded Image - image_file_size FLOAT, - image_file_type VARCHAR(50), - image_url VARCHAR(500), - image_width INT, - image_height INT, - - status VARCHAR(50) NOT NULL, + status VARCHAR(50) NOT NULL, + recruitment_status VARCHAR(50) NOT NULL, -- FK to course - course_id BIGINT NOT NULL, + course_id BIGINT NOT NULL, FOREIGN KEY (course_id) REFERENCES course (id) ); +CREATE TABLE member +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE +); + +CREATE TABLE enrollment +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + session_id BIGINT NOT NULL, + member_id BIGINT NOT NULL, + status VARCHAR(50) NOT NULL, -- PENDING, APPROVED, REJECTED + + -- FK ์„ค์ • + FOREIGN KEY (session_id) REFERENCES session (id), + FOREIGN KEY (member_id) REFERENCES member (id), + + UNIQUE (session_id, member_id) -- ์ค‘๋ณต ์ˆ˜๊ฐ• ์‹ ์ฒญ ๋ฐฉ์ง€ +); + + +CREATE TABLE image +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + session_id BIGINT NOT NULL, + url VARCHAR(500) NOT NULL, + file_type VARCHAR(50), + file_size FLOAT, + width INT, + height INT, + FOREIGN KEY (session_id) REFERENCES session (id) +); + + create table ns_user ( id bigint generated by default as identity, diff --git a/src/test/java/nextstep/courses/domain/EnrollmentTest.java b/src/test/java/nextstep/courses/domain/EnrollmentTest.java new file mode 100644 index 000000000..44ffc03df --- /dev/null +++ b/src/test/java/nextstep/courses/domain/EnrollmentTest.java @@ -0,0 +1,77 @@ +package nextstep.courses.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class EnrollmentTest { + + private final Member member = new Member(1L, "ํ™๊ธธ๋™", "hong@example.com"); + private final Image validImage = new Image(500f, "png", "cdn.com", 600, 400); + private final Images validImages = new Images(List.of(validImage)); + + private Session createDummySession() { + return new Session( + "๋”๋ฏธ ๊ฐ•์˜", + 1, + LocalDateTime.now(), + LocalDateTime.now().plusDays(7), + 0L, + 0, + 10, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, + new FreeJoinStrategy() + ); + } + + @Test + @DisplayName("Enrollment๋Š” ์ƒ์„ฑ ์‹œ PENDING ์ƒํƒœ์ด๋‹ค") + void created_enrollment_has_pending_status() { + Session session = createDummySession(); + Enrollment enrollment = new Enrollment(member, session); + + assertThat(enrollment.getStatus()).isEqualTo(EnrollmentStatus.PENDING); + } + + @Test + @DisplayName("Enrollment๋ฅผ ์Šน์ธํ•˜๋ฉด APPROVED ์ƒํƒœ๊ฐ€ ๋œ๋‹ค") + void approve_sets_status_to_approved() { + Session session = createDummySession(); + Enrollment enrollment = new Enrollment(member, session); + + enrollment.approve(); + + assertThat(enrollment.getStatus()).isEqualTo(EnrollmentStatus.APPROVED); + } + + @Test + @DisplayName("Enrollment๋ฅผ ๊ฑฐ์ ˆํ•˜๋ฉด REJECTED ์ƒํƒœ๊ฐ€ ๋œ๋‹ค") + void reject_sets_status_to_rejected() { + Session session = createDummySession(); + Enrollment enrollment = new Enrollment(member, session); + + enrollment.reject(); + + assertThat(enrollment.getStatus()).isEqualTo(EnrollmentStatus.REJECTED); + } + + @Test + @DisplayName("์ด๋ฏธ ์Šน์ธ๋œ Enrollment๋Š” ๋‹ค์‹œ ์Šน์ธํ•  ์ˆ˜ ์—†๋‹ค") + void approving_twice_should_throw() { + Session session = createDummySession(); + Enrollment enrollment = new Enrollment(member, session); + + enrollment.approve(); + + assertThatThrownBy(enrollment::approve) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("์ด๋ฏธ ์Šน์ธ๋œ ์ˆ˜๊ฐ• ์‹ ์ฒญ์ž…๋‹ˆ๋‹ค."); + } +} diff --git a/src/test/java/nextstep/courses/domain/EnrollmentsTest.java b/src/test/java/nextstep/courses/domain/EnrollmentsTest.java new file mode 100644 index 000000000..f460731d1 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/EnrollmentsTest.java @@ -0,0 +1,100 @@ +package nextstep.courses.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class EnrollmentsTest { + + private final Member member1 = new Member(1L, "ํ™๊ธธ๋™", "hong@example.com"); + private final Member member2 = new Member(2L, "์ด๋ชฝ๋ฃก", "lee@example.com"); + + private final Image validImage = new Image(500f, "png", "cdn.com", 600, 400); + private final Images validImages = new Images(List.of(validImage)); + + private Session createSession() { + return new Session( + "๊ฐ•์˜", + 1, + LocalDateTime.now(), + LocalDateTime.now().plusDays(7), + 0L, + 0, + 10, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, + new FreeJoinStrategy() + ); + } + + @Test + @DisplayName("Enrollment๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฆฌ์ŠคํŠธ์— ํฌํ•จ๋œ๋‹ค") + void addEnrollment_stores_enrollment() { + Enrollments enrollments = new Enrollments(); + Session session = createSession(); + Enrollment enrollment = new Enrollment(member1, session); + + enrollments.addEnrollment(enrollment); + + assertThat(enrollments.getValues()).contains(enrollment); + } + + @Test + @DisplayName("ํ•ด๋‹น ๋ฉค๋ฒ„๊ฐ€ ์ˆ˜๊ฐ• ์‹ ์ฒญํ•œ ๊ฒฝ์šฐ isEnrolledBy()๋Š” true๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค") + void isEnrolledBy_returns_true_for_existing_member() { + Enrollments enrollments = new Enrollments(); + Session session = createSession(); + enrollments.addEnrollment(new Enrollment(member1, session)); + + assertThat(enrollments.isEnrolledBy(member1)).isTrue(); + } + + @Test + @DisplayName("ํ•ด๋‹น ๋ฉค๋ฒ„๊ฐ€ ์ˆ˜๊ฐ• ์‹ ์ฒญํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ isEnrolledBy()๋Š” false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค") + void isEnrolledBy_returns_false_for_non_enrolled_member() { + Enrollments enrollments = new Enrollments(); + assertThat(enrollments.isEnrolledBy(member1)).isFalse(); + } + + @Test + @DisplayName("findByMember()๋Š” ์ˆ˜๊ฐ• ์‹ ์ฒญํ•œ ๋ฉค๋ฒ„์˜ Enrollment๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค") + void findByMember_returns_enrollment() { + Enrollments enrollments = new Enrollments(); + Session session = createSession(); + Enrollment enrollment = new Enrollment(member1, session); + enrollments.addEnrollment(enrollment); + + Enrollment found = enrollments.findByMember(member1); + + assertThat(found).isEqualTo(enrollment); + } + + @Test + @DisplayName("findByMember()๋Š” ์ˆ˜๊ฐ• ์‹ ์ฒญํ•˜์ง€ ์•Š์€ ๋ฉค๋ฒ„์ผ ๊ฒฝ์šฐ ์˜ˆ์™ธ๋ฅผ ๋˜์ง„๋‹ค") + void findByMember_throws_for_non_existing_member() { + Enrollments enrollments = new Enrollments(); + + assertThatThrownBy(() -> enrollments.findByMember(member2)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("ํ•ด๋‹น ํšŒ์›์˜ ์ˆ˜๊ฐ• ์‹ ์ฒญ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."); + } + + @Test + @DisplayName("getValues()๋Š” ๋ถˆ๋ณ€ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค") + void getValues_returns_unmodifiable_list() { + Enrollments enrollments = new Enrollments(); + Session session = createSession(); + enrollments.addEnrollment(new Enrollment(member1, session)); + + List values = enrollments.getValues(); + + assertThatThrownBy(() -> values.add(new Enrollment(member2, session))) + .isInstanceOf(UnsupportedOperationException.class); + } +} diff --git a/src/test/java/nextstep/courses/domain/ImagesTest.java b/src/test/java/nextstep/courses/domain/ImagesTest.java new file mode 100644 index 000000000..f06660988 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/ImagesTest.java @@ -0,0 +1,44 @@ +package nextstep.courses.domain; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class ImagesTest { + + @Test + void ์ƒ์„ฑ_๋ฐ_์กฐํšŒ() { + Image image1 = new Image(100, "jpg", "url1", 300, 200); + Image image2 = new Image(100, "png", "url2", 300, 200); + + Images images = new Images(List.of(image1, image2)); + + assertThat(images.getImages()).hasSize(2) + .containsExactly(image1, image2); + } + + @Test + void ์ƒ์„ฑ์ž์—์„œ_๋ณต์‚ฌ๋œ_๋ฆฌ์ŠคํŠธ๋Š”_์™ธ๋ถ€_๋ฆฌ์ŠคํŠธ_๋ณ€๊ฒฝ_์˜ํ–ฅ์„_๋ฐ›์ง€_์•Š๋Š”๋‹ค() { + List original = new ArrayList<>(); + original.add(new Image(100, "jpg", "url1", 300, 200)); + + Images images = new Images(original); + + original.add(new Image(100, "png", "url2", 300, 200)); + + assertThat(images.getImages()).hasSize(1); + } + + @Test + void getImages๋กœ_๊ฐ€์ ธ์˜จ_๋ฆฌ์ŠคํŠธ๋Š”_์ˆ˜์ •ํ• _์ˆ˜_์—†๋‹ค() { + Images images = new Images(List.of(new Image(100, "jpg", "url1", 300, 200))); + + List retrieved = images.getImages(); + + assertThatThrownBy(() -> retrieved.add(new Image(100, "png", "url2", 300, 200))) + .isInstanceOf(UnsupportedOperationException.class); + } +} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/domain/MemberTest.java b/src/test/java/nextstep/courses/domain/MemberTest.java new file mode 100644 index 000000000..dc05f66b9 --- /dev/null +++ b/src/test/java/nextstep/courses/domain/MemberTest.java @@ -0,0 +1,46 @@ +package nextstep.courses.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class MemberTest { + + @Test + @DisplayName("Member๋Š” ์ƒ์„ฑ ์‹œ id, name, email ๊ฐ’์„ ๊ฐ€์ง„๋‹ค") + void member_creation_and_getters() { + Member member = new Member(1L, "ํ™๊ธธ๋™", "hong@example.com"); + + assertThat(member.getId()).isEqualTo(1L); + assertThat(member.getName()).isEqualTo("ํ™๊ธธ๋™"); + assertThat(member.getEmail()).isEqualTo("hong@example.com"); + } + + @Test + @DisplayName("id๊ฐ€ ๊ฐ™์€ ๋‘ Member๋Š” equals๋กœ ๊ฐ™๋‹ค๊ณ  ํŒ๋‹จํ•œ๋‹ค") + void members_with_same_id_are_equal() { + Member member1 = new Member(1L, "ํ™๊ธธ๋™", "hong@example.com"); + Member member2 = new Member(1L, "์ด๋ฆ„๋ฌด๊ด€", "๋‹ค๋ฅธ์ด๋ฉ”์ผ@example.com"); + + assertThat(member1).isEqualTo(member2); + } + + @Test + @DisplayName("id๊ฐ€ ๋‹ค๋ฅธ ๋‘ Member๋Š” equals๋กœ ๋‹ค๋ฅด๋‹ค๊ณ  ํŒ๋‹จํ•œ๋‹ค") + void members_with_different_id_are_not_equal() { + Member member1 = new Member(1L, "ํ™๊ธธ๋™", "hong@example.com"); + Member member2 = new Member(2L, "ํ™๊ธธ๋™", "hong@example.com"); + + assertThat(member1).isNotEqualTo(member2); + } + + @Test + @DisplayName("id๊ฐ€ ๊ฐ™์œผ๋ฉด hashCode๋„ ๋™์ผํ•˜๋‹ค") + void members_with_same_id_have_same_hashcode() { + Member member1 = new Member(1L, "ํ™๊ธธ๋™", "hong@example.com"); + Member member2 = new Member(1L, "๋‹ค๋ฅธ์ด๋ฆ„", "๋‹ค๋ฅธ์ด๋ฉ”์ผ@example.com"); + + assertThat(member1.hashCode()).isEqualTo(member2.hashCode()); + } +} diff --git a/src/test/java/nextstep/courses/domain/SessionTest.java b/src/test/java/nextstep/courses/domain/SessionTest.java index 75a9e4de7..3af277e05 100644 --- a/src/test/java/nextstep/courses/domain/SessionTest.java +++ b/src/test/java/nextstep/courses/domain/SessionTest.java @@ -5,12 +5,15 @@ import org.junit.jupiter.api.Test; import java.time.LocalDateTime; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; class SessionTest { - private final Image validImage = new Image(500f, "png", "cdn.com", 600, 400); + private final Images validImages = new Images(List.of(validImage)); + private final Member member = new Member(1L, "ํ™๊ธธ๋™", "hong@example.com"); @Test @DisplayName("๋ชจ์ง‘์ค‘ ์ƒํƒœ์˜ ๋ฌด๋ฃŒ ๊ฐ•์˜๋Š” ์ˆ˜๊ฐ• ์‹ ์ฒญ ๊ฐ€๋Šฅํ•˜๋‹ค") @@ -23,8 +26,9 @@ void freeSession_joinable_whenRecruiting() { 0L, // tuition 0, // currentCount 0, // capacity (๋ฌด์ œํ•œ์ด์ง€๋งŒ ๊ทธ๋ƒฅ 0์œผ๋กœ ๋‘ ) - validImage, - SessionStatus.RECRUITING, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, new FreeJoinStrategy() ); @@ -42,8 +46,9 @@ void freeSession_notJoinable_whenNotRecruiting() { 0L, 0, 0, - validImage, + validImages, SessionStatus.PREPARING, + RecruitmentStatus.RECRUITING, new FreeJoinStrategy() ); @@ -61,8 +66,9 @@ void paidSession_joinable_whenRecruiting_underCapacity_andPaidCorrectly() { 10000L, // tuition 29, // currentCount 30, // capacity - validImage, - SessionStatus.RECRUITING, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, new PaidJoinStrategy() ); @@ -80,8 +86,9 @@ void paidSession_notJoinable_whenWrongAmount() { 10000L, 10, 30, - validImage, - SessionStatus.RECRUITING, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, new PaidJoinStrategy() ); @@ -99,11 +106,167 @@ void paidSession_notJoinable_whenOverCapacity() { 10000L, 30, 30, - validImage, - SessionStatus.RECRUITING, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, new PaidJoinStrategy() ); assertThat(session.joinable(new Payment(10000L))).isFalse(); } + + @Test + @DisplayName("๊ฐ•์˜ ์ƒํƒœ๊ฐ€ PREPARING์ด๋ฉด ๋ชจ์ง‘์ค‘์ด์–ด๋„ ์ˆ˜๊ฐ• ์‹ ์ฒญ ๋ถˆ๊ฐ€") + void notJoinable_whenLecturePreparing_evenIfRecruiting() { + Session session = new Session( + "๊ฐ•์˜ ์ค€๋น„ ์ค‘", + 1, + LocalDateTime.now(), + LocalDateTime.now().plusDays(7), + 0L, + 0, + 0, + validImages, + SessionStatus.PREPARING, + RecruitmentStatus.RECRUITING, + new FreeJoinStrategy() + ); + + assertThat(session.joinable(new Payment())).isFalse(); + } + + @Test + @DisplayName("๊ฐ•์˜ ์ƒํƒœ๊ฐ€ CLOSED์ด๋ฉด ๋ชจ์ง‘์ค‘์ด์–ด๋„ ์ˆ˜๊ฐ• ์‹ ์ฒญ ๋ถˆ๊ฐ€") + void notJoinable_whenLectureClosed_evenIfRecruiting() { + Session session = new Session( + "์ข…๋ฃŒ๋œ ๊ฐ•์˜", + 1, + LocalDateTime.now().minusDays(10), + LocalDateTime.now().minusDays(3), + 0L, + 0, + 0, + validImages, + SessionStatus.CLOSED, + RecruitmentStatus.RECRUITING, + new FreeJoinStrategy() + ); + + assertThat(session.joinable(new Payment())).isFalse(); + } + + @Test + @DisplayName("๋ชจ์ง‘ ์ƒํƒœ๊ฐ€ NOT_RECRUITING์ด๋ฉด ๊ฐ•์˜๊ฐ€ Ongoing์ด์–ด๋„ ์ˆ˜๊ฐ• ์‹ ์ฒญ ๋ถˆ๊ฐ€") + void notJoinable_whenNotRecruiting() { + Session session = new Session( + "๋ชจ์ง‘ ๋น„ํ™œ์„ฑ ๊ฐ•์˜", + 1, + LocalDateTime.now(), + LocalDateTime.now().plusDays(7), + 0L, + 0, + 0, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.NOT_RECRUITING, + new FreeJoinStrategy() + ); + + assertThat(session.joinable(new Payment())).isFalse(); + } + + @Test + @DisplayName("์ˆ˜๊ฐ• ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋ฉด ์ˆ˜๊ฐ• ์‹ ์ฒญ์ด PENDING ์ƒํƒœ๋กœ ๋“ฑ๋ก๋œ๋‹ค") + void enroll_success_creates_pending_enrollment() { + Session session = new Session("๊ฐ•์˜", 1, + LocalDateTime.now(), LocalDateTime.now().plusDays(7), + 0L, 0, 10, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, + new FreeJoinStrategy() + ); + + session.enroll(new Payment(), member); + + Enrollment enrollment = session.getEnrollments().findByMember(member); + assertThat(enrollment.getStatus()).isEqualTo(EnrollmentStatus.PENDING); + } + + @Test + @DisplayName("๋™์ผํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ์ค‘๋ณต ์‹ ์ฒญ ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ") + void duplicate_enrollment_should_throw() { + Session session = new Session("๊ฐ•์˜", 1, + LocalDateTime.now(), LocalDateTime.now().plusDays(7), + 0L, 0, 10, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, + new FreeJoinStrategy() + ); + + session.enroll(new Payment(), member); + + assertThatThrownBy(() -> session.enroll(new Payment(), member)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("์ด๋ฏธ ์ˆ˜๊ฐ• ์‹ ์ฒญํ•œ ํšŒ์›์ž…๋‹ˆ๋‹ค."); + } + + @Test + @DisplayName("๊ฐ•์‚ฌ๊ฐ€ ์ˆ˜๊ฐ• ์‹ ์ฒญ์„ ์Šน์ธํ•˜๋ฉด APPROVED ์ƒํƒœ๊ฐ€ ๋˜๊ณ  ์ •์›์ด ์ฆ๊ฐ€ํ•œ๋‹ค") + void approve_enrollment_increases_capacity() { + Session session = new Session("๊ฐ•์˜", 1, + LocalDateTime.now(), LocalDateTime.now().plusDays(7), + 0L, 0, 10, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, + new FreeJoinStrategy() + ); + + session.enroll(new Payment(), member); + session.approveEnrollment(member); + + Enrollment enrollment = session.getEnrollments().findByMember(member); + assertThat(enrollment.getStatus()).isEqualTo(EnrollmentStatus.APPROVED); + assertThat(session.getCurrentCount()).isEqualTo(1); + } + + @Test + @DisplayName("๊ฐ•์‚ฌ๊ฐ€ ์ˆ˜๊ฐ• ์‹ ์ฒญ์„ ๊ฑฐ์ ˆํ•˜๋ฉด REJECTED ์ƒํƒœ๊ฐ€ ๋œ๋‹ค") + void reject_enrollment_sets_rejected_status() { + Session session = new Session("๊ฐ•์˜", 1, + LocalDateTime.now(), LocalDateTime.now().plusDays(7), + 0L, 0, 10, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, + new FreeJoinStrategy() + ); + + session.enroll(new Payment(), member); + session.rejectEnrollment(member); + + Enrollment enrollment = session.getEnrollments().findByMember(member); + assertThat(enrollment.getStatus()).isEqualTo(EnrollmentStatus.REJECTED); + } + + @Test + @DisplayName("์Šน์ธํ•˜์ง€ ์•Š์œผ๋ฉด ์ˆ˜๊ฐ• ์‹ ์ฒญ์€ APPROVED ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋‹ค") + void pending_enrollment_not_approved_by_default() { + Session session = new Session("๊ฐ•์˜", 1, + LocalDateTime.now(), LocalDateTime.now().plusDays(7), + 0L, 0, 10, + validImages, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, + new FreeJoinStrategy() + ); + + session.enroll(new Payment(), member); + + Enrollment enrollment = session.getEnrollments().findByMember(member); + assertThat(enrollment.isApproved()).isFalse(); + } + } diff --git a/src/test/java/nextstep/courses/domain/SessionsTest.java b/src/test/java/nextstep/courses/domain/SessionsTest.java index 7b2ea5065..327f4ebfc 100644 --- a/src/test/java/nextstep/courses/domain/SessionsTest.java +++ b/src/test/java/nextstep/courses/domain/SessionsTest.java @@ -9,8 +9,8 @@ import static org.assertj.core.api.Assertions.*; class SessionsTest { - private final Image image = new Image(500f, "png", "cdn.com", 600, 400); + private final Images images = new Images(List.of(image)); private Session createSessionWithId(int id) { return new Session( @@ -21,8 +21,9 @@ private Session createSessionWithId(int id) { 0L, 0, 0, - image, - SessionStatus.RECRUITING, + images, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING, new FreeJoinStrategy() ); } 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 000000000..457d02761 --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/EnrollmentRepositoryTest.java @@ -0,0 +1,81 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@JdbcTest +class JdbcEnrollmentRepositoryTest { + + @Autowired + private JdbcTemplate jdbc; + + private JdbcSessionRepository sessionRepository; + private JdbcEnrollmentRepository enrollmentRepository; + + @BeforeEach + void setUp() { + sessionRepository = new JdbcSessionRepository(jdbc); + enrollmentRepository = new JdbcEnrollmentRepository(jdbc, sessionRepository); + + // ํ…Œ์ŠคํŠธ์šฉ ๋ฐ์ดํ„ฐ ์‚ฝ์ž… + jdbc.update("INSERT INTO member (id, name, email) VALUES (?, ?, ?)", + 1L, "ํ™๊ธธ๋™", "hong@example.com"); + + jdbc.update("INSERT INTO session (id, title, start_date, end_date, tuition, current_count, capacity, status, recruitment_status, course_id) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + 1L, "๋„๋ฉ”์ธ ์ฃผ๋„ ์„ค๊ณ„", + LocalDateTime.now(), LocalDateTime.now().plusDays(30), + 10000L, 0, 20, + "ONGOING", "RECRUITING", 1L + ); + + // image insert ์—†์ด๋„ session์€ ์ƒ์„ฑ๋จ + } + + @Test + @DisplayName("Enrollment ์ €์žฅ ํ›„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค (Session ํฌํ•จ)") + void save_and_find_with_session() { + // when + enrollmentRepository.save(1L, 1L, EnrollmentStatus.PENDING); + + // then + List result = enrollmentRepository.findBySessionId(1L); + assertThat(result).hasSize(1); + + Enrollment enrollment = result.get(0); + assertThat(enrollment.getStudent().getId()).isEqualTo(1L); + assertThat(enrollment.getStatus()).isEqualTo(EnrollmentStatus.PENDING); + + // โœ… Session๊นŒ์ง€ ์ œ๋Œ€๋กœ ์—ฐ๊ฒฐ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ + Session session = enrollment.getSession(); + assertThat(session).isNotNull(); + assertThat(session.getTitle()).isEqualTo("๋„๋ฉ”์ธ ์ฃผ๋„ ์„ค๊ณ„"); + assertThat(session.getTuition()).isEqualTo(10000L); + } + + @Test + @DisplayName("Enrollment ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค") + void update_status() { + // given + enrollmentRepository.save(1L, 1L, EnrollmentStatus.PENDING); + + // when + enrollmentRepository.updateStatus(1L, 1L, EnrollmentStatus.APPROVED); + + // then + List result = enrollmentRepository.findBySessionId(1L); + Enrollment enrollment = result.get(0); + + assertThat(enrollment.getStatus()).isEqualTo(EnrollmentStatus.APPROVED); + } +} diff --git a/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java index 94523c758..ad4442989 100644 --- a/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java +++ b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java @@ -1,9 +1,6 @@ package nextstep.courses.infrastructure; -import nextstep.courses.domain.Image; -import nextstep.courses.domain.Session; -import nextstep.courses.domain.SessionRepository; -import nextstep.courses.domain.SessionStatus; +import nextstep.courses.domain.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -13,6 +10,7 @@ import org.springframework.jdbc.core.JdbcTemplate; import java.time.LocalDateTime; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -34,29 +32,39 @@ void setUp() { void crud() { // given Image image = new Image(100.0f, "png", "https://example.com/image.png", 300, 200); + Images images = new Images(List.of(image)); Session session = new Session( "๋„๋ฉ”์ธ ์ฃผ๋„ ์„ค๊ณ„", - 1, + 0, // ์ €์žฅ ์ „์— id๋Š” ๋ฌด์˜๋ฏธํ•จ LocalDateTime.now(), LocalDateTime.now().plusDays(30), 10000L, 0, 20, - image, - SessionStatus.RECRUITING + images, + SessionStatus.ONGOING, + RecruitmentStatus.RECRUITING ); // when - int count = sessionRepository.save(session, 1L); // courseId = 1L - assertThat(count).isEqualTo(1); - - Session saved = sessionRepository.findById(1L); + int savedSessionId = sessionRepository.save(session, 1L); // courseId = 1L + Session saved = sessionRepository.findById((long) savedSessionId); // then assertThat(saved.getTitle()).isEqualTo(session.getTitle()); assertThat(saved.getTuition()).isEqualTo(session.getTuition()); assertThat(saved.getCapacity()).isEqualTo(session.getCapacity()); - assertThat(saved.getCoverImage().getImageUrl()).isEqualTo(session.getCoverImage().getImageUrl()); + + // ์ด๋ฏธ์ง€ ๊ฒ€์ฆ + List savedImages = saved.getCoverImages().getImages(); + assertThat(savedImages).hasSize(1); + + Image savedImage = savedImages.get(0); + assertThat(savedImage.getImageUrl()).isEqualTo(image.getImageUrl()); + assertThat(savedImage.getSize()).isEqualTo(image.getSize()); + assertThat(savedImage.getType()).isEqualTo(image.getType()); + assertThat(savedImage.getWidth()).isEqualTo(image.getWidth()); + assertThat(savedImage.getHeight()).isEqualTo(image.getHeight()); LOGGER.debug("Session: {}", saved); }