diff --git a/src/main/java/org/runimo/runimo/hatch/controller/HatchController.java b/src/main/java/org/runimo/runimo/hatch/controller/HatchController.java index c29e2668..77f5284c 100644 --- a/src/main/java/org/runimo/runimo/hatch/controller/HatchController.java +++ b/src/main/java/org/runimo/runimo/hatch/controller/HatchController.java @@ -24,7 +24,8 @@ public class HatchController { @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "[HSH2011] 알 부화 성공"), @ApiResponse(responseCode = "400", description = "[HEH4001] 부화 요청 알이 부화 가능한 상태가 아님"), - @ApiResponse(responseCode = "404", description = "[HEH4041] 부화 요청 알이 존재하지 않음") + @ApiResponse(responseCode = "404", description = "[HEH4041] 부화 요청 알이 존재하지 않음"), + @ApiResponse(responseCode = "500", description = "[HEH5001] [서버 내부 오류] 부화될 러니모 존재하지 않음") }) @PostMapping("/api/v1/incubating-eggs/{incubatingEggId}/hatch") public ResponseEntity> hatch( diff --git a/src/main/java/org/runimo/runimo/hatch/exception/HatchHttpResponseCode.java b/src/main/java/org/runimo/runimo/hatch/exception/HatchHttpResponseCode.java index 617a9582..93fcd23d 100644 --- a/src/main/java/org/runimo/runimo/hatch/exception/HatchHttpResponseCode.java +++ b/src/main/java/org/runimo/runimo/hatch/exception/HatchHttpResponseCode.java @@ -8,6 +8,10 @@ public enum HatchHttpResponseCode implements CustomResponseCode { HATCH_EGG_NOT_READY("HEH4001", "부화 요청 알이 부화 가능한 상태가 아님", "부화 요청 알이 부화 가능한 상태가 아님", HttpStatus.BAD_REQUEST), HATCH_EGG_NOT_FOUND("HEH4041", "부화 요청 알이 존재하지 않음", "부화 요청 알이 존재하지 않음", HttpStatus.NOT_FOUND), + + + // TODO : 다르게 처리할 방법 고민해보기 + HATCH_RUNIMO_NOT_FOUND_INTERNAL_ERROR("HEH5001", "[서버 내부 오류] 부화될 러니모 존재하지 않음", "[서버 내부 오류] 부화될 러니모 존재하지 않음", HttpStatus.INTERNAL_SERVER_ERROR), ; diff --git a/src/main/java/org/runimo/runimo/hatch/service/HatchClient.java b/src/main/java/org/runimo/runimo/hatch/service/HatchClient.java index 894be6be..63bd9204 100644 --- a/src/main/java/org/runimo/runimo/hatch/service/HatchClient.java +++ b/src/main/java/org/runimo/runimo/hatch/service/HatchClient.java @@ -1,15 +1,25 @@ package org.runimo.runimo.hatch.service; -import org.runimo.runimo.item.domain.EggType; -import org.runimo.runimo.runimo.domain.Runimo; +import lombok.RequiredArgsConstructor; +import org.runimo.runimo.hatch.exception.HatchException; +import org.runimo.runimo.hatch.exception.HatchHttpResponseCode; +import org.runimo.runimo.runimo.domain.RunimoDefinition; +import org.runimo.runimo.runimo.repository.RunimoDefinitionRepository; import org.runimo.runimo.user.domain.IncubatingEgg; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; -@Component +@RequiredArgsConstructor +@Service public class HatchClient { + public final RunimoDefinitionRepository runimoDefinitionRepository; // TODO : 로직 구현 - public Runimo getRunimoFromEgg(IncubatingEgg incubatingEgg) { - return new Runimo("토끼_dummy", "R-100", "마당에 사는 토끼 dummy", "http://dummy", EggType.MADANG); + public RunimoDefinition getRunimoDefFromEgg(IncubatingEgg incubatingEgg) { + String dummyRunimoCode = "R-101"; + + RunimoDefinition runimoDefinition = runimoDefinitionRepository.findByCode(dummyRunimoCode) + .orElseThrow(() -> HatchException.of(HatchHttpResponseCode.HATCH_RUNIMO_NOT_FOUND_INTERNAL_ERROR)); + + return runimoDefinition; } } diff --git a/src/main/java/org/runimo/runimo/hatch/service/usecase/HatchUsecaseImpl.java b/src/main/java/org/runimo/runimo/hatch/service/usecase/HatchUsecaseImpl.java index eb4b984b..379a22f3 100644 --- a/src/main/java/org/runimo/runimo/hatch/service/usecase/HatchUsecaseImpl.java +++ b/src/main/java/org/runimo/runimo/hatch/service/usecase/HatchUsecaseImpl.java @@ -5,10 +5,9 @@ import org.runimo.runimo.hatch.exception.HatchException; import org.runimo.runimo.hatch.exception.HatchHttpResponseCode; import org.runimo.runimo.hatch.service.HatchClient; +import org.runimo.runimo.runimo.domain.RunimoDefinition; import org.runimo.runimo.runimo.domain.Runimo; -import org.runimo.runimo.runimo.domain.UserRunimo; import org.runimo.runimo.runimo.repository.RunimoRepository; -import org.runimo.runimo.runimo.repository.UserRunimoRepository; import org.runimo.runimo.user.domain.IncubatingEgg; import org.runimo.runimo.user.repository.IncubatingEggRepository; import org.springframework.stereotype.Service; @@ -18,9 +17,8 @@ @Transactional(readOnly = true) @Service public class HatchUsecaseImpl implements HatchUsecase { - private final RunimoRepository runimoRepository; private final IncubatingEggRepository incubatingEggRepository; - private final UserRunimoRepository userRunimoRepository; + private final RunimoRepository runimoRepository; private final HatchClient hatchClient; @Transactional @@ -31,23 +29,23 @@ public HatchEggResponse execute(Long userId, Long incubatingEggId) { validAndHatchIncubatingEgg(incubatingEgg); // 부화 - 러니모 획득 - Runimo runimo = hatchClient.getRunimoFromEgg(incubatingEgg); - + RunimoDefinition runimoDefinition = hatchClient.getRunimoDefFromEgg(incubatingEgg); // 이미 보유한 러니모인지 확인 - boolean isDuplicatedRunimo = userRunimoRepository.existsByUserIdAndRunimoId(userId, runimo.getId()); - - // 러니모 저장 -> 나중에 다른 요청으로 빼야 할 듯 - Runimo runimoSaved = runimoRepository.save(runimo); - UserRunimo userRunimo = UserRunimo.builder() - .userId(userId) - .runimoId(runimoSaved.getId()) - .build(); - userRunimoRepository.save(userRunimo); + boolean isDuplicatedRunimo = runimoRepository.existsByUserIdAndRunimoDefinitionId(userId, runimoDefinition.getId()); + + // 러니모 저장 (중복 아닐 경우만) + if(!isDuplicatedRunimo){ + Runimo runimo = Runimo.builder() + .userId(userId) + .runimoDefinitionId(runimoDefinition.getId()) + .build(); + runimoRepository.save(runimo); + } return new HatchEggResponse( - runimo.getName(), - runimo.getImgUrl(), - runimo.getCode(), + runimoDefinition.getName(), + runimoDefinition.getImgUrl(), + runimoDefinition.getCode(), isDuplicatedRunimo ); } diff --git a/src/main/java/org/runimo/runimo/runimo/controller/dto/response/GetMyRunimoListResponse.java b/src/main/java/org/runimo/runimo/runimo/controller/dto/response/GetMyRunimoListResponse.java index c7f448d2..7ada18d7 100644 --- a/src/main/java/org/runimo/runimo/runimo/controller/dto/response/GetMyRunimoListResponse.java +++ b/src/main/java/org/runimo/runimo/runimo/controller/dto/response/GetMyRunimoListResponse.java @@ -6,6 +6,6 @@ @Schema(description = "보유 러니모 목록 조회 응답") public record GetMyRunimoListResponse( - List myRunimos + List runimos ) { } diff --git a/src/main/java/org/runimo/runimo/runimo/domain/Runimo.java b/src/main/java/org/runimo/runimo/runimo/domain/Runimo.java index af474d56..0deed12f 100644 --- a/src/main/java/org/runimo/runimo/runimo/domain/Runimo.java +++ b/src/main/java/org/runimo/runimo/runimo/domain/Runimo.java @@ -1,40 +1,46 @@ package org.runimo.runimo.runimo.domain; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import org.runimo.runimo.common.BaseEntity; -import org.runimo.runimo.item.domain.EggType; +import org.runimo.runimo.runimo.exception.RunimoException; +import org.runimo.runimo.runimo.exception.RunimoHttpResponseCode; @Entity @Table(name = "runimo") @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class Runimo extends BaseEntity { - @Column(name = "name") - private String name; - @Column(name = "code", nullable = false) - private String code; + @Column(name = "user_id", nullable = false) + private Long userId; - @Column(name = "description") - private String description; + @Column(name = "runimo_definition_id", nullable = false) + private Long runimoDefinitionId; - @Column(name = "img_url") - private String imgUrl; + @Column(name = "total_run_count", nullable = false) + private Long totalRunCount = 0L; + + @Column(name = "total_distance_in_meters", nullable = false) + private Long totalDistanceInMeters = 0L; - @Enumerated(EnumType.STRING) - @Column(name = "egg_type", nullable = false) - private EggType type; @Builder - public Runimo(String name, String code, String description, String imgUrl, EggType type) { - this.name = name; - this.code = code; - this.description = description; - this.imgUrl = imgUrl; - this.type = type; + private Runimo(Long id, Long userId, Long runimoDefinitionId) { + this.id = id; + this.userId = userId; + this.runimoDefinitionId = runimoDefinitionId; + } + + public void validateOwner(Long userId) { + if(!this.userId.equals(userId)){ + throw RunimoException.of(RunimoHttpResponseCode.USER_DO_NOT_OWN_RUNIMO); + } + } } diff --git a/src/main/java/org/runimo/runimo/runimo/domain/RunimoDefinition.java b/src/main/java/org/runimo/runimo/runimo/domain/RunimoDefinition.java new file mode 100644 index 00000000..6bcd3f31 --- /dev/null +++ b/src/main/java/org/runimo/runimo/runimo/domain/RunimoDefinition.java @@ -0,0 +1,40 @@ +package org.runimo.runimo.runimo.domain; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.runimo.runimo.common.BaseEntity; +import org.runimo.runimo.item.domain.EggType; + +@Entity +@Table(name = "runimo_definition") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class RunimoDefinition extends BaseEntity { + @Column(name = "name") + private String name; + + @Column(name = "code", nullable = false) + private String code; + + @Column(name = "description") + private String description; + + @Column(name = "img_url") + private String imgUrl; + + @Enumerated(EnumType.STRING) + @Column(name = "egg_type", nullable = false) + private EggType type; + + @Builder + public RunimoDefinition(String name, String code, String description, String imgUrl, EggType type) { + this.name = name; + this.code = code; + this.description = description; + this.imgUrl = imgUrl; + this.type = type; + } +} diff --git a/src/main/java/org/runimo/runimo/runimo/domain/UserRunimo.java b/src/main/java/org/runimo/runimo/runimo/domain/UserRunimo.java deleted file mode 100644 index 62fcf978..00000000 --- a/src/main/java/org/runimo/runimo/runimo/domain/UserRunimo.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.runimo.runimo.runimo.domain; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.runimo.runimo.common.BaseEntity; - -@Entity -@Table(name = "user_runimo") -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@Getter -public class UserRunimo extends BaseEntity { - - @Column(name = "user_id", nullable = false) - private Long userId; - - @Column(name = "runimo_id", nullable = false) - private Long runimoId; - - @Builder - private UserRunimo(Long id, Long userId, Long runimoId) { - this.id = id; - this.userId = userId; - this.runimoId = runimoId; - } -} diff --git a/src/main/java/org/runimo/runimo/runimo/repository/RunimoDefinitionRepository.java b/src/main/java/org/runimo/runimo/runimo/repository/RunimoDefinitionRepository.java new file mode 100644 index 00000000..d8918e5f --- /dev/null +++ b/src/main/java/org/runimo/runimo/runimo/repository/RunimoDefinitionRepository.java @@ -0,0 +1,10 @@ +package org.runimo.runimo.runimo.repository; + +import org.runimo.runimo.runimo.domain.RunimoDefinition; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface RunimoDefinitionRepository extends JpaRepository { + Optional findByCode(String runimoCode); +} diff --git a/src/main/java/org/runimo/runimo/runimo/repository/RunimoRepository.java b/src/main/java/org/runimo/runimo/runimo/repository/RunimoRepository.java index 79c23f19..b5fb8802 100644 --- a/src/main/java/org/runimo/runimo/runimo/repository/RunimoRepository.java +++ b/src/main/java/org/runimo/runimo/runimo/repository/RunimoRepository.java @@ -1,7 +1,22 @@ package org.runimo.runimo.runimo.repository; import org.runimo.runimo.runimo.domain.Runimo; +import org.runimo.runimo.runimo.service.model.RunimoSimpleModel; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; public interface RunimoRepository extends JpaRepository { + + boolean existsByUserIdAndRunimoDefinitionId(Long UserId, Long runimoDefinitionId); + + @Query(""" + select new org.runimo.runimo.runimo.service.model.RunimoSimpleModel(r.id, r.name, r.imgUrl, r.code, r.type, r.description) + from Runimo ur + join RunimoDefinition r on r.id = ur.runimoDefinitionId + where ur.userId = :userId + """) + List findAllByUserId(@Param("userId") Long userId); } diff --git a/src/main/java/org/runimo/runimo/runimo/repository/UserRunimoRepository.java b/src/main/java/org/runimo/runimo/runimo/repository/UserRunimoRepository.java deleted file mode 100644 index 6544fd16..00000000 --- a/src/main/java/org/runimo/runimo/runimo/repository/UserRunimoRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.runimo.runimo.runimo.repository; - -import org.runimo.runimo.runimo.domain.UserRunimo; -import org.runimo.runimo.runimo.service.model.RunimoSimpleModel; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -import java.util.List; -import java.util.Optional; - -public interface UserRunimoRepository extends JpaRepository { - - boolean existsByUserIdAndRunimoId(Long UserId, Long runimoId); - - @Query(""" - select new org.runimo.runimo.runimo.service.model.RunimoSimpleModel(r.id, r.name, r.imgUrl, r.code, r.type, r.description) - from UserRunimo ur - join Runimo r on r.id = ur.runimoId - where ur.userId = :userId - """) - List findAllByUserId(@Param("userId") Long userId); -} diff --git a/src/main/java/org/runimo/runimo/runimo/service/usecase/RunimoUsecaseImpl.java b/src/main/java/org/runimo/runimo/runimo/service/usecase/RunimoUsecaseImpl.java index 8c800766..ece3b4d2 100644 --- a/src/main/java/org/runimo/runimo/runimo/service/usecase/RunimoUsecaseImpl.java +++ b/src/main/java/org/runimo/runimo/runimo/service/usecase/RunimoUsecaseImpl.java @@ -4,10 +4,11 @@ import org.runimo.runimo.runimo.controller.dto.response.GetMyRunimoListResponse; import org.runimo.runimo.runimo.controller.dto.response.SetMainRunimoResponse; import org.runimo.runimo.runimo.domain.Runimo; +import org.runimo.runimo.runimo.domain.RunimoDefinition; import org.runimo.runimo.runimo.exception.RunimoException; import org.runimo.runimo.runimo.exception.RunimoHttpResponseCode; +import org.runimo.runimo.runimo.repository.RunimoDefinitionRepository; import org.runimo.runimo.runimo.repository.RunimoRepository; -import org.runimo.runimo.runimo.repository.UserRunimoRepository; import org.runimo.runimo.runimo.service.model.RunimoSimpleModel; import org.runimo.runimo.user.domain.User; import org.runimo.runimo.user.service.UserFinder; @@ -21,12 +22,12 @@ @RequiredArgsConstructor @Service public class RunimoUsecaseImpl implements RunimoUsecase{ + private final RunimoDefinitionRepository runimoDefinitionRepository; private final RunimoRepository runimoRepository; - private final UserRunimoRepository userRunimoRepository; private final UserFinder userFinder; public GetMyRunimoListResponse getMyRunimoList(Long userId) { - List runimos = userRunimoRepository.findAllByUserId(userId); + List runimos = runimoRepository.findAllByUserId(userId); return new GetMyRunimoListResponse(RunimoSimpleModel.toDtoList(runimos)); } @@ -34,7 +35,7 @@ public GetMyRunimoListResponse getMyRunimoList(Long userId) { public SetMainRunimoResponse setMainRunimo(Long userId, Long runimoId) { Runimo runimo = runimoRepository.findById(runimoId) .orElseThrow(() -> RunimoException.of(RunimoHttpResponseCode.RUNIMO_NOT_FOUND)); - validateOwner(userId, runimo.getId()); + runimo.validateOwner(userId); User user = userFinder.findUserById(userId) .orElseThrow(NoSuchElementException::new); @@ -44,9 +45,4 @@ public SetMainRunimoResponse setMainRunimo(Long userId, Long runimoId) { return new SetMainRunimoResponse(runimoId); } - private void validateOwner(Long userId, Long runimoId) { - if(!userRunimoRepository.existsByUserIdAndRunimoId(userId, runimoId)){ - throw RunimoException.of(RunimoHttpResponseCode.USER_DO_NOT_OWN_RUNIMO); - } - } } diff --git a/src/main/resources/sql/schema.sql b/src/main/resources/sql/schema.sql index 98b987be..b42633f7 100644 --- a/src/main/resources/sql/schema.sql +++ b/src/main/resources/sql/schema.sql @@ -5,9 +5,9 @@ DROP TABLE IF EXISTS oauth_account; DROP TABLE IF EXISTS running_record; DROP TABLE IF EXISTS user_item; DROP TABLE IF EXISTS incubator; -DROP TABLE IF EXISTS user_runimo; -DROP TABLE IF EXISTS item_activity; DROP TABLE IF EXISTS runimo; +DROP TABLE IF EXISTS item_activity; +DROP TABLE IF EXISTS runimo_definition; DROP TABLE IF EXISTS item; DROP TABLE IF EXISTS users; DROP TABLE IF EXISTS user_love_point; @@ -131,7 +131,7 @@ CREATE TABLE `incubating_egg` `deleted_at` TIMESTAMP NULL ); -CREATE TABLE `runimo` +CREATE TABLE `runimo_definition` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(255), @@ -144,11 +144,13 @@ CREATE TABLE `runimo` `deleted_at` TIMESTAMP NULL ); -CREATE TABLE `user_runimo` +CREATE TABLE `runimo` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `user_id` BIGINT NOT NULL, - `runimo_id` BIGINT NOT NULL, + `runimo_definition_id` BIGINT NOT NULL, + `total_run_count` BIGINT NOT NULL DEFAULT 0, + `total_distance_in_meters` BIGINT NOT NULL DEFAULT 0, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `deleted_at` TIMESTAMP NULL @@ -159,3 +161,11 @@ ALTER TABLE `user_token` ALTER TABLE `oauth_account` ADD FOREIGN KEY (`user_id`) REFERENCES `users` (`id`); + + +-- insert static data +INSERT INTO runimo_definition (id, name, code, description, img_url, egg_type, created_at, updated_at) +VALUES (1, '토끼', 'R-101', '마당 토끼예여', 'http://dummy1', 'MADANG', NOW(), NOW()), + (2, '강아지', 'R-102', '마당 강아지예여', 'http://dummy2', 'MADANG', NOW(), NOW()), + (3, '오리', 'R-103', '마당 오리예여', 'http://dummy3', 'MADANG', NOW(), NOW()), + (4, '늑대', 'R-104', '주인이 다른 마당 늑대예여', 'http://dummy4', 'MADANG', NOW(), NOW()); \ No newline at end of file diff --git a/src/test/java/org/runimo/runimo/CleanUpUtil.java b/src/test/java/org/runimo/runimo/CleanUpUtil.java index cfabc546..18db1831 100644 --- a/src/test/java/org/runimo/runimo/CleanUpUtil.java +++ b/src/test/java/org/runimo/runimo/CleanUpUtil.java @@ -14,8 +14,8 @@ public class CleanUpUtil { "running_record", "user_love_point", "incubating_egg", + "runimo_definition", "runimo", - "user_runimo", }; @Autowired diff --git a/src/test/java/org/runimo/runimo/hatch/controller/HatchControllerTest.java b/src/test/java/org/runimo/runimo/hatch/controller/HatchControllerTest.java index d347f6da..d02d8ea7 100644 --- a/src/test/java/org/runimo/runimo/hatch/controller/HatchControllerTest.java +++ b/src/test/java/org/runimo/runimo/hatch/controller/HatchControllerTest.java @@ -24,91 +24,91 @@ @ActiveProfiles("test") class HatchControllerTest { - @LocalServerPort - int port; + @LocalServerPort + int port; - @Autowired - private JwtTokenFactory jwtTokenFactory; + @Autowired + private JwtTokenFactory jwtTokenFactory; - @Autowired - private CleanUpUtil cleanUpUtil; + @Autowired + private CleanUpUtil cleanUpUtil; - @Autowired - private ObjectMapper objectMapper; + @Autowired + private ObjectMapper objectMapper; - @BeforeEach - void setUp() { - RestAssured.port = port; - } + @BeforeEach + void setUp() { + RestAssured.port = port; + } - @AfterEach - void tearDown() { - cleanUpUtil.cleanUpUserInfos(); - } + @AfterEach + void tearDown() { + cleanUpUtil.cleanUpUserInfos(); + } - @Test - @Sql(scripts = "/sql/hatch_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) - void 사용자의_알_부화_성공() { - String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); + @Test + @Sql(scripts = "/sql/hatch_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + void 사용자의_알_부화_성공() { + String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); - given() - .header("Authorization", token) - .contentType(ContentType.JSON) + given() + .header("Authorization", token) + .contentType(ContentType.JSON) - .when() - .post("/api/v1/incubating-eggs/" + "1" + "/hatch") + .when() + .post("/api/v1/incubating-eggs/" + "1" + "/hatch") - .then() - .log().all() - .statusCode(HttpStatus.CREATED.value()) + .then() + .log().all() + .statusCode(HttpStatus.CREATED.value()) - .body("code", equalTo("HSH2011")) - .body("payload.name", equalTo("토끼_dummy")) - .body("payload.img_url", equalTo("http://dummy")) - .body("payload.code", equalTo("R-100")) - .body("payload.is_duplicated", equalTo(false)); - } + .body("code", equalTo("HSH2011")) + .body("payload.name", equalTo("토끼")) + .body("payload.img_url", equalTo("http://dummy1")) + .body("payload.code", equalTo("R-101")) + .body("payload.is_duplicated", equalTo(false)); + } - @Test - @Sql(scripts = "/sql/hatch_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) - void 사용자의_알_부화_실패_부화가능한_상태가_아님() { - String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); - CustomResponseCode responseCode = HatchHttpResponseCode.HATCH_EGG_NOT_READY; + @Test + @Sql(scripts = "/sql/hatch_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + void 사용자의_알_부화_실패_부화가능한_상태가_아님() { + String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); + CustomResponseCode responseCode = HatchHttpResponseCode.HATCH_EGG_NOT_READY; - given() - .header("Authorization", token) - .contentType(ContentType.JSON) + given() + .header("Authorization", token) + .contentType(ContentType.JSON) - .when() - .post("/api/v1/incubating-eggs/" + "2" + "/hatch") + .when() + .post("/api/v1/incubating-eggs/" + "2" + "/hatch") - .then() - .log().all() - .statusCode(responseCode.getHttpStatusCode().value()) + .then() + .log().all() + .statusCode(responseCode.getHttpStatusCode().value()) - .body("code", equalTo(responseCode.getCode())) - .body("message", equalTo(responseCode.getClientMessage())); - } + .body("code", equalTo(responseCode.getCode())) + .body("message", equalTo(responseCode.getClientMessage())); + } - @Test - @Sql(scripts = "/sql/hatch_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) - void 사용자의_알_부화_실패_알_존재하지_않음() { - String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); - CustomResponseCode responseCode = HatchHttpResponseCode.HATCH_EGG_NOT_FOUND; + @Test + @Sql(scripts = "/sql/hatch_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + void 사용자의_알_부화_실패_알_존재하지_않음() { + String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); + CustomResponseCode responseCode = HatchHttpResponseCode.HATCH_EGG_NOT_FOUND; - given() - .header("Authorization", token) - .contentType(ContentType.JSON) + given() + .header("Authorization", token) + .contentType(ContentType.JSON) - .when() - .post("/api/v1/incubating-eggs/" + "9999" + "/hatch") + .when() + .post("/api/v1/incubating-eggs/" + "9999" + "/hatch") - .then() - .log().all() - .statusCode(responseCode.getHttpStatusCode().value()) + .then() + .log().all() + .statusCode(responseCode.getHttpStatusCode().value()) - .body("code", equalTo(responseCode.getCode())) - .body("message", equalTo(responseCode.getClientMessage())); - } + .body("code", equalTo(responseCode.getCode())) + .body("message", equalTo(responseCode.getClientMessage())); + } } \ No newline at end of file diff --git a/src/test/java/org/runimo/runimo/runimo/controller/RunimoControllerTest.java b/src/test/java/org/runimo/runimo/runimo/controller/RunimoControllerTest.java index 6fb50e6e..0b6121a4 100644 --- a/src/test/java/org/runimo/runimo/runimo/controller/RunimoControllerTest.java +++ b/src/test/java/org/runimo/runimo/runimo/controller/RunimoControllerTest.java @@ -19,120 +19,123 @@ import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") class RunimoControllerTest { - @LocalServerPort - int port; + @LocalServerPort + int port; - @Autowired - private JwtTokenFactory jwtTokenFactory; + @Autowired + private JwtTokenFactory jwtTokenFactory; - @Autowired - private CleanUpUtil cleanUpUtil; + @Autowired + private CleanUpUtil cleanUpUtil; - @Autowired - private ObjectMapper objectMapper; + @Autowired + private ObjectMapper objectMapper; - @BeforeEach - void setUp() { - RestAssured.port = port; - } + @BeforeEach + void setUp() { + RestAssured.port = port; + } - @AfterEach - void tearDown() { - cleanUpUtil.cleanUpUserInfos(); - } + @AfterEach + void tearDown() { + cleanUpUtil.cleanUpUserInfos(); + } - @Test - @Sql(scripts = "/sql/get_my_runimo_list_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) - void 보유_러니모_목록_조회_성공() { - String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); + @Test + @Sql(scripts = "/sql/get_my_runimo_list_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + void 보유_러니모_목록_조회_성공() { + String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); - given() - .header("Authorization", token) - .contentType(ContentType.JSON) + given() + .header("Authorization", token) + .contentType(ContentType.JSON) - .when() - .get("/api/v1/runimos/my") + .when() + .get("/api/v1/runimos/my") - .then() - .log().all() - .statusCode(HttpStatus.OK.value()) + .then() + .log().all() + .statusCode(HttpStatus.OK.value()) - .body("code", equalTo("MSH2001")) - .body("payload.my_runimos[0].id", equalTo(1)) - .body("payload.my_runimos[0].name", equalTo("토끼")) - .body("payload.my_runimos[0].img_url", equalTo("http://dummy1")) - .body("payload.my_runimos[0].code", equalTo("R-101")) - .body("payload.my_runimos[0].egg_type", equalTo("MADANG")) - .body("payload.my_runimos[0].description", equalTo("마당 토끼예여")); - } + .body("code", equalTo("MSH2001")) + .body("payload.runimos", hasSize(3)) - @Test - @Sql(scripts = "/sql/set_main_runimo_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) - void 대표_러니모_설정_성공() { - String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); + .body("payload.runimos[0].id", equalTo(1)) + .body("payload.runimos[0].name", equalTo("토끼")) + .body("payload.runimos[0].img_url", equalTo("http://dummy1")) + .body("payload.runimos[0].code", equalTo("R-101")) + .body("payload.runimos[0].egg_type", equalTo("MADANG")) + .body("payload.runimos[0].description", equalTo("마당 토끼예여")); + } + @Test + @Sql(scripts = "/sql/set_main_runimo_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + void 대표_러니모_설정_성공() { + String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); - given() - .header("Authorization", token) - .contentType(ContentType.JSON) - .when() - .patch("/api/v1/runimos/" + "1" + "/main") + given() + .header("Authorization", token) + .contentType(ContentType.JSON) - .then() - .log().all() - .statusCode(HttpStatus.OK.value()) + .when() + .patch("/api/v1/runimos/" + "1" + "/main") - .body("code", equalTo("MSH2002")) - .body("payload.main_runimo_id", equalTo(1)); - } + .then() + .log().all() + .statusCode(HttpStatus.OK.value()) - @Test - @Sql(scripts = "/sql/set_main_runimo_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) - void 대표_러니모_설정_실패_러니모의_소유자_아님() { - String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); - CustomResponseCode responseCode = RunimoHttpResponseCode.USER_DO_NOT_OWN_RUNIMO; + .body("code", equalTo("MSH2002")) + .body("payload.main_runimo_id", equalTo(1)); + } - given() - .header("Authorization", token) - .contentType(ContentType.JSON) + @Test + @Sql(scripts = "/sql/set_main_runimo_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + void 대표_러니모_설정_실패_러니모의_소유자_아님() { + String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); + CustomResponseCode responseCode = RunimoHttpResponseCode.USER_DO_NOT_OWN_RUNIMO; - .when() - .patch("/api/v1/runimos/" + "4" + "/main") + given() + .header("Authorization", token) + .contentType(ContentType.JSON) - .then() - .log().all() - .statusCode(responseCode.getHttpStatusCode().value()) + .when() + .patch("/api/v1/runimos/" + "4" + "/main") - .body("code", equalTo(responseCode.getCode())) - .body("message", equalTo(responseCode.getClientMessage())); - } + .then() + .log().all() + .statusCode(responseCode.getHttpStatusCode().value()) - @Test - @Sql(scripts = "/sql/set_main_runimo_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) - void 대표_러니모_설정_실패_러니모_존재하지않음() { - String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); - CustomResponseCode responseCode = RunimoHttpResponseCode.RUNIMO_NOT_FOUND; + .body("code", equalTo(responseCode.getCode())) + .body("message", equalTo(responseCode.getClientMessage())); + } - given() - .header("Authorization", token) - .contentType(ContentType.JSON) + @Test + @Sql(scripts = "/sql/set_main_runimo_test_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + void 대표_러니모_설정_실패_러니모_존재하지않음() { + String token = "Bearer " + jwtTokenFactory.generateAccessToken("test-user-uuid-1"); + CustomResponseCode responseCode = RunimoHttpResponseCode.RUNIMO_NOT_FOUND; - .when() - .patch("/api/v1/runimos/" + "9999" + "/main") + given() + .header("Authorization", token) + .contentType(ContentType.JSON) - .then() - .log().all() - .statusCode(responseCode.getHttpStatusCode().value()) + .when() + .patch("/api/v1/runimos/" + "9999" + "/main") - .body("code", equalTo(responseCode.getCode())) - .body("message", equalTo(responseCode.getClientMessage())); - } + .then() + .log().all() + .statusCode(responseCode.getHttpStatusCode().value()) + + .body("code", equalTo(responseCode.getCode())) + .body("message", equalTo(responseCode.getClientMessage())); + } } \ No newline at end of file diff --git a/src/test/resources/sql/get_my_runimo_list_test_data.sql b/src/test/resources/sql/get_my_runimo_list_test_data.sql index 55165749..02ef4fc5 100644 --- a/src/test/resources/sql/get_my_runimo_list_test_data.sql +++ b/src/test/resources/sql/get_my_runimo_list_test_data.sql @@ -8,16 +8,16 @@ VALUES (1, 'test-user-uuid-1', 'Daniel', 'https://example.com/images/user1.png', SET FOREIGN_KEY_CHECKS = 1; -- 러니모 -TRUNCATE TABLE runimo; -INSERT INTO runimo (id, name, code, description, img_url, egg_type, created_at, updated_at) -VALUES (1, '토끼', 'R-101', '마당 토끼예여', 'http://dummy1', 'MADANG', NOW(), NOW()); -VALUES (2, '강아지', 'R-102', '마당 강아지예여', 'http://dummy2', 'MADANG', NOW(), NOW()); -VALUES (3, '오리', 'R-103', '마당 오리예여', 'http://dummy3', 'MADANG', NOW(), NOW()); -VALUES (4, '늑대', 'R-104', '마당 늑대예여', 'http://dummy4', 'MADANG', NOW(), NOW()); +TRUNCATE TABLE runimo_definition; +INSERT INTO runimo_definition (id, name, code, description, img_url, egg_type, created_at, updated_at) +VALUES (1, '토끼', 'R-101', '마당 토끼예여', 'http://dummy1', 'MADANG', NOW(), NOW()), + (2, '강아지', 'R-102', '마당 강아지예여', 'http://dummy2', 'MADANG', NOW(), NOW()), + (3, '오리', 'R-103', '마당 오리예여', 'http://dummy3', 'MADANG', NOW(), NOW()), + (4, '늑대', 'R-104', '주인이 다른 마당 늑대예여', 'http://dummy4', 'MADANG', NOW(), NOW()); -- 사용자-알 맵핑 -TRUNCATE TABLE user_runimo; -INSERT INTO user_runimo (id, user_id, runimo_id, created_at, updated_at) +TRUNCATE TABLE runimo; +INSERT INTO runimo (id, user_id, runimo_definition_id, created_at, updated_at) VALUES (1, 1, 1, NOW(), NOW()), (2, 1, 2, NOW(), NOW()), (3, 1, 3, NOW(), NOW()), diff --git a/src/test/resources/sql/hatch_test_data.sql b/src/test/resources/sql/hatch_test_data.sql index f4b1ae6c..89e625a3 100644 --- a/src/test/resources/sql/hatch_test_data.sql +++ b/src/test/resources/sql/hatch_test_data.sql @@ -11,4 +11,16 @@ SET FOREIGN_KEY_CHECKS = 1; TRUNCATE TABLE incubating_egg; INSERT INTO incubating_egg (id, user_id, egg_id, current_love_point_amount, hatch_require_amount, egg_status, created_at, updated_at) VALUES (1, 1, 1, 100, 100, 'INCUBATED', NOW(), NOW()), - (2, 1, 2, 50, 100, 'INCUBATING', NOW(), NOW()); \ No newline at end of file + (2, 1, 2, 50, 100, 'INCUBATING', NOW(), NOW()); + +-- 러니모 +TRUNCATE TABLE runimo_definition; +INSERT INTO runimo_definition (id, name, code, description, img_url, egg_type, created_at, updated_at) +VALUES (1, '토끼', 'R-101', '마당 토끼예여', 'http://dummy1', 'MADANG', NOW(), NOW()), + (2, '강아지', 'R-102', '이미 소유하고 있는 마당 강아지예여', 'http://dummy2', 'MADANG', NOW(), NOW()); + + +-- 사용자-러니모 맵핑 +TRUNCATE TABLE runimo; +INSERT INTO runimo (id, user_id, runimo_definition_id, created_at, updated_at) +VALUES (1, 1, 2, NOW(), NOW()); \ No newline at end of file diff --git a/src/test/resources/sql/schema.sql b/src/test/resources/sql/schema.sql index ff7cb380..6cb6f898 100644 --- a/src/test/resources/sql/schema.sql +++ b/src/test/resources/sql/schema.sql @@ -5,9 +5,9 @@ DROP TABLE IF EXISTS oauth_account; DROP TABLE IF EXISTS running_record; DROP TABLE IF EXISTS user_item; DROP TABLE IF EXISTS incubator; -DROP TABLE IF EXISTS user_runimo; -DROP TABLE IF EXISTS item_activity; DROP TABLE IF EXISTS runimo; +DROP TABLE IF EXISTS item_activity; +DROP TABLE IF EXISTS runimo_definition; DROP TABLE IF EXISTS item; DROP TABLE IF EXISTS users; DROP TABLE IF EXISTS user_love_point; @@ -132,7 +132,7 @@ CREATE TABLE `incubating_egg` `deleted_at` TIMESTAMP NULL ); -CREATE TABLE `runimo` +CREATE TABLE `runimo_definition` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(255), @@ -145,11 +145,13 @@ CREATE TABLE `runimo` `deleted_at` TIMESTAMP NULL ); -CREATE TABLE `user_runimo` +CREATE TABLE `runimo` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `user_id` BIGINT NOT NULL, - `runimo_id` BIGINT NOT NULL, + `runimo_definition_id` BIGINT NOT NULL, + `total_run_count` BIGINT NOT NULL DEFAULT 0, + `total_distance_in_meters` BIGINT NOT NULL DEFAULT 0, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `deleted_at` TIMESTAMP NULL diff --git a/src/test/resources/sql/set_main_runimo_test_data.sql b/src/test/resources/sql/set_main_runimo_test_data.sql index b1e54fa1..e2fc7dcb 100644 --- a/src/test/resources/sql/set_main_runimo_test_data.sql +++ b/src/test/resources/sql/set_main_runimo_test_data.sql @@ -8,16 +8,17 @@ VALUES (1, 'test-user-uuid-1', 'Daniel', 'https://example.com/images/user1.png', SET FOREIGN_KEY_CHECKS = 1; -- 러니모 -TRUNCATE TABLE runimo; -INSERT INTO runimo (id, name, code, description, img_url, egg_type, created_at, updated_at) +TRUNCATE TABLE runimo_definition; +INSERT INTO runimo_definition (id, name, code, description, img_url, egg_type, created_at, updated_at) VALUES (1, '토끼', 'R-101', '마당 토끼예여', 'http://dummy1', 'MADANG', NOW(), NOW()), (2, '강아지', 'R-102', '마당 강아지예여', 'http://dummy2', 'MADANG', NOW(), NOW()), (3, '오리', 'R-103', '마당 오리예여', 'http://dummy3', 'MADANG', NOW(), NOW()), - (4, '늑대', 'R-104', '주인없는 마당 늑대예여', 'http://dummy4', 'MADANG', NOW(), NOW()); + (4, '늑대', 'R-104', '주인이 다른 마당 늑대예여', 'http://dummy4', 'MADANG', NOW(), NOW()); -- 사용자-러니모 맵핑 -TRUNCATE TABLE user_runimo; -INSERT INTO user_runimo (id, user_id, runimo_id, created_at, updated_at) +TRUNCATE TABLE runimo; +INSERT INTO runimo (id, user_id, runimo_definition_id, created_at, updated_at) VALUES (1, 1, 1, NOW(), NOW()), (2, 1, 2, NOW(), NOW()), - (3, 1, 3, NOW(), NOW()); \ No newline at end of file + (3, 1, 3, NOW(), NOW()), + (4, 2, 4, NOW(), NOW()); \ No newline at end of file