diff --git a/src/main/java/com/vibevault/repository/PlaylistRepository.java b/src/main/java/com/vibevault/repository/PlaylistRepository.java index bf451f0..7bed22b 100644 --- a/src/main/java/com/vibevault/repository/PlaylistRepository.java +++ b/src/main/java/com/vibevault/repository/PlaylistRepository.java @@ -3,8 +3,11 @@ package com.vibevault.repository; import com.vibevault.model.Playlist; import com.vibevault.model.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +import java.util.Optional; + import java.util.List; /** @@ -23,4 +26,8 @@ public interface PlaylistRepository extends JpaRepository { // [Advanced] 按名称模糊搜索歌单 List findByNameContainingIgnoreCase(String keyword); + + // 加载歌单及其包含的歌曲 + @Query("SELECT p FROM Playlist p LEFT JOIN FETCH p.songs WHERE p.id = :id") + Optional findByIdWithSongs(Long id); } diff --git a/src/test/java/com/vibevault/integration/PlaylistIntegrationTest.java b/src/test/java/com/vibevault/integration/PlaylistIntegrationTest.java new file mode 100644 index 0000000..950fd02 --- /dev/null +++ b/src/test/java/com/vibevault/integration/PlaylistIntegrationTest.java @@ -0,0 +1,103 @@ +package com.vibevault.integration; + +import com.vibevault.controller.PlaylistController; +import com.vibevault.dto.SongCreateDTO; +import com.vibevault.exception.UnauthorizedException; +import com.vibevault.model.Playlist; +import com.vibevault.model.User; +import com.vibevault.repository.PlaylistRepository; +import com.vibevault.repository.UserRepository; +import com.vibevault.service.PlaylistServiceImpl; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@TestPropertySource(locations = "classpath:application-test.properties") +class PlaylistIntegrationTest { + + @Autowired + private PlaylistServiceImpl playlistService; + + @Autowired + private PlaylistRepository playlistRepository; + + @Autowired + private UserRepository userRepository; + + @BeforeEach + void setUp() { + // 清理数据库 + playlistRepository.deleteAll(); + userRepository.deleteAll(); + } + + @Test + void testDeletePlaylist_PermissionCheck() { + // 创建所有者用户 + User owner = new User("testowner", "password123"); + owner.setRole("ROLE_USER"); + userRepository.save(owner); + + // 创建另一个用户 + User otherUser = new User("testuser", "password456"); + otherUser.setRole("ROLE_USER"); + userRepository.save(otherUser); + + // 创建歌单 + Playlist playlist = new Playlist("Test Playlist", owner); + playlistRepository.save(playlist); + Long playlistId = playlist.getId(); + + // 验证歌单已创建 + assertEquals(1, playlistRepository.count()); + + // 所有者应该能够删除歌单 + assertDoesNotThrow(() -> playlistService.deletePlaylist(playlistId, "testowner")); + assertEquals(0, playlistRepository.count()); + + // 重新创建歌单 + playlist = new Playlist("Test Playlist", owner); + playlistRepository.save(playlist); + Long newPlaylistId = playlist.getId(); + assertEquals(1, playlistRepository.count()); + + // 非所有者不应该能够删除歌单 + assertThrows(UnauthorizedException.class, () -> playlistService.deletePlaylist(newPlaylistId, "testuser")); + assertEquals(1, playlistRepository.count()); + } + + @Test + void testAddSong_PermissionCheck() { + // 创建所有者用户 + User owner = new User("testowner", "password123"); + owner.setRole("ROLE_USER"); + userRepository.save(owner); + + // 创建另一个用户 + User otherUser = new User("testuser", "password456"); + otherUser.setRole("ROLE_USER"); + userRepository.save(otherUser); + + // 创建歌单 + Playlist playlist = new Playlist("Test Playlist", owner); + playlistRepository.save(playlist); + Long playlistId = playlist.getId(); + + // 所有者应该能够添加歌曲 + SongCreateDTO song = new SongCreateDTO("Test Song", "Test Artist", 180); + assertDoesNotThrow(() -> playlistService.addSongToPlaylist(playlistId, song, "testowner")); + Playlist updatedPlaylist = playlistRepository.findByIdWithSongs(playlistId).orElseThrow(); + assertEquals(1, updatedPlaylist.getSongs().size()); + + // 非所有者不应该能够添加歌曲 + SongCreateDTO song2 = new SongCreateDTO("Test Song 2", "Test Artist", 200); + assertThrows(UnauthorizedException.class, () -> playlistService.addSongToPlaylist(playlistId, song2, "testuser")); + updatedPlaylist = playlistRepository.findByIdWithSongs(playlistId).orElseThrow(); + assertEquals(1, updatedPlaylist.getSongs().size()); + } +} diff --git a/src/test/java/com/vibevault/service/PlaylistDeleteTest.java b/src/test/java/com/vibevault/service/PlaylistDeleteTest.java new file mode 100644 index 0000000..e08355a --- /dev/null +++ b/src/test/java/com/vibevault/service/PlaylistDeleteTest.java @@ -0,0 +1,105 @@ +package com.vibevault.service; + +import com.vibevault.exception.UnauthorizedException; +import com.vibevault.model.Playlist; +import com.vibevault.model.User; +import com.vibevault.repository.PlaylistRepository; +import com.vibevault.repository.UserRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +class PlaylistDeleteTest { + + @Mock + private PlaylistRepository playlistRepository; + + @Mock + private UserRepository userRepository; + + @InjectMocks + private PlaylistServiceImpl playlistService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testDeletePlaylist_OwnerCanDelete() { + // 创建用户和歌单 + User owner = new User("testuser", "password"); + owner.setRole("ROLE_USER"); + Playlist playlist = new Playlist("Test Playlist", owner); + playlist.setId(1L); + + // 模拟用户存储库 + when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(owner)); + + // 模拟歌单存储库 + when(playlistRepository.findById(1L)).thenReturn(Optional.of(playlist)); + + // 所有者应该能够删除自己的歌单,不应该抛出异常 + playlistService.deletePlaylist(1L, "testuser"); + + // 验证歌单已被删除 + verify(playlistRepository, times(1)).delete(playlist); + } + + @Test + void testDeletePlaylist_NonOwnerCannotDelete() { + // 创建所有者用户和歌单 + User owner = new User("owner", "password"); + owner.setRole("ROLE_USER"); + Playlist playlist = new Playlist("Test Playlist", owner); + playlist.setId(1L); + + // 创建另一个用户 + User otherUser = new User("otheruser", "password"); + otherUser.setRole("ROLE_USER"); + + // 模拟用户存储库 + when(userRepository.findByUsername("otheruser")).thenReturn(Optional.of(otherUser)); + + // 模拟歌单存储库 + when(playlistRepository.findById(1L)).thenReturn(Optional.of(playlist)); + + // 非所有者不应该能够删除歌单,应该抛出 UnauthorizedException + assertThrows(UnauthorizedException.class, () -> playlistService.deletePlaylist(1L, "otheruser")); + + // 验证歌单没有被删除 + verify(playlistRepository, never()).delete(any(Playlist.class)); + } + + @Test + void testDeletePlaylist_AdminCanDelete() { + // 创建所有者用户和歌单 + User owner = new User("owner", "password"); + owner.setRole("ROLE_USER"); + Playlist playlist = new Playlist("Test Playlist", owner); + playlist.setId(1L); + + // 创建管理员用户 + User adminUser = new User("admin", "password"); + adminUser.setRole("ROLE_ADMIN"); + + // 模拟用户存储库 + when(userRepository.findByUsername("admin")).thenReturn(Optional.of(adminUser)); + + // 模拟歌单存储库 + when(playlistRepository.findById(1L)).thenReturn(Optional.of(playlist)); + + // 管理员应该能够删除任何歌单,不应该抛出异常 + playlistService.deletePlaylist(1L, "admin"); + + // 验证歌单已被删除 + verify(playlistRepository, times(1)).delete(playlist); + } +}