-
Notifications
You must be signed in to change notification settings - Fork 0
Add UserMediaList entity, Flyway migration, and persistence tests #86
Changes from all commits
eff5550
b9d3513
a1735b4
cc3d08d
9fb0fdd
ce65993
fd06f72
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| package com.espacogeek.geek.models; | ||
|
|
||
| import java.io.Serializable; | ||
| import java.util.Date; | ||
| import java.util.UUID; | ||
|
|
||
| import org.hibernate.annotations.UuidGenerator; | ||
|
|
||
| import jakarta.persistence.Column; | ||
| import jakarta.persistence.Entity; | ||
| import jakarta.persistence.Id; | ||
| import jakarta.persistence.JoinColumn; | ||
| import jakarta.persistence.ManyToOne; | ||
| import jakarta.persistence.Table; | ||
| import jakarta.persistence.Temporal; | ||
| import jakarta.persistence.TemporalType; | ||
| import jakarta.persistence.UniqueConstraint; | ||
| import jakarta.validation.constraints.NotNull; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
| import lombok.Setter; | ||
|
|
||
| @Entity | ||
| @Table(name = "user_media_list", uniqueConstraints = { | ||
| @UniqueConstraint(name = "uniq_user_media_list_user_media", columnNames = {"user_id", "media_id"}) | ||
| }) | ||
| @Getter | ||
| @Setter | ||
| @AllArgsConstructor | ||
| @NoArgsConstructor | ||
| public class UserMediaListModel implements Serializable { | ||
|
Comment on lines
+28
to
+32
|
||
| @Id | ||
| @UuidGenerator(style = UuidGenerator.Style.TIME) | ||
| @Column(name = "id_user_media_list", columnDefinition = "BINARY(16)", updatable = false, nullable = false) | ||
| private UUID id; | ||
|
|
||
| @ManyToOne | ||
| @JoinColumn(name = "user_id", nullable = false) | ||
| @NotNull | ||
| private UserModel user; | ||
|
|
||
| @ManyToOne | ||
| @JoinColumn(name = "media_id", nullable = false) | ||
| @NotNull | ||
| private MediaModel media; | ||
|
|
||
| @Column(name = "status", length = 50) | ||
| private String status; | ||
|
|
||
| @Column(name = "score") | ||
| private Float score; | ||
|
|
||
| @Column(name = "progress") | ||
| private Integer progress; | ||
|
|
||
| @Column(name = "start_date") | ||
| @Temporal(TemporalType.TIMESTAMP) | ||
| private Date startDate; | ||
|
|
||
| @Column(name = "finish_date") | ||
| @Temporal(TemporalType.TIMESTAMP) | ||
| private Date finishDate; | ||
|
|
||
| @Column(name = "time_spent") | ||
| private Integer timeSpent; | ||
|
|
||
| @Column(name = "note", length = 2000) | ||
| private String note; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.espacogeek.geek.repositories; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
|
|
||
| import com.espacogeek.geek.models.UserMediaListModel; | ||
|
|
||
| public interface UserMediaListRepository extends JpaRepository<UserMediaListModel, UUID> { | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| CREATE TABLE IF NOT EXISTS `user_media_list` ( | ||
| `id_user_media_list` BINARY(16) NOT NULL, | ||
| `user_id` INT NOT NULL, | ||
| `media_id` INT NOT NULL, | ||
| `status` VARCHAR(50) DEFAULT NULL, | ||
| `score` DECIMAL(3,1) DEFAULT NULL, | ||
| `progress` INT DEFAULT NULL, | ||
| `start_date` DATETIME DEFAULT NULL, | ||
| `finish_date` DATETIME DEFAULT NULL, | ||
| `time_spent` INT DEFAULT NULL, | ||
| `note` VARCHAR(2000) DEFAULT NULL, | ||
| PRIMARY KEY (`id_user_media_list`), | ||
| KEY `idx_user_media_list_user` (`user_id`), | ||
| KEY `idx_user_media_list_media` (`media_id`), | ||
|
vitorhugo-java marked this conversation as resolved.
|
||
| UNIQUE KEY `uniq_user_media_list_user_media` (`user_id`, `media_id`), | ||
| CONSTRAINT `FK_user_media_list_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id_user`) ON DELETE CASCADE, | ||
| CONSTRAINT `FK_user_media_list_media` FOREIGN KEY (`media_id`) REFERENCES `medias` (`id_media`) ON DELETE CASCADE | ||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package com.espacogeek.geek.repositories; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
| import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
|
||
| import org.junit.jupiter.api.BeforeEach; | ||
| 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.dao.DataIntegrityViolationException; | ||
| import org.springframework.test.context.ActiveProfiles; | ||
|
|
||
| import com.espacogeek.geek.models.MediaCategoryModel; | ||
| import com.espacogeek.geek.models.MediaModel; | ||
| import com.espacogeek.geek.models.UserMediaListModel; | ||
| import com.espacogeek.geek.models.UserModel; | ||
|
|
||
| @DataJpaTest | ||
| @ActiveProfiles("test") | ||
| class UserMediaListRepositoryTest { | ||
|
|
||
| @Autowired | ||
| private UserMediaListRepository userMediaListRepository; | ||
|
|
||
| @Autowired | ||
| private UserRepository userRepository; | ||
|
|
||
| @Autowired | ||
| private MediaRepository mediaRepository; | ||
|
|
||
| @Autowired | ||
| private MediaCategoryRepository mediaCategoryRepository; | ||
|
|
||
| private UserModel user; | ||
| private MediaModel media; | ||
|
|
||
| @BeforeEach | ||
| void setUp() { | ||
| MediaCategoryModel category = mediaCategoryRepository.save(new MediaCategoryModel(null, "ANIME", null)); | ||
|
|
||
| UserModel newUser = new UserModel(); | ||
| newUser.setUsername("testuser"); | ||
| newUser.setEmail("test@example.com"); | ||
| newUser.setPassword("password1".getBytes()); | ||
| user = userRepository.save(newUser); | ||
|
|
||
| MediaModel newMedia = new MediaModel(); | ||
| newMedia.setName("Test Media"); | ||
| newMedia.setMediaCategory(category); | ||
| media = mediaRepository.save(newMedia); | ||
| } | ||
|
|
||
| @Test | ||
| void persist_ShouldSaveAndRetrieveUserMediaListWithGeneratedUuid() { | ||
| UserMediaListModel entry = new UserMediaListModel(); | ||
| entry.setUser(user); | ||
| entry.setMedia(media); | ||
| entry.setStatus("watching"); | ||
| entry.setProgress(5); | ||
|
|
||
| UserMediaListModel saved = userMediaListRepository.save(entry); | ||
|
|
||
| assertThat(saved.getId()).isNotNull(); | ||
| assertThat(userMediaListRepository.findById(saved.getId())).isPresent() | ||
| .get() | ||
| .satisfies(found -> { | ||
| assertThat(found.getUser().getId()).isEqualTo(user.getId()); | ||
| assertThat(found.getMedia().getId()).isEqualTo(media.getId()); | ||
| assertThat(found.getStatus()).isEqualTo("watching"); | ||
| assertThat(found.getProgress()).isEqualTo(5); | ||
| }); | ||
| } | ||
|
|
||
| @Test | ||
| void persist_WhenDuplicateUserAndMedia_ShouldViolateUniqueConstraint() { | ||
| UserMediaListModel first = new UserMediaListModel(); | ||
| first.setUser(user); | ||
| first.setMedia(media); | ||
| first.setStatus("watching"); | ||
| userMediaListRepository.saveAndFlush(first); | ||
|
|
||
| UserMediaListModel duplicate = new UserMediaListModel(); | ||
| duplicate.setUser(user); | ||
| duplicate.setMedia(media); | ||
| duplicate.setStatus("completed"); | ||
|
|
||
| assertThatThrownBy(() -> userMediaListRepository.saveAndFlush(duplicate)) | ||
| .isInstanceOf(DataIntegrityViolationException.class); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider enforcing the intended uniqueness at the JPA level too (via @table(uniqueConstraints=...)) so the constraint is discoverable in code and stays aligned with the DB. There’s a precedent for this pattern in DailyQuoteArtworkModel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot apply changes based on this feedback
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in commit ce65993 —
@Tablenow declares the unique constraint matching the DB'suniq_user_media_list_user_mediakey: