From cb4d53bb5ed91097543d45b0ef6100d107a3ef51 Mon Sep 17 00:00:00 2001 From: pizzazoa Date: Thu, 8 Jan 2026 04:00:04 +0900 Subject: [PATCH 1/5] =?UTF-8?q?test:=20=EC=98=A4=EB=9E=98=EB=90=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=97=90=20=EB=B9=8C=EB=8D=94=20?= =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CollectionCommentLikeRepositoryTest.java | 37 +++--------------- .../CollectionCommentRepositoryTest.java | 39 +++---------------- .../CollectionLikeRepositoryTest.java | 37 +++--------------- .../repository/CollectionRepositoryTest.java | 36 +++++------------ 4 files changed, 27 insertions(+), 122 deletions(-) diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionCommentLikeRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionCommentLikeRepositoryTest.java index 4666e68b..db532b7b 100644 --- a/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionCommentLikeRepositoryTest.java +++ b/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionCommentLikeRepositoryTest.java @@ -3,16 +3,16 @@ import org.devkor.apu.saerok_server.domain.collection.core.entity.*; import org.devkor.apu.saerok_server.domain.user.core.entity.User; import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; -import org.junit.jupiter.api.*; -import org.locationtech.jts.geom.*; +import org.devkor.apu.saerok_server.testsupport.builder.CollectionBuilder; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; -import java.lang.reflect.Field; -import java.time.LocalDate; import java.util.List; import java.util.Map; import java.util.Optional; @@ -27,40 +27,15 @@ class CollectionCommentLikeRepositoryTest extends AbstractPostgresContainerTest @Autowired CollectionCommentLikeRepository repo; @Autowired TestEntityManager em; - private final GeometryFactory gf = new GeometryFactory(); - private Field collUserField; - /* ------------------------------------------------------------------ * helpers * ------------------------------------------------------------------ */ - @BeforeEach - void setup() throws NoSuchFieldException { - collUserField = UserBirdCollection.class.getDeclaredField("user"); - collUserField.setAccessible(true); - } - private User newUser() { - User user = User.createUser("test+" + System.nanoTime() + "@example.com"); - em.persist(user); - em.flush(); - return user; + return new UserBuilder(em).build(); } private UserBirdCollection newCollection(User owner) { - try { - UserBirdCollection c = new UserBirdCollection(); - collUserField.set(c, owner); - - c.setAccessLevel(AccessLevelType.PUBLIC); - c.setDiscoveredDate(LocalDate.now()); - Point p = gf.createPoint(new Coordinate(126.9780, 37.5665)); - c.setLocation(p); - - em.persist(c); - return c; - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } + return new CollectionBuilder(em).owner(owner).build(); } private UserBirdCollectionComment newComment(User user, UserBirdCollection col, String content) { diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionCommentRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionCommentRepositoryTest.java index 0da8098f..1c2293c2 100644 --- a/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionCommentRepositoryTest.java +++ b/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionCommentRepositoryTest.java @@ -1,21 +1,19 @@ package org.devkor.apu.saerok_server.domain.collection.core.repository; -import org.devkor.apu.saerok_server.domain.collection.core.entity.AccessLevelType; import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollection; import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollectionComment; import org.devkor.apu.saerok_server.domain.user.core.entity.User; import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; -import org.junit.jupiter.api.*; -import org.locationtech.jts.geom.*; +import org.devkor.apu.saerok_server.testsupport.builder.CollectionBuilder; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; -import java.lang.reflect.Field; -import java.time.LocalDate; - import static org.assertj.core.api.Assertions.*; @DataJpaTest @@ -26,37 +24,12 @@ class CollectionCommentRepositoryTest extends AbstractPostgresContainerTest { @Autowired TestEntityManager em; @Autowired CollectionCommentRepository repo; - private final GeometryFactory gf = new GeometryFactory(); - private Field collUserField; - - @BeforeEach - void setup() throws NoSuchFieldException { - collUserField = UserBirdCollection.class.getDeclaredField("user"); - collUserField.setAccessible(true); - } - private User user() { - User u = User.createUser("test@example.com"); - u.setNickname("testUser"); - em.persist(u); - return u; + return new UserBuilder(em).build(); } private UserBirdCollection collection(User owner) { - try { - UserBirdCollection c = new UserBirdCollection(); - collUserField.set(c, owner); - - Point p = gf.createPoint(new Coordinate(126.9780, 37.5665)); - c.setLocation(p); - c.setDiscoveredDate(LocalDate.now()); - c.setAccessLevel(AccessLevelType.PUBLIC); - - em.persist(c); - return c; - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } + return new CollectionBuilder(em).owner(owner).build(); } diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionLikeRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionLikeRepositoryTest.java index 4076c66c..ef6fda27 100644 --- a/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionLikeRepositoryTest.java +++ b/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionLikeRepositoryTest.java @@ -1,24 +1,19 @@ package org.devkor.apu.saerok_server.domain.collection.core.repository; -import org.devkor.apu.saerok_server.domain.collection.core.entity.AccessLevelType; import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollection; import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollectionLike; import org.devkor.apu.saerok_server.domain.user.core.entity.User; import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; -import org.junit.jupiter.api.BeforeEach; +import org.devkor.apu.saerok_server.testsupport.builder.CollectionBuilder; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.Point; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; -import java.lang.reflect.Field; -import java.time.LocalDate; import java.util.List; import java.util.Optional; @@ -35,37 +30,15 @@ class CollectionLikeRepositoryTest extends AbstractPostgresContainerTest { @Autowired TestEntityManager em; - GeometryFactory gf; - Field collUserField; - - @BeforeEach - void setUp() throws NoSuchFieldException { - gf = new GeometryFactory(); - - collUserField = UserBirdCollection.class.getDeclaredField("user"); - collUserField.setAccessible(true); - } - /* ------------------------------------------------------------------ * helpers * ------------------------------------------------------------------ */ private User newUser() { - User user = User.createUser("test+" + System.nanoTime() + "@example.com"); - user.setNickname("user-" + user.hashCode()); - em.persist(user); - em.flush(); - return user; + return new UserBuilder(em).build(); } - private UserBirdCollection newCollection(User owner) throws IllegalAccessException { - UserBirdCollection c = new UserBirdCollection(); - collUserField.set(c, owner); - c.setAccessLevel(AccessLevelType.PUBLIC); - c.setDiscoveredDate(LocalDate.now()); - Point location = gf.createPoint(new Coordinate(126.9780, 37.5665)); - c.setLocation(location); - em.persist(c); - return c; + private UserBirdCollection newCollection(User owner) { + return new CollectionBuilder(em).owner(owner).build(); } private UserBirdCollectionLike newLike(User user, UserBirdCollection collection) { diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionRepositoryTest.java index 3e89c96e..e1cfcccf 100644 --- a/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionRepositoryTest.java +++ b/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionRepositoryTest.java @@ -4,7 +4,8 @@ import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollection; import org.devkor.apu.saerok_server.domain.user.core.entity.User; import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; -import org.junit.jupiter.api.BeforeEach; +import org.devkor.apu.saerok_server.testsupport.builder.CollectionBuilder; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.locationtech.jts.geom.Coordinate; @@ -16,8 +17,6 @@ import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; -import java.lang.reflect.Field; -import java.time.LocalDate; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -33,40 +32,25 @@ class CollectionRepositoryTest extends AbstractPostgresContainerTest { @Autowired TestEntityManager em; - GeometryFactory gf; - - Field collUserField; - - @BeforeEach - void setUp() throws NoSuchFieldException { - gf = new GeometryFactory(); - - collUserField = UserBirdCollection.class.getDeclaredField("user"); - collUserField.setAccessible(true); - } + private final GeometryFactory gf = new GeometryFactory(); /* ------------------------------------------------------------------ * helpers * ------------------------------------------------------------------ */ private User newUser() { - User user = User.createUser("test+" + System.nanoTime() + "@example.com"); - em.persist(user); - em.flush(); - return user; + return new UserBuilder(em).build(); } private UserBirdCollection newCollection( User owner, Point point, AccessLevelType accessLevel - ) throws IllegalAccessException { - UserBirdCollection c = new UserBirdCollection(); - collUserField.set(c, owner); - c.setLocation(point); - c.setAccessLevel(accessLevel); - c.setDiscoveredDate(LocalDate.now()); - em.persist(c); - return c; + ) { + return new CollectionBuilder(em) + .owner(owner) + .location(point) + .accessLevel(accessLevel) + .build(); } /* ------------------------------------------------------------------ From 42509ba8138cfc7ff161cb8592a62c35e1b253c8 Mon Sep 17 00:00:00 2001 From: pizzazoa Date: Thu, 8 Jan 2026 20:33:24 +0900 Subject: [PATCH 2/5] =?UTF-8?q?test:=20=EC=83=88=20=EB=A6=AC=ED=8F=AC?= =?UTF-8?q?=EC=A7=80=ED=86=A0=EB=A6=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=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 --- .../NotificationRepositoryTest.java | 227 ++++++++++++++++++ .../core/repository/UserRepositoryTest.java | 193 +++++++++++++++ 2 files changed, 420 insertions(+) create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/NotificationRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserRepositoryTest.java diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/NotificationRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/NotificationRepositoryTest.java new file mode 100644 index 00000000..5bd11f8f --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/NotificationRepositoryTest.java @@ -0,0 +1,227 @@ +package org.devkor.apu.saerok_server.domain.notification.core.repository; + +import org.devkor.apu.saerok_server.domain.notification.core.entity.Notification; +import org.devkor.apu.saerok_server.domain.notification.core.entity.NotificationType; +import org.devkor.apu.saerok_server.domain.notification.core.repository.NotificationRepository; +import org.devkor.apu.saerok_server.domain.user.core.entity.User; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(NotificationRepository.class) +@ActiveProfiles("test") +class NotificationRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired NotificationRepository repo; + + private User user() { + return new UserBuilder(em).build(); + } + + private Notification noti(User user, NotificationType type, boolean isRead) { + return Notification.builder() + .user(user) + .type(type) + .isRead(isRead) + .payload(Map.of("key", "value")) + .build(); + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("save / findById") + void save_findById() { + User u = user(); + Notification n = noti(u, NotificationType.COMMENTED_ON_COLLECTION, false); + repo.save(n); + em.flush(); em.clear(); + + Optional found = repo.findById(n.getId()); + assertThat(found).isPresent(); + assertThat(found.get().getUser().getId()).isEqualTo(u.getId()); + assertThat(found.get().getType()).isEqualTo(NotificationType.COMMENTED_ON_COLLECTION); + assertThat(found.get().getIsRead()).isFalse(); + } + + @Test @DisplayName("findById - 존재하지 않음") + void findById_notExists() { + Optional found = repo.findById(99999L); + assertThat(found).isEmpty(); + } + + @Test @DisplayName("remove") + void remove() { + User u = user(); + Notification n = noti(u, NotificationType.LIKED_ON_COLLECTION, false); + repo.save(n); + em.flush(); em.clear(); + + Notification toDelete = em.find(Notification.class, n.getId()); + repo.remove(toDelete); + em.flush(); em.clear(); + + Optional found = repo.findById(n.getId()); + assertThat(found).isEmpty(); + } + + @Test @DisplayName("findByUserId - 최신순 정렬") + void findByUserId() throws InterruptedException { + User u1 = user(); + User u2 = user(); + + Notification n1 = noti(u1, NotificationType.COMMENTED_ON_COLLECTION, false); + repo.save(n1); + em.flush(); + + Thread.sleep(10); + + Notification n2 = noti(u1, NotificationType.LIKED_ON_COLLECTION, false); + repo.save(n2); + Notification n3 = noti(u2, NotificationType.COMMENTED_ON_COLLECTION, false); + repo.save(n3); + em.flush(); em.clear(); + + List u1Notis = repo.findByUserId(u1.getId()); + List u2Notis = repo.findByUserId(u2.getId()); + + assertThat(u1Notis).hasSize(2); + assertThat(u1Notis.get(0).getId()).isEqualTo(n2.getId()); + assertThat(u1Notis.get(1).getId()).isEqualTo(n1.getId()); + assertThat(u2Notis).hasSize(1); + assertThat(u2Notis.getFirst().getId()).isEqualTo(n3.getId()); + } + + @Test @DisplayName("findByUserId - 빈 리스트") + void findByUserId_empty() { + User u = user(); + em.flush(); em.clear(); + + List notis = repo.findByUserId(u.getId()); + assertThat(notis).isEmpty(); + } + + @Test @DisplayName("countUnreadByUserId") + void countUnreadByUserId() { + User u = user(); + repo.save(noti(u, NotificationType.COMMENTED_ON_COLLECTION, false)); + repo.save(noti(u, NotificationType.LIKED_ON_COLLECTION, false)); + repo.save(noti(u, NotificationType.COMMENTED_ON_COLLECTION, true)); + em.flush(); em.clear(); + + Long unreadCount = repo.countUnreadByUserId(u.getId()); + assertThat(unreadCount).isEqualTo(2L); + } + + @Test @DisplayName("countUnreadByUserId - 모두 읽음") + void countUnreadByUserId_allRead() { + User u = user(); + repo.save(noti(u, NotificationType.COMMENTED_ON_COLLECTION, true)); + repo.save(noti(u, NotificationType.LIKED_ON_COLLECTION, true)); + em.flush(); em.clear(); + + Long unreadCount = repo.countUnreadByUserId(u.getId()); + assertThat(unreadCount).isEqualTo(0L); + } + + @Test @DisplayName("markAllAsReadByUserId") + void markAllAsReadByUserId() { + User u = user(); + repo.save(noti(u, NotificationType.COMMENTED_ON_COLLECTION, false)); + repo.save(noti(u, NotificationType.LIKED_ON_COLLECTION, false)); + repo.save(noti(u, NotificationType.REPLIED_TO_COMMENT, false)); + em.flush(); em.clear(); + + repo.markAllAsReadByUserId(u.getId()); + em.flush(); em.clear(); + + Long unreadCount = repo.countUnreadByUserId(u.getId()); + assertThat(unreadCount).isEqualTo(0L); + + List notis = repo.findByUserId(u.getId()); + assertThat(notis).allMatch(Notification::getIsRead); + } + + @Test @DisplayName("markAllAsReadByUserId - 이미 읽음") + void markAllAsReadByUserId_alreadyRead() { + User u = user(); + repo.save(noti(u, NotificationType.COMMENTED_ON_COLLECTION, true)); + em.flush(); em.clear(); + + repo.markAllAsReadByUserId(u.getId()); + em.flush(); em.clear(); + + Long unreadCount = repo.countUnreadByUserId(u.getId()); + assertThat(unreadCount).isEqualTo(0L); + } + + @Test @DisplayName("deleteByUserId") + void deleteByUserId() { + User u1 = user(); + User u2 = user(); + repo.save(noti(u1, NotificationType.COMMENTED_ON_COLLECTION, false)); + repo.save(noti(u1, NotificationType.LIKED_ON_COLLECTION, false)); + repo.save(noti(u2, NotificationType.COMMENTED_ON_COLLECTION, false)); + em.flush(); em.clear(); + + int deletedCount = repo.deleteByUserId(u1.getId()); + em.flush(); em.clear(); + + assertThat(deletedCount).isEqualTo(2); + assertThat(repo.findByUserId(u1.getId())).isEmpty(); + assertThat(repo.findByUserId(u2.getId())).hasSize(1); + } + + @Test @DisplayName("deleteByUserId - 알림 없음") + void deleteByUserId_noNotifications() { + User u = user(); + em.flush(); em.clear(); + + int deletedCount = repo.deleteByUserId(u.getId()); + assertThat(deletedCount).isEqualTo(0); + } + + @Test @DisplayName("markAsRead") + void markAsRead() { + User u = user(); + Notification n = noti(u, NotificationType.COMMENTED_ON_COLLECTION, false); + repo.save(n); + em.flush(); em.clear(); + + Notification found = repo.findById(n.getId()).orElseThrow(); + found.markAsRead(); + em.flush(); em.clear(); + + Notification updated = repo.findById(n.getId()).orElseThrow(); + assertThat(updated.getIsRead()).isTrue(); + } + + @Test @DisplayName("Notification Builder - payload 방어적 복사") + void notification_builder_defensiveCopy() { + User u = user(); + Map payload = Map.of("key1", "value1"); + + Notification n = Notification.builder() + .user(u) + .type(NotificationType.COMMENTED_ON_COLLECTION) + .payload(payload) + .build(); + repo.save(n); + em.flush(); + + assertThat(n.getPayload()).isNotSameAs(payload); + assertThat(n.getPayload()).containsEntry("key1", "value1"); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserRepositoryTest.java new file mode 100644 index 00000000..79be47cd --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserRepositoryTest.java @@ -0,0 +1,193 @@ +package org.devkor.apu.saerok_server.domain.user.core.repository; + +import org.devkor.apu.saerok_server.domain.user.core.entity.SignupStatusType; +import org.devkor.apu.saerok_server.domain.user.core.entity.User; +import org.devkor.apu.saerok_server.domain.user.core.repository.UserRepository; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(UserRepository.class) +@ActiveProfiles("test") +class UserRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired UserRepository repo; + + private User user() { + return new UserBuilder(em).build(); + } + + private User user(String email) { + return new UserBuilder(em).email(email).build(); + } + + private User user(String email, String nickname) { + return new UserBuilder(em).email(email).nickname(nickname).build(); + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("save / findById") + void save_findById() { + User u = user("test@example.com", "testUser"); + em.flush(); em.clear(); + + Optional found = repo.findById(u.getId()); + assertThat(found).isPresent(); + assertThat(found.get().getEmail()).isEqualTo("test@example.com"); + assertThat(found.get().getNickname()).isEqualTo("testUser"); + assertThat(found.get().getSignupStatus()).isEqualTo(SignupStatusType.PROFILE_REQUIRED); + } + + @Test @DisplayName("findById - 삭제된 사용자는 조회되지 않음") + void findById_notIncludeDeleted() { + User u = user("deleted@example.com"); + em.flush(); + + u.softDelete(); + em.flush(); em.clear(); + + Optional found = repo.findById(u.getId()); + assertThat(found).isEmpty(); + } + + @Test @DisplayName("findDeletedUserById - 삭제된 사용자만 조회") + void findDeletedUserById() { + User active = user("active@example.com"); + User deleted = user("deleted@example.com"); + em.flush(); + + deleted.softDelete(); + em.flush(); em.clear(); + + Optional foundDeleted = repo.findDeletedUserById(deleted.getId()); + Optional foundActive = repo.findDeletedUserById(active.getId()); + + assertThat(foundDeleted).isPresent(); + assertThat(foundDeleted.get().getEmail()).isEqualTo("deleted@example.com"); + assertThat(foundActive).isEmpty(); + } + + @Test @DisplayName("findByNickname") + void findByNickname() { + User u1 = user("user1@example.com", "uniqueNick"); + User u2 = user("user2@example.com", "anotherNick"); + em.flush(); em.clear(); + + Optional found = repo.findByNickname("uniqueNick"); + Optional notFound = repo.findByNickname("nonExistentNick"); + + assertThat(found).isPresent(); + assertThat(found.get().getEmail()).isEqualTo("user1@example.com"); + assertThat(notFound).isEmpty(); + } + + @Test @DisplayName("findByNickname - 삭제된 사용자도 조회됨") + void findByNickname_includeDeleted() { + User u = user("deleted@example.com", "deletedNick"); + em.flush(); + + u.softDelete(); + em.flush(); em.clear(); + + Optional found = repo.findByNickname("deletedNick"); + assertThat(found).isPresent(); + assertThat(found.get().getDeletedAt()).isNotNull(); + } + + @Test @DisplayName("findActiveUserIds") + void findActiveUserIds() { + User active1 = user("active1@example.com"); + active1.setSignupStatus(SignupStatusType.COMPLETED); + User active2 = user("active2@example.com"); + active2.setSignupStatus(SignupStatusType.PROFILE_REQUIRED); + User withdrawn = user("withdrawn@example.com"); + withdrawn.setSignupStatus(SignupStatusType.WITHDRAWN); + User deleted = user("deleted@example.com"); + em.flush(); + + deleted.softDelete(); + em.flush(); em.clear(); + + List activeIds = repo.findActiveUserIds(); + + assertThat(activeIds).hasSize(2); + assertThat(activeIds).contains(active1.getId(), active2.getId()); + assertThat(activeIds).doesNotContain(withdrawn.getId(), deleted.getId()); + } + + @Test @DisplayName("findActiveUserIds - 빈 리스트") + void findActiveUserIds_empty() { + User withdrawn = user("withdrawn@example.com"); + withdrawn.setSignupStatus(SignupStatusType.WITHDRAWN); + em.flush(); em.clear(); + + List activeIds = repo.findActiveUserIds(); + assertThat(activeIds).isEmpty(); + } + + @Test @DisplayName("save - 중복 닉네임은 제약조건 위반") + void save_duplicateNickname() { + User u1 = user("user1@example.com"); + u1.setNickname("duplicateNick"); + em.flush(); + + User u2 = user("user2@example.com"); + u2.setNickname("duplicateNick"); + + assertThatThrownBy(() -> em.flush()) + .isInstanceOf(Exception.class); + } + + @Test @DisplayName("anonymizeForWithdrawal") + void anonymizeForWithdrawal() { + User u = user("withdraw@example.com"); + u.setNickname("withdrawUser"); + em.flush(); + + u.anonymizeForWithdrawal(); + em.flush(); em.clear(); + + Optional found = repo.findDeletedUserById(u.getId()); + assertThat(found).isPresent(); + assertThat(found.get().getEmail()).isNull(); + assertThat(found.get().getPhone()).isNull(); + assertThat(found.get().getGender()).isNull(); + assertThat(found.get().getBirthDate()).isNull(); + assertThat(found.get().getDefaultProfileImageVariant()).isNull(); + assertThat(found.get().getSignupStatus()).isEqualTo(SignupStatusType.WITHDRAWN); + assertThat(found.get().getDeletedAt()).isNotNull(); + } + + @Test @DisplayName("restoreForRejoin") + void restoreForRejoin() { + User u = user("rejoin@example.com"); + em.flush(); + + u.anonymizeForWithdrawal(); + em.flush(); em.clear(); + + User deleted = repo.findDeletedUserById(u.getId()).orElseThrow(); + + deleted.restoreForRejoin(); + em.flush(); em.clear(); + + Optional restored = repo.findById(u.getId()); + assertThat(restored).isPresent(); + assertThat(restored.get().getDeletedAt()).isNull(); + assertThat(restored.get().getSignupStatus()).isEqualTo(SignupStatusType.PROFILE_REQUIRED); + assertThat(restored.get().getJoinedAt()).isNotNull(); + } +} From eb8f7150a585d00f3a85421a60c971bc0ad75f94 Mon Sep 17 00:00:00 2001 From: pizzazoa Date: Thu, 8 Jan 2026 20:35:00 +0900 Subject: [PATCH 3/5] =?UTF-8?q?fix(infra):=20=ED=9E=88=EC=B9=B4=EB=A6=AC?= =?UTF-8?q?=20=ED=92=80=20=EA=B4=80=EB=A0=A8=20=EC=84=A4=EC=A0=95=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 --- src/test/resources/application-test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index 08e3dcba..97f9bede 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -17,6 +17,11 @@ spring: # 마이그레이션 검증 스킵 (이미 실행된 마이그레이션 재검증 불필요) validate-on-migrate: false + datasource: + hikari: + maximum-pool-size: 10 + minimum-idle: 0 + logging: level: org.hibernate.SQL: info From dfa001f40a95f90383bd58aea3dba276ecd15f04 Mon Sep 17 00:00:00 2001 From: pizzazoa Date: Thu, 8 Jan 2026 22:23:23 +0900 Subject: [PATCH 4/5] =?UTF-8?q?test:=20=EB=A6=AC=ED=8F=AC=EC=A7=80?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=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 --- .../repository/SocialAuthRepositoryTest.java | 127 +++++++++ .../UserRefreshTokenRepositoryTest.java | 200 ++++++++++++++ .../CollectionImageRepositoryTest.java | 243 ++++++++++++++++++ .../PopularCollectionRepositoryTest.java | 93 +++++++ .../TrendingCollectionRepositoryTest.java | 109 ++++++++ .../core/repository/BirdRepositoryTest.java | 109 ++++++++ .../NotificationSettingRepositoryTest.java | 122 +++++++++ .../repository/UserDeviceRepositoryTest.java | 149 +++++++++++ .../UserProfileImageRepositoryTest.java | 105 ++++++++ .../repository/UserRoleRepositoryTest.java | 151 +++++++++++ .../permission/PermissionRepositoryTest.java | 61 +++++ .../permission/RoleRepositoryTest.java | 85 ++++++ 12 files changed, 1554 insertions(+) create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/auth/core/repository/SocialAuthRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/auth/core/repository/UserRefreshTokenRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionImageRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/PopularCollectionRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/TrendingCollectionRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/dex/bird/core/repository/BirdRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/NotificationSettingRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/UserDeviceRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserProfileImageRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserRoleRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/global/security/permission/PermissionRepositoryTest.java create mode 100644 src/test/java/org/devkor/apu/saerok_server/global/security/permission/RoleRepositoryTest.java diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/auth/core/repository/SocialAuthRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/auth/core/repository/SocialAuthRepositoryTest.java new file mode 100644 index 00000000..e6006625 --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/auth/core/repository/SocialAuthRepositoryTest.java @@ -0,0 +1,127 @@ +package org.devkor.apu.saerok_server.domain.auth.core.repository; + +import org.devkor.apu.saerok_server.domain.auth.core.entity.SocialAuth; +import org.devkor.apu.saerok_server.domain.auth.core.entity.SocialProviderType; +import org.devkor.apu.saerok_server.domain.user.core.entity.User; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(SocialAuthRepository.class) +@ActiveProfiles("test") +class SocialAuthRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired SocialAuthRepository repo; + + private User user() { + return new UserBuilder(em).build(); + } + + private void socialAuth(User user, SocialProviderType provider, String providerUserId) { + SocialAuth auth = SocialAuth.createSocialAuth(user, provider, providerUserId); + em.persist(auth); + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("save / findByProviderAndProviderUserId") + void save_findByProviderAndProviderUserId() { + User u = user(); + SocialAuth auth = SocialAuth.createSocialAuth(u, SocialProviderType.KAKAO, "kakao123"); + repo.save(auth); + em.flush(); em.clear(); + + Optional found = repo.findByProviderAndProviderUserId(SocialProviderType.KAKAO, "kakao123"); + assertThat(found).isPresent(); + assertThat(found.get().getProvider()).isEqualTo(SocialProviderType.KAKAO); + assertThat(found.get().getProviderUserId()).isEqualTo("kakao123"); + assertThat(found.get().getUser().getId()).isEqualTo(u.getId()); + } + + @Test @DisplayName("findByProviderAndProviderUserId - 존재하지 않음") + void findByProviderAndProviderUserId_notFound() { + Optional found = repo.findByProviderAndProviderUserId(SocialProviderType.KAKAO, "nonexistent"); + assertThat(found).isEmpty(); + } + + @Test @DisplayName("findByProviderAndProviderUserId - provider가 다르면 조회 안 됨") + void findByProviderAndProviderUserId_differentProvider() { + User u = user(); + socialAuth(u, SocialProviderType.KAKAO, "user123"); + em.flush(); em.clear(); + + Optional found = repo.findByProviderAndProviderUserId(SocialProviderType.APPLE, "user123"); + assertThat(found).isEmpty(); + } + + @Test @DisplayName("unique constraint - 같은 provider와 providerUserId 중복 불가") + void uniqueConstraint_providerAndProviderUserId() { + User u1 = user(); + User u2 = user(); + + socialAuth(u1, SocialProviderType.KAKAO, "duplicate123"); + em.flush(); + + SocialAuth duplicate = SocialAuth.createSocialAuth(u2, SocialProviderType.KAKAO, "duplicate123"); + em.persist(duplicate); + + assertThatThrownBy(() -> em.flush()) + .isInstanceOf(Exception.class); + } + + @Test @DisplayName("findByUserId") + void findByUserId() { + User u1 = user(); + User u2 = user(); + + socialAuth(u1, SocialProviderType.KAKAO, "kakao123"); + socialAuth(u1, SocialProviderType.APPLE, "apple123"); + socialAuth(u2, SocialProviderType.KAKAO, "kakao456"); + em.flush(); em.clear(); + + List u1Auths = repo.findByUserId(u1.getId()); + List u2Auths = repo.findByUserId(u2.getId()); + + assertThat(u1Auths).hasSize(2); + assertThat(u1Auths).extracting(SocialAuth::getProvider) + .containsExactlyInAnyOrder(SocialProviderType.KAKAO, SocialProviderType.APPLE); + assertThat(u2Auths).hasSize(1); + assertThat(u2Auths.getFirst().getProvider()).isEqualTo(SocialProviderType.KAKAO); + } + + @Test @DisplayName("findByUserId - 빈 리스트") + void findByUserId_empty() { + User u = user(); + em.flush(); em.clear(); + + List auths = repo.findByUserId(u.getId()); + assertThat(auths).isEmpty(); + } + + @Test @DisplayName("같은 유저가 여러 provider 사용 가능") + void sameUser_multipleProviders() { + User u = user(); + socialAuth(u, SocialProviderType.KAKAO, "kakao123"); + socialAuth(u, SocialProviderType.APPLE, "apple123"); + em.flush(); em.clear(); + + Optional kakao = repo.findByProviderAndProviderUserId(SocialProviderType.KAKAO, "kakao123"); + Optional apple = repo.findByProviderAndProviderUserId(SocialProviderType.APPLE, "apple123"); + + assertThat(kakao).isPresent(); + assertThat(apple).isPresent(); + assertThat(kakao.get().getUser().getId()).isEqualTo(apple.get().getUser().getId()); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/auth/core/repository/UserRefreshTokenRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/auth/core/repository/UserRefreshTokenRepositoryTest.java new file mode 100644 index 00000000..c5dba5b9 --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/auth/core/repository/UserRefreshTokenRepositoryTest.java @@ -0,0 +1,200 @@ +package org.devkor.apu.saerok_server.domain.auth.core.repository; + +import org.devkor.apu.saerok_server.domain.auth.core.entity.UserRefreshToken; +import org.devkor.apu.saerok_server.domain.user.core.entity.User; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.time.Duration; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(UserRefreshTokenRepository.class) +@ActiveProfiles("test") +class UserRefreshTokenRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired UserRefreshTokenRepository repo; + + private User user() { + return new UserBuilder(em).build(); + } + + private UserRefreshToken token(User user, String hash) { + UserRefreshToken t = UserRefreshToken.create( + user, + hash, + "Mozilla/5.0", + "127.0.0.1", + Duration.ofDays(30) + ); + em.persist(t); + return t; + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("save / findByRefreshTokenHash") + void save_findByRefreshTokenHash() { + User u = user(); + UserRefreshToken token = UserRefreshToken.create( + u, + "hash123", + "Mozilla/5.0", + "127.0.0.1", + Duration.ofDays(30) + ); + repo.save(token); + em.flush(); em.clear(); + + Optional found = repo.findByRefreshTokenHash("hash123"); + assertThat(found).isPresent(); + assertThat(found.get().getRefreshTokenHash()).isEqualTo("hash123"); + assertThat(found.get().getUser().getId()).isEqualTo(u.getId()); + assertThat(found.get().getUserAgent()).isEqualTo("Mozilla/5.0"); + assertThat(found.get().getIpAddress()).isEqualTo("127.0.0.1"); + } + + @Test @DisplayName("findByRefreshTokenHash - 존재하지 않음") + void findByRefreshTokenHash_notFound() { + Optional found = repo.findByRefreshTokenHash("nonexistent"); + assertThat(found).isEmpty(); + } + + @Test @DisplayName("unique constraint - 같은 user와 hash 중복 불가") + void uniqueConstraint_userAndHash() { + User u = user(); + token(u, "duplicateHash"); + em.flush(); + + UserRefreshToken duplicate = UserRefreshToken.create( + u, + "duplicateHash", + "Chrome", + "192.168.1.1", + Duration.ofDays(30) + ); + em.persist(duplicate); + + assertThatThrownBy(() -> em.flush()) + .isInstanceOf(Exception.class); + } + + @Test @DisplayName("deleteByUserId") + void deleteByUserId() { + User u1 = user(); + User u2 = user(); + + token(u1, "hash1"); + token(u1, "hash2"); + token(u2, "hash3"); + em.flush(); em.clear(); + + int deletedCount = repo.deleteByUserId(u1.getId()); + em.flush(); em.clear(); + + assertThat(deletedCount).isEqualTo(2); + assertThat(repo.findByRefreshTokenHash("hash1")).isEmpty(); + assertThat(repo.findByRefreshTokenHash("hash2")).isEmpty(); + assertThat(repo.findByRefreshTokenHash("hash3")).isPresent(); + } + + @Test @DisplayName("deleteByUserId - 토큰 없음") + void deleteByUserId_noTokens() { + User u = user(); + em.flush(); em.clear(); + + int deletedCount = repo.deleteByUserId(u.getId()); + assertThat(deletedCount).isEqualTo(0); + } + + @Test @DisplayName("isUsable - 정상 토큰") + void isUsable_valid() { + User u = user(); + UserRefreshToken token = token(u, "validHash"); + em.flush(); em.clear(); + + UserRefreshToken found = repo.findByRefreshTokenHash("validHash").orElseThrow(); + assertThat(found.isUsable()).isTrue(); + } + + @Test @DisplayName("isUsable - revoked된 토큰") + void isUsable_revoked() { + User u = user(); + UserRefreshToken token = token(u, "revokedHash"); + token.revoke(); + em.flush(); em.clear(); + + UserRefreshToken found = repo.findByRefreshTokenHash("revokedHash").orElseThrow(); + assertThat(found.isUsable()).isFalse(); + assertThat(found.getRevokedAt()).isNotNull(); + } + + @Test @DisplayName("isUsable - 만료된 토큰") + void isUsable_expired() { + User u = user(); + UserRefreshToken token = UserRefreshToken.create( + u, + "expiredHash", + "Mozilla/5.0", + "127.0.0.1", + Duration.ofMillis(-1000) // 이미 만료됨 + ); + em.persist(token); + em.flush(); em.clear(); + + UserRefreshToken found = repo.findByRefreshTokenHash("expiredHash").orElseThrow(); + assertThat(found.isUsable()).isFalse(); + } + + @Test @DisplayName("revoke") + void revoke() { + User u = user(); + UserRefreshToken token = token(u, "toRevoke"); + em.flush(); em.clear(); + + UserRefreshToken found = repo.findByRefreshTokenHash("toRevoke").orElseThrow(); + assertThat(found.getRevokedAt()).isNull(); + + found.revoke(); + em.flush(); em.clear(); + + UserRefreshToken revoked = repo.findByRefreshTokenHash("toRevoke").orElseThrow(); + assertThat(revoked.getRevokedAt()).isNotNull(); + assertThat(revoked.isUsable()).isFalse(); + } + + @Test @DisplayName("revoke - 이중 revoke 예외") + void revoke_alreadyRevoked() { + User u = user(); + UserRefreshToken token = token(u, "doubleRevoke"); + token.revoke(); + em.flush(); em.clear(); + + UserRefreshToken found = repo.findByRefreshTokenHash("doubleRevoke").orElseThrow(); + assertThatThrownBy(() -> found.revoke()) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("이미 revoke된 리프레시 토큰"); + } + + @Test @DisplayName("같은 유저가 여러 디바이스에서 여러 토큰 보유 가능") + void sameUser_multipleTokens() { + User u = user(); + token(u, "device1"); + token(u, "device2"); + token(u, "device3"); + em.flush(); em.clear(); + + assertThat(repo.findByRefreshTokenHash("device1")).isPresent(); + assertThat(repo.findByRefreshTokenHash("device2")).isPresent(); + assertThat(repo.findByRefreshTokenHash("device3")).isPresent(); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionImageRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionImageRepositoryTest.java new file mode 100644 index 00000000..e1ae68d9 --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/collection/core/repository/CollectionImageRepositoryTest.java @@ -0,0 +1,243 @@ +package org.devkor.apu.saerok_server.domain.collection.core.repository; + +import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollection; +import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollectionImage; +import org.devkor.apu.saerok_server.domain.user.core.entity.User; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.CollectionBuilder; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(CollectionImageRepository.class) +@ActiveProfiles("test") +class CollectionImageRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired CollectionImageRepository repo; + + private User user() { + return new UserBuilder(em).build(); + } + + private UserBirdCollection collection(User owner) { + return new CollectionBuilder(em).owner(owner).build(); + } + + private UserBirdCollectionImage image(UserBirdCollection collection, String objectKey, int orderIndex) { + UserBirdCollectionImage img = UserBirdCollectionImage.builder() + .collection(collection) + .objectKey(objectKey) + .contentType("image/jpeg") + .orderIndex(orderIndex) + .build(); + em.persist(img); + return img; + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("save / findById") + void save_findById() { + User u = user(); + UserBirdCollection c = collection(u); + UserBirdCollectionImage img = UserBirdCollectionImage.builder() + .collection(c) + .objectKey("s3://bucket/image.jpg") + .contentType("image/jpeg") + .orderIndex(0) + .build(); + Long id = repo.save(img); + em.flush(); em.clear(); + + Optional found = repo.findById(id); + assertThat(found).isPresent(); + assertThat(found.get().getObjectKey()).isEqualTo("s3://bucket/image.jpg"); + assertThat(found.get().getContentType()).isEqualTo("image/jpeg"); + assertThat(found.get().getOrderIndex()).isEqualTo(0); + assertThat(found.get().getCollection().getId()).isEqualTo(c.getId()); + } + + @Test @DisplayName("findById - 존재하지 않음") + void findById_notFound() { + Optional found = repo.findById(999L); + assertThat(found).isEmpty(); + } + + @Test @DisplayName("findObjectKeysByCollectionId") + void findObjectKeysByCollectionId() { + User u = user(); + UserBirdCollection c = collection(u); + image(c, "key1.jpg", 0); + image(c, "key2.jpg", 1); + image(c, "key3.jpg", 2); + em.flush(); em.clear(); + + List keys = repo.findObjectKeysByCollectionId(c.getId()); + assertThat(keys).hasSize(3); + assertThat(keys).containsExactlyInAnyOrder("key1.jpg", "key2.jpg", "key3.jpg"); + } + + @Test @DisplayName("findObjectKeysByCollectionId - 빈 리스트") + void findObjectKeysByCollectionId_empty() { + User u = user(); + UserBirdCollection c = collection(u); + em.flush(); em.clear(); + + List keys = repo.findObjectKeysByCollectionId(c.getId()); + assertThat(keys).isEmpty(); + } + + @Test @DisplayName("findByCollectionId") + void findByCollectionId() { + User u = user(); + UserBirdCollection c1 = collection(u); + UserBirdCollection c2 = collection(u); + + image(c1, "c1-img1.jpg", 0); + image(c1, "c1-img2.jpg", 1); + image(c2, "c2-img1.jpg", 0); + em.flush(); em.clear(); + + List c1Images = repo.findByCollectionId(c1.getId()); + List c2Images = repo.findByCollectionId(c2.getId()); + + assertThat(c1Images).hasSize(2); + assertThat(c1Images).extracting(UserBirdCollectionImage::getObjectKey) + .containsExactlyInAnyOrder("c1-img1.jpg", "c1-img2.jpg"); + assertThat(c2Images).hasSize(1); + assertThat(c2Images.getFirst().getObjectKey()).isEqualTo("c2-img1.jpg"); + } + + @Test @DisplayName("findByCollectionId - 빈 리스트") + void findByCollectionId_empty() { + User u = user(); + UserBirdCollection c = collection(u); + em.flush(); em.clear(); + + List images = repo.findByCollectionId(c.getId()); + assertThat(images).isEmpty(); + } + + @Test @DisplayName("remove") + void remove() { + User u = user(); + UserBirdCollection c = collection(u); + UserBirdCollectionImage img = image(c, "to-remove.jpg", 0); + em.flush(); em.clear(); + + UserBirdCollectionImage found = repo.findById(img.getId()).orElseThrow(); + repo.remove(found); + em.flush(); em.clear(); + + Optional removed = repo.findById(img.getId()); + assertThat(removed).isEmpty(); + } + + @Test @DisplayName("removeByCollectionId") + void removeByCollectionId() { + User u = user(); + UserBirdCollection c1 = collection(u); + UserBirdCollection c2 = collection(u); + + image(c1, "c1-img1.jpg", 0); + image(c1, "c1-img2.jpg", 1); + image(c2, "c2-img1.jpg", 0); + em.flush(); em.clear(); + + repo.removeByCollectionId(c1.getId()); + em.flush(); em.clear(); + + assertThat(repo.findByCollectionId(c1.getId())).isEmpty(); + assertThat(repo.findByCollectionId(c2.getId())).hasSize(1); + } + + @Test @DisplayName("removeByCollectionId - 이미지 없음") + void removeByCollectionId_noImages() { + User u = user(); + UserBirdCollection c = collection(u); + em.flush(); em.clear(); + + repo.removeByCollectionId(c.getId()); + em.flush(); em.clear(); + + assertThat(repo.findByCollectionId(c.getId())).isEmpty(); + } + + @Test @DisplayName("findPrimaryKeysByCollectionIds - 정상 케이스") + void findPrimaryKeysByCollectionIds() { + User u = user(); + UserBirdCollection c1 = collection(u); + UserBirdCollection c2 = collection(u); + UserBirdCollection c3 = collection(u); + + image(c1, "c1-primary.jpg", 0); + image(c1, "c1-second.jpg", 1); + image(c2, "c2-primary.jpg", 0); + image(c3, "c3-primary.jpg", 0); + em.flush(); em.clear(); + + Map primaryKeys = repo.findPrimaryKeysByCollectionIds( + List.of(c1.getId(), c2.getId(), c3.getId()) + ); + + assertThat(primaryKeys).hasSize(3); + assertThat(primaryKeys.get(c1.getId())).isEqualTo("c1-primary.jpg"); + assertThat(primaryKeys.get(c2.getId())).isEqualTo("c2-primary.jpg"); + assertThat(primaryKeys.get(c3.getId())).isEqualTo("c3-primary.jpg"); + } + + @Test @DisplayName("findPrimaryKeysByCollectionIds - 빈 리스트") + void findPrimaryKeysByCollectionIds_emptyList() { + Map primaryKeys = repo.findPrimaryKeysByCollectionIds(List.of()); + assertThat(primaryKeys).isEmpty(); + } + + @Test @DisplayName("findPrimaryKeysByCollectionIds - 이미지 없는 컬렉션 포함") + void findPrimaryKeysByCollectionIds_withNoImages() { + User u = user(); + UserBirdCollection c1 = collection(u); + UserBirdCollection c2 = collection(u); + UserBirdCollection c3 = collection(u); + + image(c1, "c1-primary.jpg", 0); + // c2는 이미지 없음 + image(c3, "c3-primary.jpg", 0); + em.flush(); em.clear(); + + Map primaryKeys = repo.findPrimaryKeysByCollectionIds( + List.of(c1.getId(), c2.getId(), c3.getId()) + ); + + assertThat(primaryKeys).hasSize(3); + assertThat(primaryKeys.get(c1.getId())).isEqualTo("c1-primary.jpg"); + assertThat(primaryKeys.get(c2.getId())).isNull(); + assertThat(primaryKeys.get(c3.getId())).isEqualTo("c3-primary.jpg"); + } + + @Test @DisplayName("findPrimaryKeysByCollectionIds - orderIndex 가장 작은 것 선택") + void findPrimaryKeysByCollectionIds_minOrderIndex() { + User u = user(); + UserBirdCollection c = collection(u); + + image(c, "third.jpg", 3); + image(c, "first.jpg", 1); + image(c, "second.jpg", 2); + em.flush(); em.clear(); + + Map primaryKeys = repo.findPrimaryKeysByCollectionIds(List.of(c.getId())); + + assertThat(primaryKeys.get(c.getId())).isEqualTo("first.jpg"); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/PopularCollectionRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/PopularCollectionRepositoryTest.java new file mode 100644 index 00000000..08cb4e8b --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/PopularCollectionRepositoryTest.java @@ -0,0 +1,93 @@ +package org.devkor.apu.saerok_server.domain.community.core.repository; + +import org.devkor.apu.saerok_server.domain.community.core.entity.PopularCollection; +import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollection; +import org.devkor.apu.saerok_server.domain.user.core.entity.User; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.CollectionBuilder; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(PopularCollectionRepository.class) +@ActiveProfiles("test") +class PopularCollectionRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired PopularCollectionRepository repo; + + private User user() { + return new UserBuilder(em).build(); + } + + private UserBirdCollection collection(User owner) { + return new CollectionBuilder(em).owner(owner).build(); + } + + private PopularCollection popular(UserBirdCollection collection, int order) { + return new PopularCollection( + collection, + 1.0, + 2.0, + 3.0, + OffsetDateTime.now(), + order + ); + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("save / existsByCollectionId") + void save_existsByCollectionId() { + User owner = user(); + UserBirdCollection collection = collection(owner); + repo.save(popular(collection, 1)); + em.flush(); em.clear(); + + assertThat(repo.existsByCollectionId(collection.getId())).isTrue(); + assertThat(repo.existsByCollectionId(999999L)).isFalse(); + } + + @Test @DisplayName("existsByCollectionIds returns map for input ids") + void existsByCollectionIds_returnsMap() { + User owner = user(); + UserBirdCollection c1 = collection(owner); + UserBirdCollection c2 = collection(owner); + UserBirdCollection c3 = collection(owner); + repo.save(popular(c1, 1)); + repo.save(popular(c3, 2)); + em.flush(); em.clear(); + + Map result = repo.existsByCollectionIds(List.of(c1.getId(), c2.getId(), c3.getId())); + + assertThat(result).containsEntry(c1.getId(), true); + assertThat(result).containsEntry(c2.getId(), false); + assertThat(result).containsEntry(c3.getId(), true); + } + + @Test @DisplayName("deleteAll") + void deleteAll() { + User owner = user(); + UserBirdCollection c1 = collection(owner); + UserBirdCollection c2 = collection(owner); + repo.saveAll(List.of(popular(c1, 1), popular(c2, 2))); + em.flush(); em.clear(); + + repo.deleteAll(); + em.flush(); em.clear(); + + assertThat(repo.existsByCollectionId(c1.getId())).isFalse(); + assertThat(repo.existsByCollectionId(c2.getId())).isFalse(); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/TrendingCollectionRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/TrendingCollectionRepositoryTest.java new file mode 100644 index 00000000..37a851e4 --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/TrendingCollectionRepositoryTest.java @@ -0,0 +1,109 @@ +package org.devkor.apu.saerok_server.domain.community.core.repository; + +import org.devkor.apu.saerok_server.domain.collection.core.entity.AccessLevelType; +import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollection; +import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollectionComment; +import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollectionLike; +import org.devkor.apu.saerok_server.domain.community.core.repository.dto.TrendingCollectionCandidate; +import org.devkor.apu.saerok_server.domain.user.core.entity.User; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.CollectionBuilder; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(TrendingCollectionRepository.class) +@ActiveProfiles("test") +class TrendingCollectionRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired TrendingCollectionRepository repo; + + private User user() { + return new UserBuilder(em).build(); + } + + private UserBirdCollection collection(User owner, AccessLevelType accessLevel) { + return new CollectionBuilder(em).owner(owner).accessLevel(accessLevel).build(); + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("findRecentPublicCollections filters by access and date") + void findRecentPublicCollections_filtersByAccessAndDate() throws InterruptedException { + User owner = user(); + UserBirdCollection oldPublic = collection(owner, AccessLevelType.PUBLIC); + em.flush(); + + OffsetDateTime createdAfter = OffsetDateTime.now(); + Thread.sleep(10); + + UserBirdCollection newPublic = collection(owner, AccessLevelType.PUBLIC); + UserBirdCollection newPrivate = collection(owner, AccessLevelType.PRIVATE); + em.flush(); em.clear(); + + List result = repo.findRecentPublicCollections(createdAfter); + + assertThat(result).hasSize(1); + assertThat(result.getFirst().collectionId()).isEqualTo(newPublic.getId()); + assertThat(result).noneMatch(c -> c.collectionId().equals(oldPublic.getId())); + assertThat(result).noneMatch(c -> c.collectionId().equals(newPrivate.getId())); + } + + @Test @DisplayName("findLikeCreatedAtByCollectionIds") + void findLikeCreatedAtByCollectionIds() { + User owner = user(); + User liker1 = user(); + User liker2 = user(); + UserBirdCollection c1 = collection(owner, AccessLevelType.PUBLIC); + UserBirdCollection c2 = collection(owner, AccessLevelType.PUBLIC); + + em.persist(new UserBirdCollectionLike(liker1, c1)); + em.persist(new UserBirdCollectionLike(liker2, c1)); + em.persist(new UserBirdCollectionLike(liker1, c2)); + em.flush(); em.clear(); + + Map> result = + repo.findLikeCreatedAtByCollectionIds(List.of(c1.getId(), c2.getId())); + + assertThat(result).containsKeys(c1.getId(), c2.getId()); + assertThat(result.get(c1.getId())).hasSize(2); + assertThat(result.get(c2.getId())).hasSize(1); + } + + @Test @DisplayName("findLastCommentAtByCollectionIds") + void findLastCommentAtByCollectionIds() throws InterruptedException { + User owner = user(); + User commenter = user(); + UserBirdCollection c1 = collection(owner, AccessLevelType.PUBLIC); + UserBirdCollection c2 = collection(owner, AccessLevelType.PUBLIC); + + UserBirdCollectionComment first = UserBirdCollectionComment.of(commenter, c1, "first"); + em.persist(first); + em.flush(); + + Thread.sleep(10); + + UserBirdCollectionComment second = UserBirdCollectionComment.of(commenter, c1, "second"); + em.persist(second); + em.persist(UserBirdCollectionComment.of(commenter, c2, "other")); + em.flush(); em.clear(); + + Map> result = + repo.findLastCommentAtByCollectionIds(List.of(c1.getId(), c2.getId())); + + assertThat(result).containsKeys(c1.getId(), c2.getId()); + assertThat(result.get(c1.getId()).get(commenter.getId())).isEqualTo(second.getCreatedAt()); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/dex/bird/core/repository/BirdRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/dex/bird/core/repository/BirdRepositoryTest.java new file mode 100644 index 00000000..6ec49a1c --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/dex/bird/core/repository/BirdRepositoryTest.java @@ -0,0 +1,109 @@ +package org.devkor.apu.saerok_server.domain.dex.bird.core.repository; + +import org.devkor.apu.saerok_server.domain.dex.bird.core.entity.Bird; +import org.devkor.apu.saerok_server.domain.dex.bird.query.dto.BirdSearchDto; +import org.devkor.apu.saerok_server.domain.dex.bird.query.dto.CmRangeDto; +import org.devkor.apu.saerok_server.domain.dex.bird.query.enums.BirdSearchSortDirType; +import org.devkor.apu.saerok_server.domain.dex.bird.query.enums.BirdSearchSortType; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.BirdBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(BirdRepository.class) +@ActiveProfiles("test") +class BirdRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired BirdRepository repo; + + private Bird newBird(String koreanName, double bodyLengthCm) { + return new BirdBuilder(em) + .korName(koreanName) + .bodyLengthCm(bodyLengthCm) + .build(); + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("findById") + void findById_returnsActiveBird() { + Bird bird = newBird("search-bird-" + System.nanoTime(), 12.0); + em.flush(); em.clear(); + + Optional found = repo.findById(bird.getId()); + assertThat(found).isPresent(); + assertThat(found.get().getId()).isEqualTo(bird.getId()); + } + + @Test @DisplayName("findById - soft delete 된 새 제외") + void findById_excludesSoftDeleted() { + Bird bird = newBird("deleted-bird-" + System.nanoTime(), 10.0); + em.flush(); + + bird.softDelete(); + em.flush(); em.clear(); + + Optional found = repo.findById(bird.getId()); + assertThat(found).isEmpty(); + } + + @Test @DisplayName("search filters by keyword") + void search_filtersByKeyword_andOrdersById() { + String keyword = "search-keyword-" + System.nanoTime(); + Bird first = newBird(keyword + "-a", 8.0); + Bird second = newBird(keyword + "-b", 15.0); + em.flush(); em.clear(); + + BirdSearchDto dto = new BirdSearchDto( + 1, + 10, + keyword, + null, + null, + null, + BirdSearchSortType.ID, + BirdSearchSortDirType.ASC + ); + + List result = repo.search(dto); + + assertThat(result).hasSize(2); + assertThat(result.getFirst().getId()).isEqualTo(first.getId()); + assertThat(result.get(1).getId()).isEqualTo(second.getId()); + } + + @Test @DisplayName("search filters by body length range") + void search_filtersByBodyLengthRange() { + String keyword = "length-keyword-" + System.nanoTime(); + Bird small = newBird(keyword + "-small", 9.0); + newBird(keyword + "-large", 25.0); + em.flush(); em.clear(); + + BirdSearchDto dto = new BirdSearchDto( + null, + null, + keyword, + null, + List.of(new CmRangeDto(5.0, 10.0)), + null, + BirdSearchSortType.ID, + BirdSearchSortDirType.ASC + ); + + List result = repo.search(dto); + + assertThat(result).hasSize(1); + assertThat(result.getFirst().getId()).isEqualTo(small.getId()); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/NotificationSettingRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/NotificationSettingRepositoryTest.java new file mode 100644 index 00000000..05638f9b --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/NotificationSettingRepositoryTest.java @@ -0,0 +1,122 @@ +package org.devkor.apu.saerok_server.domain.notification.core.repository; + +import org.devkor.apu.saerok_server.domain.notification.core.entity.NotificationSetting; +import org.devkor.apu.saerok_server.domain.notification.core.entity.NotificationType; +import org.devkor.apu.saerok_server.domain.notification.core.entity.UserDevice; +import org.devkor.apu.saerok_server.domain.user.core.entity.User; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(NotificationSettingRepository.class) +@ActiveProfiles("test") +class NotificationSettingRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired NotificationSettingRepository repo; + + private User user() { + return new UserBuilder(em).build(); + } + + private UserDevice device(User user, String deviceId) { + UserDevice userDevice = UserDevice.create(user, deviceId, "token-" + deviceId); + em.persist(userDevice); + return userDevice; + } + + private void setting(UserDevice device, NotificationType type, boolean enabled) { + NotificationSetting setting = NotificationSetting.of(device, type, enabled); + repo.save(setting); + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("findByUserDeviceId") + void findByUserDeviceId_returnsSettings() { + User user = user(); + UserDevice device = device(user, "device-1"); + UserDevice otherDevice = device(user, "device-2"); + setting(device, NotificationType.COMMENTED_ON_COLLECTION, true); + setting(device, NotificationType.LIKED_ON_COLLECTION, false); + setting(otherDevice, NotificationType.COMMENTED_ON_COLLECTION, true); + em.flush(); em.clear(); + + List result = repo.findByUserDeviceId(device.getId()); + + assertThat(result).hasSize(2); + assertThat(result).extracting(NotificationSetting::getType) + .containsExactlyInAnyOrder( + NotificationType.COMMENTED_ON_COLLECTION, + NotificationType.LIKED_ON_COLLECTION + ); + } + + @Test @DisplayName("findByUserDeviceIdAndType - 일치하는 설정만 반환") + void findByUserDeviceIdAndType_returnsMatch() { + User user = user(); + UserDevice device = device(user, "device-1"); + setting(device, NotificationType.LIKED_ON_COLLECTION, false); + em.flush(); em.clear(); + + Optional found = + repo.findByUserDeviceIdAndType(device.getId(), NotificationType.LIKED_ON_COLLECTION); + Optional missing = + repo.findByUserDeviceIdAndType(device.getId(), NotificationType.REPLIED_TO_COMMENT); + + assertThat(found).isPresent(); + assertThat(found.get().getEnabled()).isFalse(); + assertThat(missing).isEmpty(); + } + + @Test @DisplayName("findEnabledDeviceIdsByUserAndType - enabled devices만 반환") + void findEnabledDeviceIdsByUserAndType_returnsEnabled() { + User user = user(); + User otherUser = user(); + UserDevice enabledDevice = device(user, "device-enabled"); + UserDevice disabledDevice = device(user, "device-disabled"); + UserDevice otherDevice = device(otherUser, "device-other"); + setting(enabledDevice, NotificationType.COMMENTED_ON_COLLECTION, true); + setting(disabledDevice, NotificationType.COMMENTED_ON_COLLECTION, false); + setting(otherDevice, NotificationType.COMMENTED_ON_COLLECTION, true); + em.flush(); em.clear(); + + List deviceIds = + repo.findEnabledDeviceIdsByUserAndType(user.getId(), NotificationType.COMMENTED_ON_COLLECTION); + + assertThat(deviceIds).containsExactly(enabledDevice.getId()); + } + + @Test @DisplayName("deleteByUserId") + void deleteByUserId_removesSettings() { + User user = user(); + UserDevice device1 = device(user, "device-1"); + UserDevice device2 = device(user, "device-2"); + setting(device1, NotificationType.LIKED_ON_COLLECTION, true); + setting(device2, NotificationType.COMMENTED_ON_COLLECTION, true); + + User otherUser = user(); + UserDevice otherDevice = device(otherUser, "device-3"); + setting(otherDevice, NotificationType.LIKED_ON_COLLECTION, true); + em.flush(); em.clear(); + + int deleted = repo.deleteByUserId(user.getId()); + em.flush(); em.clear(); + + assertThat(deleted).isEqualTo(2); + assertThat(repo.findByUserDeviceId(device1.getId())).isEmpty(); + assertThat(repo.findByUserDeviceId(device2.getId())).isEmpty(); + assertThat(repo.findByUserDeviceId(otherDevice.getId())).hasSize(1); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/UserDeviceRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/UserDeviceRepositoryTest.java new file mode 100644 index 00000000..fbd53bdd --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/notification/core/repository/UserDeviceRepositoryTest.java @@ -0,0 +1,149 @@ +package org.devkor.apu.saerok_server.domain.notification.core.repository; + +import org.devkor.apu.saerok_server.domain.notification.core.entity.UserDevice; +import org.devkor.apu.saerok_server.domain.user.core.entity.User; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(UserDeviceRepository.class) +@ActiveProfiles("test") +class UserDeviceRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired UserDeviceRepository repo; + + private User user() { + return new UserBuilder(em).build(); + } + + private UserDevice device(User user, String deviceId, String token) { + UserDevice userDevice = UserDevice.create(user, deviceId, token); + repo.save(userDevice); + return userDevice; + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("save and findById") + void save_and_findById() { + User user = user(); + UserDevice device = device(user, "device-1", "token-1"); + repo.flush(); em.clear(); + + Optional found = repo.findById(device.getId()); + + assertThat(found).isPresent(); + assertThat(found.get().getDeviceId()).isEqualTo("device-1"); + assertThat(found.get().getToken()).isEqualTo("token-1"); + } + + @Test @DisplayName("findByUserIdAndDeviceId") + void findByUserIdAndDeviceId_returnsMatch() { + User user = user(); + device(user, "device-1", "token-1"); + repo.flush(); em.clear(); + + Optional found = repo.findByUserIdAndDeviceId(user.getId(), "device-1"); + Optional missing = repo.findByUserIdAndDeviceId(user.getId(), "device-2"); + + assertThat(found).isPresent(); + assertThat(missing).isEmpty(); + } + + @Test @DisplayName("deleteByUserIdAndDeviceId") + void deleteByUserIdAndDeviceId_removesDevice() { + User user = user(); + device(user, "device-1", "token-1"); + repo.flush(); em.clear(); + + repo.deleteByUserIdAndDeviceId(user.getId(), "device-1"); + repo.flush(); em.clear(); + + Optional found = repo.findByUserIdAndDeviceId(user.getId(), "device-1"); + assertThat(found).isEmpty(); + } + + @Test @DisplayName("deleteByToken") + void deleteByToken_removesDevice() { + User user = user(); + device(user, "device-1", "token-1"); + device(user, "device-2", "token-2"); + repo.flush(); em.clear(); + + repo.deleteByToken("token-1"); + repo.flush(); em.clear(); + + List devices = repo.findAllByUserId(user.getId()); + assertThat(devices).hasSize(1); + assertThat(devices.getFirst().getToken()).isEqualTo("token-2"); + } + + @Test @DisplayName("deleteByUserId") + void deleteByUserId_removesAllDevices() { + User user1 = user(); + User user2 = user(); + device(user1, "device-1", "token-1"); + device(user1, "device-2", "token-2"); + device(user2, "device-3", "token-3"); + repo.flush(); em.clear(); + + int deleted = repo.deleteByUserId(user1.getId()); + repo.flush(); em.clear(); + + assertThat(deleted).isEqualTo(2); + assertThat(repo.findAllByUserId(user1.getId())).isEmpty(); + assertThat(repo.findAllByUserId(user2.getId())).hasSize(1); + } + + @Test @DisplayName("findAllByUserId") + void findAllByUserId_returnsDevices() { + User user = user(); + UserDevice first = device(user, "device-1", "token-1"); + UserDevice second = device(user, "device-2", "token-2"); + repo.flush(); em.clear(); + + List devices = repo.findAllByUserId(user.getId()); + + assertThat(devices).hasSize(2); + assertThat(devices).extracting(UserDevice::getId) + .containsExactlyInAnyOrder(first.getId(), second.getId()); + } + + @Test @DisplayName("findTokensByUserDeviceIds") + void findTokensByUserDeviceIds_returnsTokens() { + User user = user(); + UserDevice first = device(user, "device-1", "token-1"); + UserDevice second = device(user, "device-2", "token-2"); + repo.flush(); em.clear(); + + List tokens = repo.findTokensByUserDeviceIds(List.of(first.getId(), second.getId())); + List emptyTokens = repo.findTokensByUserDeviceIds(List.of()); + + assertThat(tokens).containsExactlyInAnyOrder("token-1", "token-2"); + assertThat(emptyTokens).isEmpty(); + } + + @Test @DisplayName("findTokensByUserId") + void findTokensByUserId_returnsTokens() { + User user = user(); + device(user, "device-1", "token-1"); + device(user, "device-2", "token-2"); + repo.flush(); em.clear(); + + List tokens = repo.findTokensByUserId(user.getId()); + + assertThat(tokens).containsExactlyInAnyOrder("token-1", "token-2"); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserProfileImageRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserProfileImageRepositoryTest.java new file mode 100644 index 00000000..4add3fd0 --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserProfileImageRepositoryTest.java @@ -0,0 +1,105 @@ +package org.devkor.apu.saerok_server.domain.user.core.repository; + +import org.devkor.apu.saerok_server.domain.user.core.entity.User; +import org.devkor.apu.saerok_server.domain.user.core.entity.UserProfileImage; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(UserProfileImageRepository.class) +@ActiveProfiles("test") +class UserProfileImageRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired UserProfileImageRepository repo; + + private User user() { + return new UserBuilder(em).build(); + } + + private UserProfileImage image(User user, String objectKey) { + UserProfileImage img = UserProfileImage.of(user, objectKey, "image/jpeg"); + repo.save(img); + return img; + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("save / findByUserId") + void save_findByUserId() { + User user = user(); + UserProfileImage img = image(user, "profiles/test-1.jpg"); + em.flush(); em.clear(); + + Optional found = repo.findByUserId(user.getId()); + + assertThat(found).isPresent(); + assertThat(found.get().getId()).isEqualTo(img.getId()); + assertThat(found.get().getObjectKey()).isEqualTo("profiles/test-1.jpg"); + } + + @Test @DisplayName("findObjectKeyByUserId") + void findObjectKeyByUserId() { + User user = user(); + User missingUser = user(); + image(user, "profiles/test-2.jpg"); + em.flush(); em.clear(); + + Optional key = repo.findObjectKeyByUserId(user.getId()); + Optional missing = repo.findObjectKeyByUserId(missingUser.getId()); + + assertThat(key).contains("profiles/test-2.jpg"); + assertThat(missing).isEmpty(); + } + + @Test @DisplayName("findObjectKeysByUserIds maps missing users to null") + void findObjectKeysByUserIds_mapsMissingToNull() { + User user1 = user(); + User user2 = user(); + User user3 = user(); + image(user1, "profiles/user1.jpg"); + image(user3, "profiles/user3.jpg"); + em.flush(); em.clear(); + + Map result = repo.findObjectKeysByUserIds( + List.of(user1.getId(), user2.getId(), user3.getId()) + ); + + assertThat(result).hasSize(3); + assertThat(result.get(user1.getId())).isEqualTo("profiles/user1.jpg"); + assertThat(result.get(user2.getId())).isNull(); + assertThat(result.get(user3.getId())).isEqualTo("profiles/user3.jpg"); + } + + @Test @DisplayName("findObjectKeysByUserIds - 입력이 empty") + void findObjectKeysByUserIds_emptyInput() { + Map result = repo.findObjectKeysByUserIds(List.of()); + assertThat(result).isEmpty(); + } + + @Test @DisplayName("remove") + void remove() { + User user = user(); + UserProfileImage img = image(user, "profiles/remove.jpg"); + em.flush(); em.clear(); + + UserProfileImage toDelete = em.find(UserProfileImage.class, img.getId()); + repo.remove(toDelete); + em.flush(); em.clear(); + + Optional found = repo.findByUserId(user.getId()); + assertThat(found).isEmpty(); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserRoleRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserRoleRepositoryTest.java new file mode 100644 index 00000000..6bc6ceed --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/domain/user/core/repository/UserRoleRepositoryTest.java @@ -0,0 +1,151 @@ +package org.devkor.apu.saerok_server.domain.user.core.repository; + +import org.devkor.apu.saerok_server.domain.user.core.entity.User; +import org.devkor.apu.saerok_server.domain.user.core.entity.UserRole; +import org.devkor.apu.saerok_server.global.security.permission.Role; +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(UserRoleRepository.class) +@ActiveProfiles("test") +class UserRoleRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired UserRoleRepository repo; + + private User user() { + return new UserBuilder(em).build(); + } + + private Role role(String code) { + Role role = Role.custom(code, code + " NAME", code + " DESC"); + em.persist(role); + return role; + } + + private UserRole userRole(User user, Role role) { + UserRole userRole = UserRole.createUserRole(user, role); + repo.save(userRole); + return userRole; + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("findByUser") + void findByUser_ordersByRoleCode() { + User user = user(); + Role roleB = role("ROLE_B"); + Role roleA = role("ROLE_A"); + userRole(user, roleB); + userRole(user, roleA); + em.flush(); em.clear(); + + User managed = em.find(User.class, user.getId()); + List roles = repo.findByUser(managed); + + assertThat(roles).hasSize(2); + assertThat(roles.get(0).getRole().getCode()).isEqualTo("ROLE_A"); + assertThat(roles.get(1).getRole().getCode()).isEqualTo("ROLE_B"); + } + + @Test @DisplayName("existsByRole / existsByUserIdAndRoleCode") + void existsByRole_and_existsByUserIdAndRoleCode() { + User user = user(); + Role roleA = role("ROLE_EXISTS_A"); + Role roleB = role("ROLE_EXISTS_B"); + userRole(user, roleA); + em.flush(); em.clear(); + + Role managedRoleA = em.find(Role.class, roleA.getId()); + Role managedRoleB = em.find(Role.class, roleB.getId()); + + assertThat(repo.existsByRole(managedRoleA)).isTrue(); + assertThat(repo.existsByRole(managedRoleB)).isFalse(); + assertThat(repo.existsByUserIdAndRoleCode(user.getId(), "ROLE_EXISTS_A")).isTrue(); + assertThat(repo.existsByUserIdAndRoleCode(user.getId(), "ROLE_EXISTS_B")).isFalse(); + } + + @Test @DisplayName("findByUserIdAndRoleCode") + void findByUserIdAndRoleCode() { + User user = user(); + Role role = role("ROLE_FIND"); + userRole(user, role); + em.flush(); em.clear(); + + Optional found = repo.findByUserIdAndRoleCode(user.getId(), "ROLE_FIND"); + Optional missing = repo.findByUserIdAndRoleCode(user.getId(), "ROLE_MISSING"); + + assertThat(found).isPresent(); + assertThat(found.get().getUser().getId()).isEqualTo(user.getId()); + assertThat(found.get().getRole().getCode()).isEqualTo("ROLE_FIND"); + assertThat(missing).isEmpty(); + } + + @Test @DisplayName("deleteByUserId") + void deleteByUserId_removesAll() { + User user = user(); + Role roleA = role("ROLE_DELETE_A"); + Role roleB = role("ROLE_DELETE_B"); + userRole(user, roleA); + userRole(user, roleB); + + User other = user(); + Role otherRole = role("ROLE_OTHER"); + userRole(other, otherRole); + em.flush(); em.clear(); + + int deleted = repo.deleteByUserId(user.getId()); + em.flush(); em.clear(); + + assertThat(deleted).isEqualTo(2); + assertThat(repo.findByUserIdAndRoleCode(user.getId(), "ROLE_DELETE_A")).isEmpty(); + assertThat(repo.findByUserIdAndRoleCode(user.getId(), "ROLE_DELETE_B")).isEmpty(); + assertThat(repo.findByUserIdAndRoleCode(other.getId(), "ROLE_OTHER")).isPresent(); + } + + @Test @DisplayName("delete") + void delete_removesSingleRole() { + User user = user(); + Role role = role("ROLE_DELETE_ONE"); + UserRole userRole = userRole(user, role); + em.flush(); em.clear(); + + UserRole managed = em.find(UserRole.class, userRole.getId()); + repo.delete(managed); + em.flush(); em.clear(); + + Optional found = repo.findByUserIdAndRoleCode(user.getId(), "ROLE_DELETE_ONE"); + assertThat(found).isEmpty(); + } + + @Test @DisplayName("findUserIdsByRoleCode - 삭제된 유저 제외") + void findUserIdsByRoleCode_excludesDeletedUsers() { + Role role = role("ROLE_ACTIVE_ONLY"); + User active1 = user(); + User active2 = user(); + User deleted = user(); + userRole(active1, role); + userRole(active2, role); + userRole(deleted, role); + em.flush(); + + deleted.softDelete(); + em.flush(); em.clear(); + + List userIds = repo.findUserIdsByRoleCode("ROLE_ACTIVE_ONLY"); + + assertThat(userIds).containsExactly(active1.getId(), active2.getId()); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/global/security/permission/PermissionRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/global/security/permission/PermissionRepositoryTest.java new file mode 100644 index 00000000..535a175e --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/global/security/permission/PermissionRepositoryTest.java @@ -0,0 +1,61 @@ +package org.devkor.apu.saerok_server.global.security.permission; + +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(PermissionRepository.class) +@ActiveProfiles("test") +class PermissionRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired PermissionRepository repo; + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("findByKey") + void findByKey() { + Optional found = repo.findByKey(PermissionKey.ADMIN_LOGIN); + assertThat(found).isPresent(); + assertThat(found.get().getKey()).isEqualTo(PermissionKey.ADMIN_LOGIN); + } + + @Test @DisplayName("findAll - key asc") + void findAll_ordersByKey() { + List results = repo.findAll(); + + assertThat(results).isNotEmpty(); + for (int i = 1; i < results.size(); i++) { + String prev = results.get(i - 1).getKey().name(); + String curr = results.get(i).getKey().name(); + assertThat(prev.compareTo(curr)).isLessThanOrEqualTo(0); + } + } + + @Test @DisplayName("findByKeys returns subset") + void findByKeys_returnsSubset() { + List results = repo.findByKeys(List.of( + PermissionKey.ADMIN_LOGIN, + PermissionKey.ADMIN_AD_READ + )); + + assertThat(results).extracting(Permission::getKey) + .containsExactly(PermissionKey.ADMIN_AD_READ, PermissionKey.ADMIN_LOGIN); + } + + @Test @DisplayName("findByKeys - empty input") + void findByKeys_emptyInput() { + List results = repo.findByKeys(List.of()); + assertThat(results).isEmpty(); + } +} diff --git a/src/test/java/org/devkor/apu/saerok_server/global/security/permission/RoleRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/global/security/permission/RoleRepositoryTest.java new file mode 100644 index 00000000..5cbf0e89 --- /dev/null +++ b/src/test/java/org/devkor/apu/saerok_server/global/security/permission/RoleRepositoryTest.java @@ -0,0 +1,85 @@ +package org.devkor.apu.saerok_server.global.security.permission; + +import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; +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.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@DataJpaTest +@Import(RoleRepository.class) +@ActiveProfiles("test") +class RoleRepositoryTest extends AbstractPostgresContainerTest { + + @Autowired TestEntityManager em; + @Autowired RoleRepository repo; + + private Role role(String code) { + return Role.custom(code, code + " NAME", code + " DESC"); + } + + /* ------------------------------------------------------------------ */ + @Test @DisplayName("save / findByCode") + void save_findByCode() { + Role role = role("ROLE_TEST_" + System.nanoTime()); + repo.save(role); + em.flush(); em.clear(); + + Optional found = repo.findByCode(role.getCode()); + + assertThat(found).isPresent(); + assertThat(found.get().getDisplayName()).isEqualTo(role.getCode() + " NAME"); + assertThat(found.get().getDescription()).isEqualTo(role.getCode() + " DESC"); + assertThat(found.get().isBuiltin()).isFalse(); + } + + @Test @DisplayName("findAll") + void findAll_ordersByCode() { + String suffix = String.valueOf(System.nanoTime()); + Role roleA = role("ZZ_TEST_ROLE_A_" + suffix); + Role roleB = role("ZZ_TEST_ROLE_B_" + suffix); + repo.save(roleB); + repo.save(roleA); + em.flush(); em.clear(); + + List roles = repo.findAll(); + + int indexA = indexOfCode(roles, roleA.getCode()); + int indexB = indexOfCode(roles, roleB.getCode()); + + assertThat(indexA).isNotNegative(); + assertThat(indexB).isNotNegative(); + assertThat(indexA).isLessThan(indexB); + } + + @Test @DisplayName("delete") + void delete() { + Role role = role("ROLE_DELETE_" + System.nanoTime()); + repo.save(role); + em.flush(); em.clear(); + + Role managed = repo.findByCode(role.getCode()).orElseThrow(); + repo.delete(managed); + em.flush(); em.clear(); + + Optional found = repo.findByCode(role.getCode()); + assertThat(found).isEmpty(); + } + + private int indexOfCode(List roles, String code) { + for (int i = 0; i < roles.size(); i++) { + if (code.equals(roles.get(i).getCode())) { + return i; + } + } + return -1; + } +} From 088d3a9d9fb958fa97cb9c4fb040b9de97239c81 Mon Sep 17 00:00:00 2001 From: pizzazoa Date: Thu, 8 Jan 2026 22:42:44 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20=EB=AC=B8=EC=A0=9C=EA=B0=80=20?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TrendingCollectionRepositoryTest.java | 109 ------------------ 1 file changed, 109 deletions(-) delete mode 100644 src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/TrendingCollectionRepositoryTest.java diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/TrendingCollectionRepositoryTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/TrendingCollectionRepositoryTest.java deleted file mode 100644 index 37a851e4..00000000 --- a/src/test/java/org/devkor/apu/saerok_server/domain/community/core/repository/TrendingCollectionRepositoryTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.devkor.apu.saerok_server.domain.community.core.repository; - -import org.devkor.apu.saerok_server.domain.collection.core.entity.AccessLevelType; -import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollection; -import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollectionComment; -import org.devkor.apu.saerok_server.domain.collection.core.entity.UserBirdCollectionLike; -import org.devkor.apu.saerok_server.domain.community.core.repository.dto.TrendingCollectionCandidate; -import org.devkor.apu.saerok_server.domain.user.core.entity.User; -import org.devkor.apu.saerok_server.testsupport.AbstractPostgresContainerTest; -import org.devkor.apu.saerok_server.testsupport.builder.CollectionBuilder; -import org.devkor.apu.saerok_server.testsupport.builder.UserBuilder; -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.orm.jpa.DataJpaTest; -import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.ActiveProfiles; - -import java.time.OffsetDateTime; -import java.util.List; -import java.util.Map; - -import static org.assertj.core.api.Assertions.*; - -@DataJpaTest -@Import(TrendingCollectionRepository.class) -@ActiveProfiles("test") -class TrendingCollectionRepositoryTest extends AbstractPostgresContainerTest { - - @Autowired TestEntityManager em; - @Autowired TrendingCollectionRepository repo; - - private User user() { - return new UserBuilder(em).build(); - } - - private UserBirdCollection collection(User owner, AccessLevelType accessLevel) { - return new CollectionBuilder(em).owner(owner).accessLevel(accessLevel).build(); - } - - /* ------------------------------------------------------------------ */ - @Test @DisplayName("findRecentPublicCollections filters by access and date") - void findRecentPublicCollections_filtersByAccessAndDate() throws InterruptedException { - User owner = user(); - UserBirdCollection oldPublic = collection(owner, AccessLevelType.PUBLIC); - em.flush(); - - OffsetDateTime createdAfter = OffsetDateTime.now(); - Thread.sleep(10); - - UserBirdCollection newPublic = collection(owner, AccessLevelType.PUBLIC); - UserBirdCollection newPrivate = collection(owner, AccessLevelType.PRIVATE); - em.flush(); em.clear(); - - List result = repo.findRecentPublicCollections(createdAfter); - - assertThat(result).hasSize(1); - assertThat(result.getFirst().collectionId()).isEqualTo(newPublic.getId()); - assertThat(result).noneMatch(c -> c.collectionId().equals(oldPublic.getId())); - assertThat(result).noneMatch(c -> c.collectionId().equals(newPrivate.getId())); - } - - @Test @DisplayName("findLikeCreatedAtByCollectionIds") - void findLikeCreatedAtByCollectionIds() { - User owner = user(); - User liker1 = user(); - User liker2 = user(); - UserBirdCollection c1 = collection(owner, AccessLevelType.PUBLIC); - UserBirdCollection c2 = collection(owner, AccessLevelType.PUBLIC); - - em.persist(new UserBirdCollectionLike(liker1, c1)); - em.persist(new UserBirdCollectionLike(liker2, c1)); - em.persist(new UserBirdCollectionLike(liker1, c2)); - em.flush(); em.clear(); - - Map> result = - repo.findLikeCreatedAtByCollectionIds(List.of(c1.getId(), c2.getId())); - - assertThat(result).containsKeys(c1.getId(), c2.getId()); - assertThat(result.get(c1.getId())).hasSize(2); - assertThat(result.get(c2.getId())).hasSize(1); - } - - @Test @DisplayName("findLastCommentAtByCollectionIds") - void findLastCommentAtByCollectionIds() throws InterruptedException { - User owner = user(); - User commenter = user(); - UserBirdCollection c1 = collection(owner, AccessLevelType.PUBLIC); - UserBirdCollection c2 = collection(owner, AccessLevelType.PUBLIC); - - UserBirdCollectionComment first = UserBirdCollectionComment.of(commenter, c1, "first"); - em.persist(first); - em.flush(); - - Thread.sleep(10); - - UserBirdCollectionComment second = UserBirdCollectionComment.of(commenter, c1, "second"); - em.persist(second); - em.persist(UserBirdCollectionComment.of(commenter, c2, "other")); - em.flush(); em.clear(); - - Map> result = - repo.findLastCommentAtByCollectionIds(List.of(c1.getId(), c2.getId())); - - assertThat(result).containsKeys(c1.getId(), c2.getId()); - assertThat(result.get(c1.getId()).get(commenter.getId())).isEqualTo(second.getCreatedAt()); - } -}