From 845e2a8ad6de00b4cba1f650532bc26a79bfc130 Mon Sep 17 00:00:00 2001 From: heewonham Date: Mon, 21 Apr 2025 18:52:25 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat=20:=20(review)=20=EA=B0=92=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20equal,=20hashcode=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/courses/domain/Capacity.java | 15 +++++++++++++++ src/main/java/nextstep/courses/domain/Period.java | 15 +++++++++++++++ .../java/nextstep/courses/domain/TuitionFee.java | 14 ++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/main/java/nextstep/courses/domain/Capacity.java b/src/main/java/nextstep/courses/domain/Capacity.java index 05ddba85c..9db736f60 100644 --- a/src/main/java/nextstep/courses/domain/Capacity.java +++ b/src/main/java/nextstep/courses/domain/Capacity.java @@ -1,5 +1,7 @@ package nextstep.courses.domain; +import java.util.Objects; + public class Capacity { private final int value; @@ -17,4 +19,17 @@ public int getValue() { public boolean isFull(int registeredCount) { return registeredCount >= value; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Capacity)) return false; + Capacity capacity = (Capacity) o; + return value == capacity.value; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } } \ No newline at end of file diff --git a/src/main/java/nextstep/courses/domain/Period.java b/src/main/java/nextstep/courses/domain/Period.java index ca47de8b4..6b4eced95 100644 --- a/src/main/java/nextstep/courses/domain/Period.java +++ b/src/main/java/nextstep/courses/domain/Period.java @@ -1,6 +1,7 @@ package nextstep.courses.domain; import java.time.LocalDate; +import java.util.Objects; public class Period { private final LocalDate startDate; @@ -21,4 +22,18 @@ public LocalDate getStartDate() { public LocalDate getEndDate() { return endDate; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Period)) return false; + Period period = (Period) o; + return Objects.equals(startDate, period.startDate) && + Objects.equals(endDate, period.endDate); + } + + @Override + public int hashCode() { + return Objects.hash(startDate, endDate); + } } diff --git a/src/main/java/nextstep/courses/domain/TuitionFee.java b/src/main/java/nextstep/courses/domain/TuitionFee.java index 27d3935c3..20c8875d5 100644 --- a/src/main/java/nextstep/courses/domain/TuitionFee.java +++ b/src/main/java/nextstep/courses/domain/TuitionFee.java @@ -1,5 +1,7 @@ package nextstep.courses.domain; +import java.util.Objects; + public class TuitionFee { private final int amount; @@ -12,5 +14,17 @@ public TuitionFee(int amount) { public int getAmount() { return amount; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TuitionFee)) return false; + TuitionFee that = (TuitionFee) o; + return amount == that.amount; + } + + @Override + public int hashCode() { + return Objects.hash(amount); } } \ No newline at end of file From fbca01548db72255f38ec2d9281b29158bda2e2d Mon Sep 17 00:00:00 2001 From: heewonham Date: Mon, 21 Apr 2025 18:53:41 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat=20:=20(review)=20students=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=89=20=EC=BB=AC=EB=A0=89=EC=85=98=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/courses/domain/Session.java | 12 ++--- .../java/nextstep/courses/domain/Student.java | 30 +++++++++++++ .../nextstep/courses/domain/Students.java | 28 ++++++++++++ .../java/nextstep/qna/domain/SessionTest.java | 2 +- .../nextstep/qna/domain/StudentsTest.java | 44 +++++++++++++++++++ 5 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 src/main/java/nextstep/courses/domain/Student.java create mode 100644 src/main/java/nextstep/courses/domain/Students.java create mode 100644 src/test/java/nextstep/qna/domain/StudentsTest.java diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index 25a86af5b..6b9c3333c 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -2,15 +2,13 @@ import nextstep.payments.domain.Payment; -import java.util.HashSet; -import java.util.Set; public abstract class Session { protected Long id; protected String name; protected Period period; protected Image coverImage; protected SessionStatus status; - protected Set registeredStudents = new HashSet<>(); + protected Students registeredStudents; public Session(Long id, String name, Period period, Image coverImage, SessionStatus status) { this.id = id; @@ -18,6 +16,7 @@ public Session(Long id, String name, Period period, Image coverImage, SessionSta this.period = period; this.coverImage = coverImage; this.status = status; + this.registeredStudents = new Students(); } public void register(Long studentId, Payment payment) { @@ -26,11 +25,12 @@ public void register(Long studentId, Payment payment) { } validateRegistration(studentId, payment); - registeredStudents.add(studentId); + registeredStudents.addStudent(new Student(studentId)); } - public boolean isRegistered(Long studentId) { - return registeredStudents.contains(studentId); + public Students getRegisteredStudent() { + return registeredStudents; + } protected abstract void validateRegistration(Long studentId, Payment payment); diff --git a/src/main/java/nextstep/courses/domain/Student.java b/src/main/java/nextstep/courses/domain/Student.java new file mode 100644 index 000000000..f76d9877a --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Student.java @@ -0,0 +1,30 @@ +package nextstep.courses.domain; + +public class Student { + Long id; + public Student() { + } + public Student(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public boolean contains(Long studentId) { + return id.equals(studentId); + } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Student student = (Student) o; + return id != null && id.equals(student.id); + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} diff --git a/src/main/java/nextstep/courses/domain/Students.java b/src/main/java/nextstep/courses/domain/Students.java new file mode 100644 index 000000000..ebb2a1ea6 --- /dev/null +++ b/src/main/java/nextstep/courses/domain/Students.java @@ -0,0 +1,28 @@ +package nextstep.courses.domain; + +import java.util.HashSet; +import java.util.Set; + +public class Students { + private Set students = new HashSet<>(); + + public void addStudent(Student student) { + if (contains(student)) { + throw new IllegalArgumentException("이미 등록된 학생입니다."); + } + students.add(student); + } + + public void removeStudent(Student student) { + students.remove(student); + } + + public boolean contains(Student student) { + return students.stream() + .anyMatch(s -> s.equals(student)); + } + + public int size() { + return students.size(); + } +} diff --git a/src/test/java/nextstep/qna/domain/SessionTest.java b/src/test/java/nextstep/qna/domain/SessionTest.java index 98e285acd..06de4283a 100644 --- a/src/test/java/nextstep/qna/domain/SessionTest.java +++ b/src/test/java/nextstep/qna/domain/SessionTest.java @@ -36,7 +36,7 @@ void registerSession() { SessionStatus.RECRUITING ); session.register(1L, new Payment()); - assertThat(session.isRegistered(1L)).isTrue(); + assertThat(session.getRegisteredStudent().contains(new Student(1L))).isTrue(); } @Test diff --git a/src/test/java/nextstep/qna/domain/StudentsTest.java b/src/test/java/nextstep/qna/domain/StudentsTest.java new file mode 100644 index 000000000..7807194ee --- /dev/null +++ b/src/test/java/nextstep/qna/domain/StudentsTest.java @@ -0,0 +1,44 @@ +package nextstep.qna.domain; + +import nextstep.courses.domain.Capacity; +import nextstep.courses.domain.Student; +import nextstep.courses.domain.Students; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +public class StudentsTest { + + @Test + @DisplayName("같은 id를 가진 학생을 두 번 추가할 수 없다.") + void cannotRegisteredWithSameId() { + Students students = new Students(); + students.addStudent(new Student(1L)); + assertThatThrownBy(() -> students.addStudent(new Student(1L))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이미 등록된 학생입니다."); + } + + @Test + void checkRegisteredStudents() { + Students students = new Students(); + students.addStudent(new Student(1L)); + assertTrue(students.contains(new Student(1L))); + assertFalse(students.contains(new Student(99L))); + } + + @Test + @DisplayName("학생을 추가하고 제거할 수 있다.") + void addAndRemoveStudent() { + Students students = new Students(); + + Student student = new Student(1L); + students.addStudent(student); + assertTrue(students.contains(student)); + + students.removeStudent(student); + assertFalse(students.contains(student)); + } +} From 37e118057151275d61807c5d4d495da0299eb547 Mon Sep 17 00:00:00 2001 From: heewonham Date: Mon, 21 Apr 2025 18:54:16 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat=20:=20(review)=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD,=20=EC=9D=98=EB=AF=B8?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20=EC=83=81=EC=88=98=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD,=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/courses/domain/Image.java | 12 ++++++------ .../java/nextstep/courses/domain/PaidSession.java | 2 +- .../java/nextstep/courses/domain/TuitionFee.java | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/nextstep/courses/domain/Image.java b/src/main/java/nextstep/courses/domain/Image.java index 0ae0186d8..fc080c95f 100644 --- a/src/main/java/nextstep/courses/domain/Image.java +++ b/src/main/java/nextstep/courses/domain/Image.java @@ -1,16 +1,16 @@ package nextstep.courses.domain; public class Image { + private static final int MAX_IMAGE_SIZE = 1024 * 1024; + private static final int WIDTH_LIMIT = 300; + private static final int HEIGHT_LIMIT = 200; + private static final double REQUIRED_ASPECT_RATIO = 1.5; + private static final double ASPECT_RATIO_TOLERANCE = 0.01; private String fileName; private String contentType; private long sizeInBytes; private int width; private int height; - private static final int IMAGE_SIZE_1MB = 1024 * 1024; - private static final int WIDTH_LIMIT = 300; - private static final int HEIGHT_LIMIT = 200; - private static final double REQUIRED_ASPECT_RATIO = 1.5; - private static final double ASPECT_RATIO_TOLERANCE = 0.01; public Image(String fileName, String contentType, long sizeInBytes, int width, int height) { validate(contentType, sizeInBytes, width, height); @@ -22,7 +22,7 @@ public Image(String fileName, String contentType, long sizeInBytes, int width, i } private void validate(String contentType, long sizeInBytes, int width, int height) { - if (sizeInBytes > IMAGE_SIZE_1MB) { + if (sizeInBytes > MAX_IMAGE_SIZE) { throw new IllegalArgumentException("이미지 크기는 1MB 이하여야 합니다."); } diff --git a/src/main/java/nextstep/courses/domain/PaidSession.java b/src/main/java/nextstep/courses/domain/PaidSession.java index 187190ef1..70c6e0389 100644 --- a/src/main/java/nextstep/courses/domain/PaidSession.java +++ b/src/main/java/nextstep/courses/domain/PaidSession.java @@ -27,7 +27,7 @@ protected void validateRegistration(Long studentId, Payment payment) { if (!studentId.equals(payment.getNsUserId())) { throw new IllegalArgumentException("결제한 사용자와 일치하지 않습니다."); } - if (this.tuitionFee.getAmount() != payment.getAmount()) { + if (!this.tuitionFee.isSameAmount(payment.getAmount())) { throw new IllegalArgumentException("결제 금액과 일치하지 않습니다."); } } diff --git a/src/main/java/nextstep/courses/domain/TuitionFee.java b/src/main/java/nextstep/courses/domain/TuitionFee.java index 20c8875d5..e9f0cd128 100644 --- a/src/main/java/nextstep/courses/domain/TuitionFee.java +++ b/src/main/java/nextstep/courses/domain/TuitionFee.java @@ -12,8 +12,9 @@ public TuitionFee(int amount) { this.amount = amount; } - public int getAmount() { - return amount; + public boolean isSameAmount(int amount) { + return this.amount == amount; + } @Override public boolean equals(Object o) { From 623f840d371d9c637a88a6342aaff7e647d500c8 Mon Sep 17 00:00:00 2001 From: heewonham Date: Tue, 22 Apr 2025 00:10:50 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat=20:=20session=20Repository=20=EB=B0=8F?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit initmode always는 test 환경임으로 설정한다. --- .../java/nextstep/courses/domain/Image.java | 16 +++ .../nextstep/courses/domain/PaidSession.java | 8 ++ .../java/nextstep/courses/domain/Session.java | 29 ++++- .../infrastructure/JdbcSessionRepository.java | 118 ++++++++++++++++++ src/main/resources/application.properties | 2 + src/main/resources/schema.sql | 27 ++++ .../infrastructure/SessionRepositoryTest.java | 72 +++++++++++ 7 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java create mode 100644 src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java diff --git a/src/main/java/nextstep/courses/domain/Image.java b/src/main/java/nextstep/courses/domain/Image.java index fc080c95f..847a443d4 100644 --- a/src/main/java/nextstep/courses/domain/Image.java +++ b/src/main/java/nextstep/courses/domain/Image.java @@ -21,6 +21,22 @@ public Image(String fileName, String contentType, long sizeInBytes, int width, i this.height = height; } + public String getFileName() { + return fileName; + } + public String getContentType() { + return contentType; + } + public long getSizeInBytes() { + return sizeInBytes; + } + public int getWidth() { + return width; + } + public int getHeight() { + return height; + } + private void validate(String contentType, long sizeInBytes, int width, int height) { if (sizeInBytes > MAX_IMAGE_SIZE) { throw new IllegalArgumentException("이미지 크기는 1MB 이하여야 합니다."); diff --git a/src/main/java/nextstep/courses/domain/PaidSession.java b/src/main/java/nextstep/courses/domain/PaidSession.java index 70c6e0389..a849f7481 100644 --- a/src/main/java/nextstep/courses/domain/PaidSession.java +++ b/src/main/java/nextstep/courses/domain/PaidSession.java @@ -13,6 +13,14 @@ public PaidSession(Long id, String name, Period period, Image coverImage, this.tuitionFee = new TuitionFee(tuitionFee); } + public Capacity getCapacity() { + return maxCapacity; + } + + public TuitionFee getTuitionFee() { + return tuitionFee; + } + @Override protected void validateRegistration(Long studentId, Payment payment) { if (registeredStudents.size() >= maxCapacity.getValue()) { diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index 6b9c3333c..b15236daf 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -19,6 +19,30 @@ public Session(Long id, String name, Period period, Image coverImage, SessionSta this.registeredStudents = new Students(); } + public Students getRegisteredStudent() { + return registeredStudents; + } + + public Image getCoverImage() { + return coverImage; + } + + public String getName() { + return name; + } + + public Period getPeriod() { + return period; + } + + public SessionStatus getStatus() { + return status; + } + + public Long getId() { + return id; + } + public void register(Long studentId, Payment payment) { if (status != SessionStatus.RECRUITING) { throw new IllegalStateException("수강 신청은 모집중일 때만 가능합니다."); @@ -28,11 +52,6 @@ public void register(Long studentId, Payment payment) { registeredStudents.addStudent(new Student(studentId)); } - public Students getRegisteredStudent() { - return registeredStudents; - - } - protected abstract void validateRegistration(Long studentId, Payment payment); public abstract SessionType getType(); 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 000000000..a58d50cfd --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -0,0 +1,118 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.Image; +import nextstep.courses.domain.Period; +import nextstep.courses.domain.Session; +import nextstep.courses.domain.SessionStatus; +import nextstep.courses.domain.PaidSession; +import nextstep.courses.domain.FreeSession; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; +import org.springframework.stereotype.Repository; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Repository +public class JdbcSessionRepository { + private final JdbcTemplate jdbcTemplate; + + public JdbcSessionRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public void save(Session session, Long courseId) { + Long imageId = null; + if (session.getCoverImage() != null) { + imageId = insertImage(session.getCoverImage()); + } + + String sql = "insert into session (name, type, start_date, end_date, status, capacity, tuition_fee, image_id, course_id) " + + "values (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + jdbcTemplate.update(sql, + session.getName(), + session.getType().name(), + session.getPeriod().getStartDate(), + session.getPeriod().getEndDate(), + session.getStatus().name(), + session instanceof PaidSession ? ((PaidSession) session).getCapacity() : null, + session instanceof PaidSession ? ((PaidSession) session).getTuitionFee() : null, + imageId, + courseId + ); + } + + public List findAll() { + String sql = "select s.*, i.file_name, i.content_type, i.size_in_bytes, i.width, i.height " + + "from session s left join image i on s.image_id = i.id"; + return jdbcTemplate.query(sql, sessionRowMapper()); + } + + public Session findById(Long id) { + String sql = "select s.*, i.file_name, i.content_type, i.size_in_bytes, i.width, i.height " + + "from session s left join image i on s.image_id = i.id where s.id = ?"; + return jdbcTemplate.queryForObject(sql, sessionRowMapper(), id); + } + + public void update(Session session) { + String sql = "update session set name = ?, start_date = ?, end_date = ?, status = ?, capacity = ?, tuition_fee = ? where id = ?"; + jdbcTemplate.update(sql, + session.getName(), + session.getPeriod().getStartDate(), + session.getPeriod().getEndDate(), + session.getStatus().name(), + session instanceof PaidSession ? ((PaidSession) session).getCapacity() : null, + session instanceof PaidSession ? ((PaidSession) session).getTuitionFee() : null, + session.getId() + ); + } + + public void deleteById(Long id) { + jdbcTemplate.update("delete from session where id = ?", id); + } + + private Long insertImage(Image image) { + SimpleJdbcInsert insert = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("image") + .usingGeneratedKeyColumns("id"); + + Map params = new HashMap<>(); + params.put("file_name", image.getFileName()); + params.put("content_type", image.getContentType()); + params.put("size_in_bytes", image.getSizeInBytes()); + params.put("width", image.getWidth()); + params.put("height", image.getHeight()); + + return insert.executeAndReturnKey(params).longValue(); + } + + private RowMapper sessionRowMapper() { + return (rs, rowNum) -> { + SessionStatus status = SessionStatus.valueOf(rs.getString("status")); + Period period = new Period(rs.getDate("start_date").toLocalDate(), rs.getDate("end_date").toLocalDate()); + + Image image = null; + if (rs.getString("file_name") != null) { + image = new Image( + rs.getString("file_name"), + rs.getString("content_type"), + rs.getLong("size_in_bytes"), + rs.getInt("width"), + rs.getInt("height") + ); + } + + String type = rs.getString("type"); + if (type.equals("FREE")) { + return new FreeSession(rs.getLong("id"), rs.getString("name"), period, image, status); + } else { + return new PaidSession(rs.getLong("id"), rs.getString("name"), period, image, status, + rs.getInt("capacity"), rs.getInt("tuition_fee")); + } + }; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 219fe02ad..af5e9b229 100755 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -2,5 +2,7 @@ spring.h2.console.enabled=true spring.datasource.url=jdbc:h2:mem://localhost/~/java-lms;DB_CLOSE_ON_EXIT=FALSE +spring.sql.init.mode=always + logging.level.org.springframework.jdbc.core=TRACE logging.level.org.springframework.jdbc.datasource.init=TRACE diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 8d5a988c8..aa2e7641d 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -48,3 +48,30 @@ create table delete_history ( deleted_by_id bigint, primary key (id) ); + +-- 추가 +create table image +( + id bigint generated by default as identity primary key, + file_name varchar(255) not null, + content_type varchar(50) not null, + size_in_bytes bigint not null, + width int not null, + height int not null +); + +create table session +( + id bigint generated by default as identity primary key, + name varchar(255) not null, + type varchar(10) not null, -- FREE, PAID + start_date date not null, + end_date date not null, + status varchar(20) not null, -- RECRUITING 등 + capacity int, -- PaidSession 전용 + tuition_fee int, -- PaidSession 전용 + image_id bigint, + course_id bigint not null, + foreign key (image_id) references image (id), + foreign key (course_id) references course (id) +); 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 000000000..5636f6a68 --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java @@ -0,0 +1,72 @@ +package nextstep.courses.infrastructure; + +import nextstep.courses.domain.FreeSession; +import nextstep.courses.domain.Image; +import nextstep.courses.domain.Period; +import nextstep.courses.domain.Session; +import nextstep.courses.domain.SessionStatus; + +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.LocalDate; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@JdbcTest +public class SessionRepositoryTest { + private JdbcSessionRepository sessionRepository; + + @Autowired + private JdbcTemplate jdbcTemplate; + + @BeforeEach + void setUp() { + sessionRepository = new JdbcSessionRepository(jdbcTemplate); + jdbcTemplate.update("insert into course (id, title, creator_id, created_at) " + + "values (1, '샘플 강의', 42, current_timestamp)"); + } + + @Test + @DisplayName("CRUD 테스트") + void crudTest() { + Long courseId = 1L; + + // Create + FreeSession session = new FreeSession(null, "세션1", + new Period(LocalDate.of(2025, 5, 1), LocalDate.of(2025, 5, 10)), + new Image("test.jpg", "jpeg", 100000, 600, 400), + SessionStatus.RECRUITING); + sessionRepository.save(session, courseId); + + // Read (findAll) + List sessions = sessionRepository.findAll(); + assertThat(sessions).hasSize(1); + + // Read (findById) + Session loaded = sessionRepository.findById(sessions.get(0).getId()); + assertThat(loaded.getName()).isEqualTo("세션1"); + + // Update + Session updatedSession = new FreeSession( + loaded.getId(), + "수정된 세션", + loaded.getPeriod(), + loaded.getCoverImage(), + loaded.getStatus() + ); + sessionRepository.update(updatedSession); + Session updated = sessionRepository.findById(loaded.getId()); + assertThat(updated.getName()).isEqualTo("수정된 세션"); + + // Delete + sessionRepository.deleteById(loaded.getId()); + List afterDelete = sessionRepository.findAll(); + assertThat(afterDelete).isEmpty(); + } +} From c17e5ec601c47face2f2b464cb08ffa39a1dd4c5 Mon Sep 17 00:00:00 2001 From: heewonham Date: Tue, 22 Apr 2025 00:11:44 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat=20:=20sessionStudent=20repository=20?= =?UTF-8?q?=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../JdbcSessionStudentRepository.java | 46 +++++++++++ src/main/resources/schema.sql | 10 +++ .../SessionStudentRepositoryTest.java | 81 +++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 src/main/java/nextstep/courses/infrastructure/JdbcSessionStudentRepository.java create mode 100644 src/test/java/nextstep/courses/infrastructure/SessionStudentRepositoryTest.java diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionStudentRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionStudentRepository.java new file mode 100644 index 000000000..6e9416f5f --- /dev/null +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionStudentRepository.java @@ -0,0 +1,46 @@ +package nextstep.courses.infrastructure; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class JdbcSessionStudentRepository { + private final JdbcTemplate jdbcTemplate; + + public JdbcSessionStudentRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public void register(Long sessionId, Long studentId) { + String sql = "insert into session_student (session_id, student_id) values (?, ?)"; + jdbcTemplate.update(sql, sessionId, studentId); + } + + public void unregister(Long sessionId, Long studentId) { + String sql = "delete from session_student where session_id = ? and student_id = ?"; + jdbcTemplate.update(sql, sessionId, studentId); + } + + public List findStudentIdsBySessionId(Long sessionId) { + String sql = "select student_id from session_student where session_id = ?"; + return jdbcTemplate.query(sql, + (rs, rowNum) -> rs.getLong("student_id"), + sessionId); + } + + public boolean isRegistered(Long sessionId, Long studentId) { + String sql = "select count(*) from session_student where session_id = ? and student_id = ?"; + Integer count = jdbcTemplate.queryForObject(sql, Integer.class, sessionId, studentId); + return count != null && count > 0; + } + + public void deleteAllBySessionId(Long sessionId) { + jdbcTemplate.update("delete from session_student where session_id = ?", sessionId); + } + + public void deleteAllByStudentId(Long studentId) { + jdbcTemplate.update("delete from session_student where student_id = ?", studentId); + } +} \ No newline at end of file diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index aa2e7641d..a3848dde5 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -75,3 +75,13 @@ create table session foreign key (image_id) references image (id), foreign key (course_id) references course (id) ); + +create table session_student +( + session_id bigint not null, + student_id bigint not null, + + primary key (session_id, student_id), + foreign key (session_id) references session (id), + foreign key (student_id) references ns_user (id) +); \ No newline at end of file diff --git a/src/test/java/nextstep/courses/infrastructure/SessionStudentRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/SessionStudentRepositoryTest.java new file mode 100644 index 000000000..0ed1755f9 --- /dev/null +++ b/src/test/java/nextstep/courses/infrastructure/SessionStudentRepositoryTest.java @@ -0,0 +1,81 @@ +package nextstep.courses.infrastructure; + +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.LocalDate; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@JdbcTest +public class SessionStudentRepositoryTest { + @Autowired + JdbcTemplate jdbcTemplate; + + JdbcSessionStudentRepository repository; + + @BeforeEach + void setUp() { + repository = new JdbcSessionStudentRepository(jdbcTemplate); + + // 테스트용 데이터 삽입 (session, ns_user) + jdbcTemplate.update("insert into course (id, title, creator_id, created_at) values (1, '강의', 1, current_timestamp)"); + jdbcTemplate.update("insert into session (id, name, type, start_date, end_date, status, course_id) " + + "values (100, '세션', 'FREE', current_date, current_date + 1, 'RECRUITING', 1)"); + jdbcTemplate.update("insert into ns_user (id, user_id, password, name, email, created_at) " + + "values (200, 'student1', 'pass', '학생1', 'student@example.com', current_timestamp)"); + } + + @Test + @DisplayName("학생 등록, 제외, 조회 테스트") + void registerStudentTest() { + Long sessionId = 100L; + Long studentId = 200L; + + repository.register(sessionId, studentId); + + List studentIds = repository.findStudentIdsBySessionId(sessionId); + assertThat(studentIds).containsExactly(studentId); + assertThat(repository.isRegistered(sessionId, studentId)).isTrue(); + + repository.unregister(sessionId, studentId); + assertThat(repository.isRegistered(sessionId, studentId)).isFalse(); + } + + @Test + @DisplayName("세션 id로 모든 학생 삭제 테스트") + void deleteAllBySessionId() { + Long sessionId = 100L; + Long student1 = 200L; + Long student2 = 201L; + + jdbcTemplate.update("insert into ns_user (id, user_id, password, name, email, created_at) " + + "values (?, ?, ?, ?, ?, current_timestamp)", + student2, "student2", "pass", "학생2", "s2@example.com"); + + repository.register(sessionId, student1); + repository.register(sessionId, student2); + assertThat(repository.findStudentIdsBySessionId(sessionId)).hasSize(2); + + repository.deleteAllBySessionId(sessionId); + assertThat(repository.findStudentIdsBySessionId(sessionId)).isEmpty(); + } + + @Test + @DisplayName("학생 id로 모든 세션 삭제 테스트") + void deleteAllByStudentId() { + Long sessionId = 100L; + Long studentId = 200L; + + repository.register(sessionId, studentId); + assertThat(repository.findStudentIdsBySessionId(sessionId)).hasSize(1); + + repository.deleteAllByStudentId(studentId); + assertThat(repository.findStudentIdsBySessionId(sessionId)).isEmpty(); + } +} From f8e00f3d9ae057506bda185064212cd26baef6e2 Mon Sep 17 00:00:00 2001 From: heewonham Date: Thu, 24 Apr 2025 09:52:57 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feat=20:=20(review)=20namedjdbctemplate=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=20=EB=B0=8F=20sessionstudent=20=EB=A0=88?= =?UTF-8?q?=ED=8F=AC=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + .../java/nextstep/courses/domain/Session.java | 8 ++ .../nextstep/courses/domain/Students.java | 5 + .../nextstep/courses/domain/TuitionFee.java | 4 + .../infrastructure/JdbcSessionRepository.java | 114 +++++++++++++----- .../JdbcSessionStudentRepository.java | 46 ------- .../infrastructure/SessionRepositoryTest.java | 92 +++++++++++--- .../SessionStudentRepositoryTest.java | 81 ------------- 8 files changed, 181 insertions(+), 171 deletions(-) delete mode 100644 src/main/java/nextstep/courses/infrastructure/JdbcSessionStudentRepository.java delete mode 100644 src/test/java/nextstep/courses/infrastructure/SessionStudentRepositoryTest.java diff --git a/.gitignore b/.gitignore index 249cf086a..3f37a7b39 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ out/ ### VS Code ### .vscode/ + +/bin/ \ No newline at end of file diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java index b15236daf..bfb6617f4 100644 --- a/src/main/java/nextstep/courses/domain/Session.java +++ b/src/main/java/nextstep/courses/domain/Session.java @@ -2,6 +2,9 @@ import nextstep.payments.domain.Payment; +import java.util.List; +import java.util.stream.Collectors; + public abstract class Session { protected Long id; protected String name; @@ -18,6 +21,11 @@ public Session(Long id, String name, Period period, Image coverImage, SessionSta this.status = status; this.registeredStudents = new Students(); } + public List getStudentIds() { + return registeredStudents.getStudents().stream() + .map(Student::getId) + .collect(Collectors.toList()); + } public Students getRegisteredStudent() { return registeredStudents; diff --git a/src/main/java/nextstep/courses/domain/Students.java b/src/main/java/nextstep/courses/domain/Students.java index ebb2a1ea6..65d29e1f3 100644 --- a/src/main/java/nextstep/courses/domain/Students.java +++ b/src/main/java/nextstep/courses/domain/Students.java @@ -2,6 +2,7 @@ import java.util.HashSet; import java.util.Set; +import java.util.Collections; public class Students { private Set students = new HashSet<>(); @@ -25,4 +26,8 @@ public boolean contains(Student student) { public int size() { return students.size(); } + + public Set getStudents() { + return Collections.unmodifiableSet(students); + } } diff --git a/src/main/java/nextstep/courses/domain/TuitionFee.java b/src/main/java/nextstep/courses/domain/TuitionFee.java index e9f0cd128..f7991f4cf 100644 --- a/src/main/java/nextstep/courses/domain/TuitionFee.java +++ b/src/main/java/nextstep/courses/domain/TuitionFee.java @@ -16,6 +16,10 @@ public boolean isSameAmount(int amount) { return this.amount == amount; } + public int getValue() { + return amount; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java index a58d50cfd..ec5cdb063 100644 --- a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java +++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java @@ -7,10 +7,13 @@ import nextstep.courses.domain.PaidSession; import nextstep.courses.domain.FreeSession; -import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.transaction.annotation.Transactional; import java.util.HashMap; import java.util.List; @@ -18,61 +21,102 @@ @Repository public class JdbcSessionRepository { + private final NamedParameterJdbcTemplate namedParameterJdbcTemplate; private final JdbcTemplate jdbcTemplate; public JdbcSessionRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; + this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate); } + @Transactional public void save(Session session, Long courseId) { Long imageId = null; if (session.getCoverImage() != null) { imageId = insertImage(session.getCoverImage()); } - String sql = "insert into session (name, type, start_date, end_date, status, capacity, tuition_fee, image_id, course_id) " + - "values (?, ?, ?, ?, ?, ?, ?, ?, ?)"; - - jdbcTemplate.update(sql, - session.getName(), - session.getType().name(), - session.getPeriod().getStartDate(), - session.getPeriod().getEndDate(), - session.getStatus().name(), - session instanceof PaidSession ? ((PaidSession) session).getCapacity() : null, - session instanceof PaidSession ? ((PaidSession) session).getTuitionFee() : null, - imageId, - courseId - ); + SimpleJdbcInsert insert = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("session") + .usingGeneratedKeyColumns("id"); + + Map params = new HashMap<>(); + params.put("name", session.getName()); + params.put("type", session.getType().name()); + params.put("start_date", session.getPeriod().getStartDate()); + params.put("end_date", session.getPeriod().getEndDate()); + params.put("status", session.getStatus().name()); + params.put("capacity", session instanceof PaidSession ? ((PaidSession) session).getCapacity().getValue() : null); + params.put("tuition_fee", session instanceof PaidSession ? ((PaidSession) session).getTuitionFee().getValue() : null); + params.put("image_id", imageId); + params.put("course_id", courseId); + + insert.execute(params); + } + + @Transactional + public void registerStudent(Long sessionId, Long studentId) { + String sql = "insert into session_student (session_id, student_id) values (:sessionId, :studentId)"; + MapSqlParameterSource params = new MapSqlParameterSource() + .addValue("sessionId", sessionId) + .addValue("studentId", studentId); + namedParameterJdbcTemplate.update(sql, params); } public List findAll() { String sql = "select s.*, i.file_name, i.content_type, i.size_in_bytes, i.width, i.height " + "from session s left join image i on s.image_id = i.id"; - return jdbcTemplate.query(sql, sessionRowMapper()); + List sessions = namedParameterJdbcTemplate.query(sql, sessionRowMapper()); + + // 각 세션에 대한 수강생 정보를 함께 조회 + for (Session session : sessions) { + List studentIds = findStudentIdsBySessionId(session.getId()); + for (Long studentId : studentIds) { + session.register(studentId, null); + } + } + return sessions; } public Session findById(Long id) { String sql = "select s.*, i.file_name, i.content_type, i.size_in_bytes, i.width, i.height " + - "from session s left join image i on s.image_id = i.id where s.id = ?"; - return jdbcTemplate.queryForObject(sql, sessionRowMapper(), id); + "from session s left join image i on s.image_id = i.id where s.id = :id"; + Session session = namedParameterJdbcTemplate.queryForObject(sql, + new MapSqlParameterSource("id", id), + sessionRowMapper()); + + if (session != null) { + // 세션의 수강생 정보를 함께 조회 + List studentIds = findStudentIdsBySessionId(id); + for (Long studentId : studentIds) { + session.register(studentId, null); + } + } + return session; } + @Transactional public void update(Session session) { - String sql = "update session set name = ?, start_date = ?, end_date = ?, status = ?, capacity = ?, tuition_fee = ? where id = ?"; - jdbcTemplate.update(sql, - session.getName(), - session.getPeriod().getStartDate(), - session.getPeriod().getEndDate(), - session.getStatus().name(), - session instanceof PaidSession ? ((PaidSession) session).getCapacity() : null, - session instanceof PaidSession ? ((PaidSession) session).getTuitionFee() : null, - session.getId() - ); + String sql = "update session set name = :name, start_date = :startDate, end_date = :endDate, " + + "status = :status, capacity = :capacity, tuition_fee = :tuitionFee where id = :id"; + + MapSqlParameterSource params = new MapSqlParameterSource() + .addValue("name", session.getName()) + .addValue("startDate", session.getPeriod().getStartDate()) + .addValue("endDate", session.getPeriod().getEndDate()) + .addValue("status", session.getStatus().name()) + .addValue("capacity", session instanceof PaidSession ? ((PaidSession) session).getCapacity().getValue() : null) + .addValue("tuitionFee", session instanceof PaidSession ? ((PaidSession) session).getTuitionFee().isSameAmount(0) ? 0 : null : null) + .addValue("id", session.getId()); + + namedParameterJdbcTemplate.update(sql, params); } + @Transactional public void deleteById(Long id) { - jdbcTemplate.update("delete from session where id = ?", id); + deleteAllStudentsBySessionId(id); + String sql = "delete from session where id = :id"; + namedParameterJdbcTemplate.update(sql, new MapSqlParameterSource("id", id)); } private Long insertImage(Image image) { @@ -90,6 +134,18 @@ private Long insertImage(Image image) { return insert.executeAndReturnKey(params).longValue(); } + private List findStudentIdsBySessionId(Long sessionId) { + String sql = "select student_id from session_student where session_id = :sessionId"; + return namedParameterJdbcTemplate.query(sql, + new MapSqlParameterSource("sessionId", sessionId), + (rs, rowNum) -> rs.getLong("student_id")); + } + + private void deleteAllStudentsBySessionId(Long sessionId) { + String sql = "delete from session_student where session_id = :sessionId"; + namedParameterJdbcTemplate.update(sql, new MapSqlParameterSource("sessionId", sessionId)); + } + private RowMapper sessionRowMapper() { return (rs, rowNum) -> { SessionStatus status = SessionStatus.valueOf(rs.getString("status")); diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionStudentRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionStudentRepository.java deleted file mode 100644 index 6e9416f5f..000000000 --- a/src/main/java/nextstep/courses/infrastructure/JdbcSessionStudentRepository.java +++ /dev/null @@ -1,46 +0,0 @@ -package nextstep.courses.infrastructure; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Repository -public class JdbcSessionStudentRepository { - private final JdbcTemplate jdbcTemplate; - - public JdbcSessionStudentRepository(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public void register(Long sessionId, Long studentId) { - String sql = "insert into session_student (session_id, student_id) values (?, ?)"; - jdbcTemplate.update(sql, sessionId, studentId); - } - - public void unregister(Long sessionId, Long studentId) { - String sql = "delete from session_student where session_id = ? and student_id = ?"; - jdbcTemplate.update(sql, sessionId, studentId); - } - - public List findStudentIdsBySessionId(Long sessionId) { - String sql = "select student_id from session_student where session_id = ?"; - return jdbcTemplate.query(sql, - (rs, rowNum) -> rs.getLong("student_id"), - sessionId); - } - - public boolean isRegistered(Long sessionId, Long studentId) { - String sql = "select count(*) from session_student where session_id = ? and student_id = ?"; - Integer count = jdbcTemplate.queryForObject(sql, Integer.class, sessionId, studentId); - return count != null && count > 0; - } - - public void deleteAllBySessionId(Long sessionId) { - jdbcTemplate.update("delete from session_student where session_id = ?", sessionId); - } - - public void deleteAllByStudentId(Long studentId) { - jdbcTemplate.update("delete from session_student where student_id = ?", studentId); - } -} \ No newline at end of file diff --git a/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java index 5636f6a68..82b1e374b 100644 --- a/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java +++ b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java @@ -1,6 +1,7 @@ package nextstep.courses.infrastructure; import nextstep.courses.domain.FreeSession; +import nextstep.courses.domain.PaidSession; import nextstep.courses.domain.Image; import nextstep.courses.domain.Period; import nextstep.courses.domain.Session; @@ -14,6 +15,7 @@ import org.springframework.jdbc.core.JdbcTemplate; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -28,6 +30,19 @@ public class SessionRepositoryTest { @BeforeEach void setUp() { sessionRepository = new JdbcSessionRepository(jdbcTemplate); + + // 테이블 초기화 + jdbcTemplate.update("delete from session_student"); + jdbcTemplate.update("delete from session"); + jdbcTemplate.update("delete from image"); + jdbcTemplate.update("delete from ns_user"); + jdbcTemplate.update("delete from course"); + + // 테스트용 사용자 추가 + jdbcTemplate.update("insert into ns_user (user_id, password, name, created_at) " + + "values ('test100', 'password', 'Test User', current_timestamp)"); + + // 테스트용 강의 추가 jdbcTemplate.update("insert into course (id, title, creator_id, created_at) " + "values (1, '샘플 강의', 42, current_timestamp)"); } @@ -37,36 +52,83 @@ void setUp() { void crudTest() { Long courseId = 1L; - // Create - FreeSession session = new FreeSession(null, "세션1", + // Create - Free Session + FreeSession freeSession = new FreeSession(null, "무료 세션", new Period(LocalDate.of(2025, 5, 1), LocalDate.of(2025, 5, 10)), - new Image("test.jpg", "jpeg", 100000, 600, 400), + new Image("free.jpg", "jpeg", 100000, 600, 400), SessionStatus.RECRUITING); - sessionRepository.save(session, courseId); + sessionRepository.save(freeSession, courseId); + + // Create - Paid Session + PaidSession paidSession = new PaidSession(null, "유료 세션", + new Period(LocalDate.of(2025, 6, 1), LocalDate.of(2025, 6, 10)), + new Image("paid.jpg", "jpeg", 200000, 600, 400), + SessionStatus.RECRUITING, + 30, 100000); + sessionRepository.save(paidSession, courseId); // Read (findAll) List sessions = sessionRepository.findAll(); - assertThat(sessions).hasSize(1); + assertThat(sessions).hasSize(2); + assertThat(sessions.get(0).getName()).isEqualTo("무료 세션"); + assertThat(sessions.get(1).getName()).isEqualTo("유료 세션"); // Read (findById) - Session loaded = sessionRepository.findById(sessions.get(0).getId()); - assertThat(loaded.getName()).isEqualTo("세션1"); + Session loadedFree = sessionRepository.findById(sessions.get(0).getId()); + assertThat(loadedFree.getName()).isEqualTo("무료 세션"); + assertThat(loadedFree).isInstanceOf(FreeSession.class); + + Session loadedPaid = sessionRepository.findById(sessions.get(1).getId()); + assertThat(loadedPaid.getName()).isEqualTo("유료 세션"); + assertThat(loadedPaid).isInstanceOf(PaidSession.class); + assertThat(((PaidSession) loadedPaid).getCapacity().getValue()).isEqualTo(30); + assertThat(((PaidSession) loadedPaid).getTuitionFee().isSameAmount(100000)).isTrue(); // Update Session updatedSession = new FreeSession( - loaded.getId(), - "수정된 세션", - loaded.getPeriod(), - loaded.getCoverImage(), - loaded.getStatus() + loadedFree.getId(), + "수정된 무료 세션", + loadedFree.getPeriod(), + loadedFree.getCoverImage(), + loadedFree.getStatus() ); sessionRepository.update(updatedSession); - Session updated = sessionRepository.findById(loaded.getId()); - assertThat(updated.getName()).isEqualTo("수정된 세션"); + Session updated = sessionRepository.findById(loadedFree.getId()); + assertThat(updated.getName()).isEqualTo("수정된 무료 세션"); // Delete - sessionRepository.deleteById(loaded.getId()); + sessionRepository.deleteById(loadedFree.getId()); + sessionRepository.deleteById(loadedPaid.getId()); List afterDelete = sessionRepository.findAll(); assertThat(afterDelete).isEmpty(); } + + @Test + @DisplayName("학생 등록 테스트") + void registerStudentTest() { + Long courseId = 1L; + + // 테스트용 학생 ID 조회 + Long studentId = jdbcTemplate.queryForObject( + "select id from ns_user where user_id = 'test100'", Long.class); + + // Create session + FreeSession session = new FreeSession(null, "무료 세션", + new Period(LocalDate.of(2025, 5, 1), LocalDate.of(2025, 5, 10)), + null, // 이미지는 선택사항으로 처리 + SessionStatus.RECRUITING); + sessionRepository.save(session, courseId); + + // Get session ID + List sessions = sessionRepository.findAll(); + Long sessionId = sessions.get(0).getId(); + + // Register student + sessionRepository.registerStudent(sessionId, studentId); + + // Verify registration + Session loadedSession = sessionRepository.findById(sessionId); + List registeredStudents = loadedSession.getStudentIds(); + assertThat(registeredStudents).contains(studentId); + } } diff --git a/src/test/java/nextstep/courses/infrastructure/SessionStudentRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/SessionStudentRepositoryTest.java deleted file mode 100644 index 0ed1755f9..000000000 --- a/src/test/java/nextstep/courses/infrastructure/SessionStudentRepositoryTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package nextstep.courses.infrastructure; - -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.LocalDate; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -@JdbcTest -public class SessionStudentRepositoryTest { - @Autowired - JdbcTemplate jdbcTemplate; - - JdbcSessionStudentRepository repository; - - @BeforeEach - void setUp() { - repository = new JdbcSessionStudentRepository(jdbcTemplate); - - // 테스트용 데이터 삽입 (session, ns_user) - jdbcTemplate.update("insert into course (id, title, creator_id, created_at) values (1, '강의', 1, current_timestamp)"); - jdbcTemplate.update("insert into session (id, name, type, start_date, end_date, status, course_id) " + - "values (100, '세션', 'FREE', current_date, current_date + 1, 'RECRUITING', 1)"); - jdbcTemplate.update("insert into ns_user (id, user_id, password, name, email, created_at) " + - "values (200, 'student1', 'pass', '학생1', 'student@example.com', current_timestamp)"); - } - - @Test - @DisplayName("학생 등록, 제외, 조회 테스트") - void registerStudentTest() { - Long sessionId = 100L; - Long studentId = 200L; - - repository.register(sessionId, studentId); - - List studentIds = repository.findStudentIdsBySessionId(sessionId); - assertThat(studentIds).containsExactly(studentId); - assertThat(repository.isRegistered(sessionId, studentId)).isTrue(); - - repository.unregister(sessionId, studentId); - assertThat(repository.isRegistered(sessionId, studentId)).isFalse(); - } - - @Test - @DisplayName("세션 id로 모든 학생 삭제 테스트") - void deleteAllBySessionId() { - Long sessionId = 100L; - Long student1 = 200L; - Long student2 = 201L; - - jdbcTemplate.update("insert into ns_user (id, user_id, password, name, email, created_at) " + - "values (?, ?, ?, ?, ?, current_timestamp)", - student2, "student2", "pass", "학생2", "s2@example.com"); - - repository.register(sessionId, student1); - repository.register(sessionId, student2); - assertThat(repository.findStudentIdsBySessionId(sessionId)).hasSize(2); - - repository.deleteAllBySessionId(sessionId); - assertThat(repository.findStudentIdsBySessionId(sessionId)).isEmpty(); - } - - @Test - @DisplayName("학생 id로 모든 세션 삭제 테스트") - void deleteAllByStudentId() { - Long sessionId = 100L; - Long studentId = 200L; - - repository.register(sessionId, studentId); - assertThat(repository.findStudentIdsBySessionId(sessionId)).hasSize(1); - - repository.deleteAllByStudentId(studentId); - assertThat(repository.findStudentIdsBySessionId(sessionId)).isEmpty(); - } -}