diff --git a/build.gradle.kts b/build.gradle.kts index 2f0e6ab4..23a7f6f0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -58,6 +58,8 @@ dependencies { testImplementation("org.testcontainers:junit-jupiter") testImplementation("org.testcontainers:mysql") testImplementation("org.testcontainers:mongodb") + testImplementation("io.mockk:mockk:1.14.7") + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") // Kotest testImplementation("io.kotest:kotest-runner-junit5:5.9.1") diff --git a/src/main/java/leets/leenk/domain/notification/domain/entity/Notification.java b/src/main/java/leets/leenk/domain/notification/domain/entity/Notification.java index 32c97f77..dd197ced 100644 --- a/src/main/java/leets/leenk/domain/notification/domain/entity/Notification.java +++ b/src/main/java/leets/leenk/domain/notification/domain/entity/Notification.java @@ -1,29 +1,25 @@ package leets.leenk.domain.notification.domain.entity; import leets.leenk.domain.notification.domain.entity.enums.NotificationType; -import org.springframework.data.mongodb.core.mapping.Document; - -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.Id; -import leets.leenk.global.common.entity.BaseEntity; +import leets.leenk.global.common.entity.MongoBaseEntity; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; @SuperBuilder @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @Document(collection = "notifications") -public class Notification extends BaseEntity { +public class Notification extends MongoBaseEntity { @Id private String id; private Long userId; - @Enumerated(EnumType.STRING) private NotificationType notificationType; private Boolean isRead; diff --git a/src/main/java/leets/leenk/global/common/entity/MongoBaseEntity.java b/src/main/java/leets/leenk/global/common/entity/MongoBaseEntity.java new file mode 100644 index 00000000..d862c157 --- /dev/null +++ b/src/main/java/leets/leenk/global/common/entity/MongoBaseEntity.java @@ -0,0 +1,21 @@ +package leets.leenk.global.common.entity; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; + +import java.time.LocalDateTime; + +@Getter +@SuperBuilder +@NoArgsConstructor +public abstract class MongoBaseEntity { + + @CreatedDate + private LocalDateTime createDate; + + @LastModifiedDate + private LocalDateTime updateDate; +} diff --git a/src/main/java/leets/leenk/global/config/JpaConfig.java b/src/main/java/leets/leenk/global/config/JpaConfig.java deleted file mode 100644 index b70c2db4..00000000 --- a/src/main/java/leets/leenk/global/config/JpaConfig.java +++ /dev/null @@ -1,17 +0,0 @@ -package leets.leenk.global.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; - -@Configuration -@EnableJpaAuditing -@EnableJpaRepositories(basePackages = { - "leets.leenk.domain.feed.domain.repository", - "leets.leenk.domain.user.domain.repository", - "leets.leenk.domain.media.domain.repository", - "leets.leenk.domain.leenk.domain.repository", - "leets.leenk.domain.birthday.domain.repository", -}) -public class JpaConfig { -} diff --git a/src/main/java/leets/leenk/global/config/MongoConfig.java b/src/main/java/leets/leenk/global/config/MongoConfig.java deleted file mode 100644 index 1fa68e1d..00000000 --- a/src/main/java/leets/leenk/global/config/MongoConfig.java +++ /dev/null @@ -1,13 +0,0 @@ -package leets.leenk.global.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.config.EnableMongoAuditing; -import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; - -@Configuration -@EnableMongoAuditing -@EnableMongoRepositories(basePackages = { - "leets.leenk.domain.notification.domain.repository" -}) -public class MongoConfig { -} diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml new file mode 100644 index 00000000..751a56a8 --- /dev/null +++ b/src/main/resources/application-test.yml @@ -0,0 +1,9 @@ +spring: + jpa: + hibernate: + ddl-auto: create-drop + show-sql: true + properties: + hibernate: + format_sql: true + dialect: org.hibernate.dialect.MySQL8Dialect diff --git a/src/test/java/leets/leenk/config/MysqlTestConfig.java b/src/test/java/leets/leenk/config/MysqlTestConfig.java new file mode 100644 index 00000000..02ac9ab1 --- /dev/null +++ b/src/test/java/leets/leenk/config/MysqlTestConfig.java @@ -0,0 +1,19 @@ +package leets.leenk.config; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.context.annotation.Bean; +import org.testcontainers.containers.MySQLContainer; + +@TestConfiguration +public class MysqlTestConfig { + + private static final String MYSQL_IMAGE = "mysql:8.0.41"; + + @Bean + @ServiceConnection + public MySQLContainer mysqlContainer() { + return new MySQLContainer<>(MYSQL_IMAGE) + .withDatabaseName("testdb"); + } +} diff --git a/src/test/java/leets/leenk/config/TestContainersTest.java b/src/test/java/leets/leenk/config/TestContainersTest.java new file mode 100644 index 00000000..400933ec --- /dev/null +++ b/src/test/java/leets/leenk/config/TestContainersTest.java @@ -0,0 +1,30 @@ +package leets.leenk.config; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +import org.testcontainers.containers.MySQLContainer; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import(MysqlTestConfig.class) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +class TestContainersTest { + + @Autowired + private MySQLContainer mysqlContainer; + + + @Test + void MySQL_컨테이너_정상_동작_테스트() { + // MySQL 컨테이너 테스트 + assertThat(mysqlContainer).isNotNull(); + assertThat(mysqlContainer.isRunning()).isTrue(); + assertThat(mysqlContainer.getDatabaseName()).isEqualTo("testdb"); + + System.out.println("MySQL Container JDBC URL: " + mysqlContainer.getJdbcUrl()); + } +} diff --git a/src/test/java/leets/leenk/domain/user/domain/repository/UserRepositoryTest.java b/src/test/java/leets/leenk/domain/user/domain/repository/UserRepositoryTest.java new file mode 100644 index 00000000..9e556e24 --- /dev/null +++ b/src/test/java/leets/leenk/domain/user/domain/repository/UserRepositoryTest.java @@ -0,0 +1,83 @@ +package leets.leenk.domain.user.domain.repository; + +import leets.leenk.config.MysqlTestConfig; +import leets.leenk.domain.user.domain.entity.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.time.LocalDate; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import(MysqlTestConfig.class) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +class UserRepositoryTest { + + @Autowired + private UserRepository userRepository; + + @BeforeEach + void setUp() { + userRepository.deleteAll(); + } + + @Test + @DisplayName("특정 날짜에 생일인 활성 사용자만 이름순으로 조회한다") + void findAllUsersInBirthday() { + // given + LocalDate birthday = LocalDate.of(2000, 5, 15); + + User activeUser1 = createUser(1L, "김철수", birthday, 1); + User activeUser2 = createUser(2L, "이영희", birthday, 1); + User activeUser3 = createUser(3L, "박민수", birthday, 1); + + // 탈퇴한 사용자 (제외되어야 함) + User leftUser = createUser(4L, "탈퇴유저", birthday, 1); + leftUser.leave(); + + // 삭제된 사용자 (제외되어야 함) + User deletedUser = createUser(5L, "삭제유저", birthday, 1); + deletedUser.delete(); + + // 생일이 다른 사용자 (제외되어야 함) + User differentBirthdayUser = createUser(6L, "다른생일", LocalDate.of(2000, 6, 20), 1); + + // 생일이 null인 사용자 (제외되어야 함) + User noBirthdayUser = createUser(7L, "생일없음", null, 1); + + userRepository.saveAll(List.of( + activeUser1, activeUser2, activeUser3, + leftUser, deletedUser, differentBirthdayUser, noBirthdayUser + )); + + // when + List result = userRepository.findAllUsersInBirthday(5, 15); + + // then + assertThat(result).hasSize(3); + assertThat(result).extracting(User::getName) + .containsExactly("김철수", "박민수", "이영희"); // 이름순 정렬 확인 + assertThat(result).allMatch(user -> user.getBirthday().equals(birthday)); + assertThat(result).allMatch(user -> user.getLeaveDate() == null); + assertThat(result).allMatch(user -> user.getDeleteDate() == null); + } + + private User createUser(Long id, String name, LocalDate birthday, int cardinal) { + return User.builder() + .id(id) + .name(name) + .birthday(birthday) + .cardinal(cardinal) + .totalReactionCount(0) + .termsAgreement(true) + .privacyAgreement(true) + .build(); + } +} diff --git a/src/test/kotlin/leets/leenk/domain/leenk/application/usecase/LeenkUsecaseTest.kt b/src/test/kotlin/leets/leenk/domain/leenk/application/usecase/LeenkUsecaseTest.kt new file mode 100644 index 00000000..99186071 --- /dev/null +++ b/src/test/kotlin/leets/leenk/domain/leenk/application/usecase/LeenkUsecaseTest.kt @@ -0,0 +1,339 @@ +package leets.leenk.domain.leenk.application.usecase + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import leets.leenk.domain.leenk.application.exception.* +import leets.leenk.domain.leenk.application.mapper.LeenkMapper +import leets.leenk.domain.leenk.application.mapper.LeenkParticipantsMapper +import leets.leenk.domain.leenk.application.mapper.LocationMapper +import leets.leenk.domain.leenk.domain.entity.Leenk +import leets.leenk.domain.leenk.domain.entity.LeenkParticipants +import leets.leenk.domain.leenk.domain.entity.Location +import leets.leenk.domain.leenk.domain.entity.enums.LeenkStatus +import leets.leenk.domain.leenk.domain.service.* +import leets.leenk.domain.leenk.test.fixture.LeenkParticipantsTestFixture +import leets.leenk.domain.leenk.test.fixture.LeenkTestFixture +import leets.leenk.domain.leenk.test.fixture.LocationTestFixture +import leets.leenk.domain.media.application.mapper.MediaMapper +import leets.leenk.domain.media.domain.service.MediaDeleteService +import leets.leenk.domain.media.domain.service.MediaGetService +import leets.leenk.domain.media.domain.service.MediaSaveService +import leets.leenk.domain.notification.application.usecase.LeenkNotificationUsecase +import leets.leenk.domain.user.domain.entity.User +import leets.leenk.domain.user.domain.service.NotionDatabaseService +import leets.leenk.domain.user.domain.service.SlackWebhookService +import leets.leenk.domain.user.domain.service.user.UserGetService +import leets.leenk.domain.user.test.fixture.UserTestFixture +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.* + + +class LeenkUsecaseTest { + + private val locationSaveService: LocationSaveService = mockk() + private val leenkSaveService: LeenkSaveService = mockk() + private val leenkGetService: LeenkGetService = mockk() + private val leenkUpdateService: LeenkUpdateService = mockk() + private val leenkDeleteService: LeenkDeleteService = mockk() + + private val leenkParticipantsSaveService: LeenkParticipantsSaveService = mockk() + private val leenkParticipantsGetService: LeenkParticipantsGetService = mockk() + private val leenkParticipantsDeleteService: LeenkParticipantsDeleteService = mockk() + + private val mediaSaveService: MediaSaveService = mockk() + private val mediaGetService: MediaGetService = mockk() + private val mediaDeleteService: MediaDeleteService = mockk() + + private val userGetService: UserGetService = mockk() + private val slackWebhookService: SlackWebhookService = mockk() + private val notionDatabaseService: NotionDatabaseService = mockk() + + private val leenkMapper: LeenkMapper = mockk() + private val participantsMapper: LeenkParticipantsMapper = mockk() + private val locationMapper: LocationMapper = mockk() + private val mediaMapper: MediaMapper = mockk() + + private val leenkNotificationUsecase: LeenkNotificationUsecase = mockk(relaxed = true) + + private lateinit var leenkUsecase: LeenkUsecase + private lateinit var user: User + private lateinit var recruitingLeenk: Leenk + private lateinit var participant: LeenkParticipants + private lateinit var location: Location + + private fun createLeenkUsecase( + locationSaveService: LocationSaveService, + leenkSaveService: LeenkSaveService, + leenkGetService: LeenkGetService, + leenkUpdateService: LeenkUpdateService, + leenkDeleteService: LeenkDeleteService, + leenkParticipantsSaveService: LeenkParticipantsSaveService, + leenkParticipantsGetService: LeenkParticipantsGetService, + leenkParticipantsDeleteService: LeenkParticipantsDeleteService, + mediaSaveService: MediaSaveService, + mediaGetService: MediaGetService, + mediaDeleteService: MediaDeleteService, + userGetService: UserGetService, + slackWebhookService: SlackWebhookService, + notionDatabaseService: NotionDatabaseService, + leenkMapper: LeenkMapper, + participantsMapper: LeenkParticipantsMapper, + locationMapper: LocationMapper, + mediaMapper: MediaMapper, + leenkNotificationUsecase: LeenkNotificationUsecase + ): LeenkUsecase { + return LeenkUsecase( + locationSaveService, + leenkSaveService, + leenkGetService, + leenkUpdateService, + leenkDeleteService, + leenkParticipantsSaveService, + leenkParticipantsGetService, + leenkParticipantsDeleteService, + mediaSaveService, + mediaGetService, + mediaDeleteService, + userGetService, + slackWebhookService, + notionDatabaseService, + leenkMapper, + participantsMapper, + locationMapper, + mediaMapper, + leenkNotificationUsecase + ) + } + + @BeforeEach + fun setUp() { + leenkUsecase = createLeenkUsecase( + locationSaveService = locationSaveService, + leenkSaveService = leenkSaveService, + leenkGetService = leenkGetService, + leenkUpdateService = leenkUpdateService, + leenkDeleteService = leenkDeleteService, + leenkParticipantsSaveService = leenkParticipantsSaveService, + leenkParticipantsGetService = leenkParticipantsGetService, + leenkParticipantsDeleteService = leenkParticipantsDeleteService, + mediaSaveService = mediaSaveService, + mediaGetService = mediaGetService, + mediaDeleteService = mediaDeleteService, + userGetService = userGetService, + slackWebhookService = slackWebhookService, + notionDatabaseService = notionDatabaseService, + leenkMapper = leenkMapper, + participantsMapper = participantsMapper, + locationMapper = locationMapper, + mediaMapper = mediaMapper, + leenkNotificationUsecase = leenkNotificationUsecase + ) + + user = UserTestFixture.createUser(id = 1L) + location = LocationTestFixture.createLocation(id = 1L) + recruitingLeenk = LeenkTestFixture.createLeenk( + id = 1L, + author = user, + location = location, + status = LeenkStatus.RECRUITING, + currentParticipants = 2L, + maxParticipants = 10L + ) + participant = LeenkParticipantsTestFixture.createParticipant( + leenk = recruitingLeenk, + participant = user + ) + } + + @Nested + @DisplayName("participateLeenk 관련 테스트") + inner class ParticipateLeenkTests { + @Test + @DisplayName("모집 중인 링크에 정상적으로 참여한다") + fun participateLeenkSuccess() { + // given + every { userGetService.findById(1L) } returns user + every { leenkGetService.findById(1L) } returns recruitingLeenk + every { leenkParticipantsGetService.existsByLeenkAndParticipant(recruitingLeenk, user) } returns false + every { participantsMapper.toParticipants(recruitingLeenk, user, any()) } returns participant + every { leenkParticipantsSaveService.save(any()) } returns participant + + val initialParticipants = recruitingLeenk.currentParticipants + + // when + leenkUsecase.participateLeenk(1L, 1L) + + // then + verify(exactly = 1) { leenkParticipantsSaveService.save(participant) } + verify(exactly = 1) { leenkNotificationUsecase.saveNewLeenkParticipantNotification(recruitingLeenk, user) } + assertThat(recruitingLeenk.currentParticipants).isEqualTo(initialParticipants + 1) + } + + @Test + @DisplayName("모집 중이 아닌 링크에 참여 시 예외가 발생한다") + fun participateLeenkNotRecruitingThrowsException() { + // given + val closedLeenk = LeenkTestFixture.createClosedLeenk( + id = 1L, + author = user, + location = location + ) + every { userGetService.findById(1L) } returns user + every { leenkGetService.findById(1L) } returns closedLeenk + + // when & then + assertThrows { + leenkUsecase.participateLeenk(1L, 1L) + } + + verify(exactly = 0) { leenkParticipantsSaveService.save(any()) } + verify(exactly = 0) { leenkNotificationUsecase.saveNewLeenkParticipantNotification(any(), any()) } + } + + @Test + @DisplayName("이미 참여한 사용자가 다시 참여 시 예외가 발생한다") + fun participateLeenkAlreadyParticipatedThrowsException() { + // given + every { userGetService.findById(1L) } returns user + every { leenkGetService.findById(1L) } returns recruitingLeenk + every { leenkParticipantsGetService.existsByLeenkAndParticipant(recruitingLeenk, user) } returns true + + // when & then + assertThrows { + leenkUsecase.participateLeenk(1L, 1L) + } + + verify(exactly = 0) { leenkParticipantsSaveService.save(any()) } + verify(exactly = 0) { leenkNotificationUsecase.saveNewLeenkParticipantNotification(any(), any()) } + } + + @Test + @DisplayName("최대 참여 인원을 초과하면 예외가 발생한다") + fun participateLeenkMaxParticipantsExceededThrowsException() { + // given + val fullLeenk = LeenkTestFixture.createFullLeenk( + id = 1L, + author = user, + location = location + ) + every { userGetService.findById(1L) } returns user + every { leenkGetService.findById(1L) } returns fullLeenk + every { leenkParticipantsGetService.existsByLeenkAndParticipant(fullLeenk, user) } returns false + + // when & then + assertThrows { + leenkUsecase.participateLeenk(1L, 1L) + } + + verify(exactly = 0) { leenkParticipantsSaveService.save(any()) } + verify(exactly = 0) { leenkNotificationUsecase.saveNewLeenkParticipantNotification(any(), any()) } + } + + @Test + @DisplayName("최대 참여 인원 직전에 참여하면 정상적으로 처리된다") + fun participateLeenkLastSlotSuccess() { + // given + val almostFullLeenk = LeenkTestFixture.createAlmostFullLeenk( + id = 1L, + author = user, + location = location + ) + + val lastParticipant = LeenkParticipantsTestFixture.createParticipant( + leenk = almostFullLeenk, + participant = user + ) + + every { userGetService.findById(1L) } returns user + every { leenkGetService.findById(1L) } returns almostFullLeenk + every { leenkParticipantsGetService.existsByLeenkAndParticipant(almostFullLeenk, user) } returns false + every { participantsMapper.toParticipants(almostFullLeenk, user, any()) } returns lastParticipant + every { leenkParticipantsSaveService.save(any()) } returns lastParticipant + + // when + leenkUsecase.participateLeenk(1L, 1L) + + // then + verify(exactly = 1) { leenkParticipantsSaveService.save(lastParticipant) } + verify(exactly = 1) { leenkNotificationUsecase.saveNewLeenkParticipantNotification(almostFullLeenk, user) } + assertThat(almostFullLeenk.currentParticipants).isEqualTo(almostFullLeenk.maxParticipants) + } + } + + @Nested + @DisplayName("closeLeenk 관련 테스트") + inner class CloseLeenkTests { + @Test + @DisplayName("링크 작성자가 모집 중인 링크를 정상적으로 마감한다") + fun closeLeenkSuccess() { + // given + every { userGetService.findById(1L) } returns user + every { leenkGetService.findById(1L) } returns recruitingLeenk + + // when + leenkUsecase.closeLeenk(1L, 1L) + + // then + assertThat(recruitingLeenk.status).isEqualTo(LeenkStatus.CLOSED) + verify(exactly = 1) { leenkNotificationUsecase.saveLeenkClosedNotification(recruitingLeenk) } + } + + @Test + @DisplayName("링크 작성자가 아닌 사용자가 마감 시도 시 예외가 발생한다") + fun closeLeenkNotOwnerThrowsException() { + // given + val otherUser = UserTestFixture.createUser(id = 2L, name = "김영희") + every { userGetService.findById(2L) } returns otherUser + every { leenkGetService.findById(1L) } returns recruitingLeenk + + // when & then + assertThrows { + leenkUsecase.closeLeenk(2L, 1L) + } + + assertThat(recruitingLeenk.status).isEqualTo(LeenkStatus.RECRUITING) + verify(exactly = 0) { leenkNotificationUsecase.saveLeenkClosedNotification(any()) } + } + + @Test + @DisplayName("이미 마감된 링크를 다시 마감 시 예외가 발생한다") + fun closeLeenkAlreadyClosedThrowsException() { + // given + val closedLeenk = LeenkTestFixture.createClosedLeenk( + id = 1L, + author = user, + location = location + ) + every { userGetService.findById(1L) } returns user + every { leenkGetService.findById(1L) } returns closedLeenk + + // when & then + assertThrows { + leenkUsecase.closeLeenk(1L, 1L) + } + + verify(exactly = 0) { leenkNotificationUsecase.saveLeenkClosedNotification(any()) } + } + + @Test + @DisplayName("완료된 상태의 링크를 마감 시 예외가 발생한다") + fun closeLeenkCompletedStatusThrowsException() { + // given + val completedLeenk = LeenkTestFixture.createFinishedLeenk( + id = 1L, + author = user, + location = location + ) + every { userGetService.findById(1L) } returns user + every { leenkGetService.findById(1L) } returns completedLeenk + + // when & then + assertThrows { + leenkUsecase.closeLeenk(1L, 1L) + } + + verify(exactly = 0) { leenkNotificationUsecase.saveLeenkClosedNotification(any()) } + } + } +} diff --git a/src/test/kotlin/leets/leenk/domain/leenk/test/fixture/LeenkParticipantsTestFixture.kt b/src/test/kotlin/leets/leenk/domain/leenk/test/fixture/LeenkParticipantsTestFixture.kt new file mode 100644 index 00000000..e36fc79a --- /dev/null +++ b/src/test/kotlin/leets/leenk/domain/leenk/test/fixture/LeenkParticipantsTestFixture.kt @@ -0,0 +1,28 @@ +package leets.leenk.domain.leenk.test.fixture + +import leets.leenk.domain.leenk.domain.entity.Leenk +import leets.leenk.domain.leenk.domain.entity.LeenkParticipants +import leets.leenk.domain.user.domain.entity.User +import java.time.LocalDateTime + +class LeenkParticipantsTestFixture { + companion object { + fun createParticipant( + id: Long? = null, + leenk: Leenk, + participant: User, + joinedAt: LocalDateTime = LocalDateTime.now() + ): LeenkParticipants { + val builder = LeenkParticipants.builder() + .leenk(leenk) + .participant(participant) + .joinedAt(joinedAt) + + if (id != null) { + builder.id(id) + } + + return builder.build() + } + } +} diff --git a/src/test/kotlin/leets/leenk/domain/leenk/test/fixture/LeenkTestFixture.kt b/src/test/kotlin/leets/leenk/domain/leenk/test/fixture/LeenkTestFixture.kt new file mode 100644 index 00000000..406b3f1f --- /dev/null +++ b/src/test/kotlin/leets/leenk/domain/leenk/test/fixture/LeenkTestFixture.kt @@ -0,0 +1,105 @@ +package leets.leenk.domain.leenk.test.fixture + +import leets.leenk.domain.leenk.domain.entity.Leenk +import leets.leenk.domain.leenk.domain.entity.Location +import leets.leenk.domain.leenk.domain.entity.enums.LeenkStatus +import leets.leenk.domain.user.domain.entity.User +import java.time.LocalDateTime + +class LeenkTestFixture { + companion object { + fun createLeenk( + id: Long? = null, + author: User, + location: Location, + title: String = "테스트 링크", + content: String? = "테스트 내용", + startTime: LocalDateTime = LocalDateTime.now().plusDays(1), + maxParticipants: Long = 10L, + currentParticipants: Long = 1L, + status: LeenkStatus = LeenkStatus.RECRUITING + ): Leenk { + val builder = Leenk.builder() + .author(author) + .location(location) + .title(title) + .content(content) + .startTime(startTime) + .maxParticipants(maxParticipants) + .currentParticipants(currentParticipants) + .status(status) + + if (id != null) { + builder.id(id) + } + + return builder.build() + } + + fun createClosedLeenk( + id: Long? = null, + author: User, + location: Location, + currentParticipants: Long = 5L, + maxParticipants: Long = 10L + ): Leenk { + return createLeenk( + id = id, + author = author, + location = location, + status = LeenkStatus.CLOSED, + currentParticipants = currentParticipants, + maxParticipants = maxParticipants + ) + } + + fun createFullLeenk( + id: Long? = null, + author: User, + location: Location, + maxParticipants: Long = 10L + ): Leenk { + return createLeenk( + id = id, + author = author, + location = location, + status = LeenkStatus.RECRUITING, + currentParticipants = maxParticipants, + maxParticipants = maxParticipants + ) + } + + fun createAlmostFullLeenk( + id: Long? = null, + author: User, + location: Location, + maxParticipants: Long = 10L + ): Leenk { + return createLeenk( + id = id, + author = author, + location = location, + status = LeenkStatus.RECRUITING, + currentParticipants = maxParticipants - 1, + maxParticipants = maxParticipants + ) + } + + fun createFinishedLeenk( + id: Long? = null, + author: User, + location: Location, + currentParticipants: Long = 10L, + maxParticipants: Long = 10L + ): Leenk { + return createLeenk( + id = id, + author = author, + location = location, + status = LeenkStatus.FINISHED, + currentParticipants = currentParticipants, + maxParticipants = maxParticipants + ) + } + } +} diff --git a/src/test/kotlin/leets/leenk/domain/leenk/test/fixture/LocationTestFixture.kt b/src/test/kotlin/leets/leenk/domain/leenk/test/fixture/LocationTestFixture.kt new file mode 100644 index 00000000..0acc00bb --- /dev/null +++ b/src/test/kotlin/leets/leenk/domain/leenk/test/fixture/LocationTestFixture.kt @@ -0,0 +1,17 @@ +package leets.leenk.domain.leenk.test.fixture + +import leets.leenk.domain.leenk.domain.entity.Location + +class LocationTestFixture { + companion object { + fun createLocation( + id: Long? = null, + placeName: String = "테스트 장소" + ): Location { + return Location.builder() + .apply { id?.let { id(it) } } + .placeName(placeName) + .build() + } + } +} diff --git a/src/test/kotlin/leets/leenk/domain/user/test/fixture/UserTestFixture.kt b/src/test/kotlin/leets/leenk/domain/user/test/fixture/UserTestFixture.kt new file mode 100644 index 00000000..a948a430 --- /dev/null +++ b/src/test/kotlin/leets/leenk/domain/user/test/fixture/UserTestFixture.kt @@ -0,0 +1,38 @@ +package leets.leenk.domain.user.test.fixture + +import leets.leenk.domain.user.domain.entity.User +import java.time.LocalDate + +class UserTestFixture { + companion object { + fun createUser( + id: Long = 1L, + name: String = "테스트유저", + cardinal: Int = 1, + profileImage: String? = null, + birthday: LocalDate? = null, + thumbnail: String? = null, + mbti: String? = null, + introduction: String? = null, + fcmToken: String? = null, + kakaoTalkId: String? = null, + totalReactionCount: Long = 0L, + termsAgreement: Boolean = true, + privacyAgreement: Boolean = true + ): User = User.builder() + .id(id) + .name(name) + .cardinal(cardinal) + .profileImage(profileImage) + .birthday(birthday) + .thumbnail(thumbnail) + .mbti(mbti) + .introduction(introduction) + .fcmToken(fcmToken) + .kakaoTalkId(kakaoTalkId) + .totalReactionCount(totalReactionCount) + .termsAgreement(termsAgreement) + .privacyAgreement(privacyAgreement) + .build() + } +}