Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 8 additions & 24 deletions backend/api/src/main/java/com/yat2/episode/episode/EpisodeId.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,25 @@

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Objects;
import java.util.UUID;

@Getter
@Setter
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
@Embeddable
public class EpisodeId implements Serializable {

@Column(name = "node_id", columnDefinition = "BINARY(16)")
@Column(name = "node_id", columnDefinition = "BINARY(16)", nullable = false)
private UUID nodeId;

@Column(name = "user_id")
@Column(name = "user_id", nullable = false)
private Long userId;

public EpisodeId() {}

public EpisodeId(UUID nodeId, Long userId) {
this.nodeId = nodeId;
this.userId = userId;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof EpisodeId that)) return false;
return Objects.equals(nodeId, that.nodeId) && Objects.equals(userId, that.userId);
}

@Override
public int hashCode() {
return Objects.hash(nodeId, userId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,6 @@

@Repository
public interface EpisodeRepository extends JpaRepository<Episode, UUID> {

@Query(
"""
SELECT e
FROM Episode e
JOIN EpisodeStar s ON s.id.nodeId = e.id
WHERE e.mindmapId = :mindmapId
AND s.id.userId = :userId
"""
)
List<Episode> findEpisodesByMindmapIdAndUserId(
@Param("mindmapId") UUID mindmapId,
@Param("userId") long userId
);

@Query("SELECT e.id FROM Episode e WHERE e.mindmapId = :mindmapId")
List<UUID> findNodeIdsByMindmapId(
@Param("mindmapId") UUID mindmapId
Expand All @@ -52,13 +37,18 @@ List<EpisodeDetail> findDetailsByMindmapIdAndUserId(
);

@Query(
"""
SELECT DISTINCT ctId
FROM Episode e
JOIN EpisodeStar s ON e.id = s.id.nodeId
JOIN s.competencyTypeIds ctId
WHERE e.mindmapId = :mindmapId
"""
value = """
SELECT DISTINCT jt.ct_id
FROM episodes e
JOIN episode_stars es ON es.node_id = e.node_id
JOIN JSON_TABLE(
es.competency_type_ids,
'$[*]' COLUMNS (
ct_id INT PATH '$'
)
) jt
WHERE e.mindmap_id = :mindmapId
""", nativeQuery = true
)
List<Integer> findCompetencyTypesByMindmapId(
@Param("mindmapId") UUID mindmapId
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package com.yat2.episode.episode;

import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;
import org.springframework.test.context.ActiveProfiles;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;

import com.yat2.episode.episode.dto.EpisodeDetail;
import com.yat2.episode.mindmap.Mindmap;
import com.yat2.episode.utils.AbstractRepositoryTest;

import static com.yat2.episode.utils.TestEntityFactory.createEpisode;
import static com.yat2.episode.utils.TestEntityFactory.createEpisodeStar;
import static com.yat2.episode.utils.TestEntityFactory.createMindmap;
import static com.yat2.episode.utils.TestEntityFactory.createUser;
import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
@ActiveProfiles("test")
@DisplayName("EpisodeRepository 통합 테스트")
class EpisodeRepositoryTest extends AbstractRepositoryTest {

@Autowired
EpisodeRepository episodeRepository;
@Autowired
EntityManager em;

@Test
@DisplayName("mindmapId로 nodeId 목록 조회")
void findNodeIdsByMindmapId() {
Mindmap m1 = createMindmap("mm-1");
Mindmap mOther = createMindmap("mm-other");
em.persist(m1);
em.persist(mOther);

UUID node1 = UUID.randomUUID();
UUID node2 = UUID.randomUUID();

em.persist(createEpisode(node1, m1.getId(), "c1"));
em.persist(createEpisode(node2, m1.getId(), "c2"));
em.persist(createEpisode(UUID.randomUUID(), mOther.getId(), "other"));

flushAndClear();

List<UUID> ids = episodeRepository.findNodeIdsByMindmapId(m1.getId());

assertThat(ids).containsExactlyInAnyOrder(node1, node2);
}

@Test
@DisplayName("상세 리스트 조회")
void findDetailsByMindmapIdAndUserId() {
Mindmap m1 = createMindmap("mm-1");
em.persist(m1);

long userId = 100L;
em.persist(createUser(userId));

UUID node1 = UUID.randomUUID();
UUID node2 = UUID.randomUUID();

em.persist(createEpisode(node1, m1.getId(), "content1"));
em.persist(createEpisode(node2, m1.getId(), "content2"));

em.persist(createEpisodeStar(node1, userId, Set.of(1, 2)));
em.persist(createEpisodeStar(node2, userId, Set.of(2, 3)));

flushAndClear();

List<EpisodeDetail> details = episodeRepository.findDetailsByMindmapIdAndUserId(m1.getId(), userId);

assertThat(details).hasSize(2);
assertThat(details).extracting(EpisodeDetail::nodeId).containsExactlyInAnyOrder(node1, node2);
}

@Test
@DisplayName("에피소드 상세 조회")
void findDetail() {
Mindmap m1 = createMindmap("mm-1");
em.persist(m1);

long userId = 100L;
em.persist(createUser(userId));

UUID node = UUID.randomUUID();
em.persist(createEpisode(node, m1.getId(), "content"));
em.persist(createEpisodeStar(node, userId, Set.of(7, 8)));

flushAndClear();

Optional<EpisodeDetail> opt = episodeRepository.findDetail(node, userId);

assertThat(opt).isPresent();
assertThat(opt.get().nodeId()).isEqualTo(node);
assertThat(opt.get().competencyTypeIds()).containsExactlyInAnyOrder(7, 8);
}

@Test
@DisplayName("Mindmap에 속한 competencyTypeIds 중복 제거 조회")
void findCompetencyTypesByMindmapId() {
Mindmap m1 = createMindmap("mm-1");
em.persist(m1);

long userA = 100L;
long userB = 200L;
em.persist(createUser(userA));
em.persist(createUser(userB));

UUID node1 = UUID.randomUUID();
UUID node2 = UUID.randomUUID();

em.persist(createEpisode(node1, m1.getId(), "c1"));
em.persist(createEpisode(node2, m1.getId(), "c2"));

em.persist(createEpisodeStar(node1, userA, Set.of(1, 2, 3)));
em.persist(createEpisodeStar(node2, userB, Set.of(2, 3, 4)));

flushAndClear();

List<Integer> ids = episodeRepository.findCompetencyTypesByMindmapId(m1.getId());

assertThat(ids).containsExactlyInAnyOrder(1, 2, 3, 4);
}

private void flushAndClear() {
em.flush();
em.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.util.ReflectionTestUtils;

import java.time.LocalDateTime;
import java.util.List;
Expand All @@ -16,36 +15,35 @@
import com.yat2.episode.user.UserRepository;
import com.yat2.episode.utils.AbstractRepositoryTest;

import static com.yat2.episode.utils.TestEntityFactory.createEntity;
import static com.yat2.episode.utils.TestEntityFactory.createMindmap;
import static com.yat2.episode.utils.TestEntityFactory.createParticipant;
import static com.yat2.episode.utils.TestEntityFactory.createUser;
import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
@ActiveProfiles("test")
@DisplayName("MindmapRepository 통합 테스트")
class MindmapRepositoryTest extends AbstractRepositoryTest {
@Autowired
private MindmapRepository mindmapRepository;

@Autowired
private UserRepository userRepository;

MindmapRepository mindmapRepository;
@Autowired
private MindmapParticipantRepository participantRepository;
UserRepository userRepository;
@Autowired
MindmapParticipantRepository participantRepository;

@Test
@DisplayName("사용자 ID로 참여 중인 마인드맵 목록을 최신순으로 조회한다")
void findByUserIdOrderByCreatedDesc_Success() {
User user = User.newUser(12345L, "테스트유저");
User user = createUser(12345L);
userRepository.save(user);

Mindmap m1 = createMindmap("먼저 만든 마인드맵", false, LocalDateTime.now().minusSeconds(10));
Mindmap m2 = createMindmap("나중에 만든 마인드맵", false, LocalDateTime.now());
mindmapRepository.save(m1);
mindmapRepository.save(m2);
mindmapRepository.saveAll(List.of(m1, m2));

saveParticipant(user, m1);
saveParticipant(user, m2);
participantRepository.save(createParticipant(m1, user));
participantRepository.save(createParticipant(m2, user));

List<Mindmap> result = mindmapRepository.findByUserIdOrderByCreatedDesc(12345L);

Expand All @@ -56,16 +54,17 @@ void findByUserIdOrderByCreatedDesc_Success() {
@Test
@DisplayName("특정 이름을 접두사로 가진 사용자의 마인드맵 이름들을 모두 조회한다")
void findAllNamesByBaseName_Success() {
User user = User.newUser(1L, "애플");
User user = createUser(1L);
userRepository.save(user);

String baseName = "애플의 마인드맵";
Mindmap m1 = createMindmap(baseName, false);
Mindmap m2 = createMindmap(baseName + "(1)", false);

mindmapRepository.saveAll(List.of(m1, m2));
saveParticipant(user, m1);
saveParticipant(user, m2);

participantRepository.save(createParticipant(m1, user));
participantRepository.save(createParticipant(m2, user));

List<String> names = mindmapRepository.findAllNamesByBaseName(baseName, 1L);

assertThat(names).hasSize(2);
Expand All @@ -85,10 +84,4 @@ void findByIdWithLock_Success() {
assertThat(result.get().getName()).isEqualTo("잠금 테스트");
}

private void saveParticipant(User user, Mindmap mindmap) {
MindmapParticipant participant = createEntity(MindmapParticipant.class);
ReflectionTestUtils.setField(participant, "user", user);
ReflectionTestUtils.setField(participant, "mindmap", mindmap);
participantRepository.save(participant);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

@Testcontainers
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public abstract class AbstractRepositoryTest {

private static final DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:8.0.45");

static final MySQLContainer<?> MYSQL =
new MySQLContainer<>("mysql:8.0").withDatabaseName("testdb").withUsername("test").withPassword("test");
new MySQLContainer<>(MYSQL_IMAGE).withDatabaseName("testdb").withUsername("test").withPassword("test");

static {
MYSQL.start();
Expand All @@ -22,6 +23,6 @@ static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", MYSQL::getJdbcUrl);
registry.add("spring.datasource.username", MYSQL::getUsername);
registry.add("spring.datasource.password", MYSQL::getPassword);
registry.add("spring.datasource.driver-class-name", () -> "com.mysql.cj.jdbc.Driver");
registry.add("spring.datasource.driver-class-name", MYSQL::getDriverClassName);
}
}
Loading