Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] : 입찰 등록, 단건 조회, 3개 조회 API #32

Merged
merged 18 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
8dbb3a2
[refactor] : UserFixture 메서드 네이밍 변경 외
ParkJuhan94 Feb 21, 2024
95ba699
[feat] : Auction 필드 속성 변경과 AuctionFixture 세팅
ParkJuhan94 Feb 21, 2024
215dafe
[chore] : 테스트 yml 의 jwt secret 변경
ParkJuhan94 Feb 21, 2024
274f63c
[feat] : Bidding 엔티티와 ErrodCode 추가
ParkJuhan94 Feb 21, 2024
fa3b974
[feat] : BiddingApiController 와 맵퍼, 요청 dto 추가
ParkJuhan94 Feb 21, 2024
3853daf
[feat] : Bidding 서비스 & 레포지토리 & 맵퍼 & dto 추가 + 테스트
ParkJuhan94 Feb 21, 2024
6cb0647
[feat] : ApiTestSupport 에 유저랑 인증 setUp 추가
ParkJuhan94 Feb 22, 2024
290e7b9
[refactor] : 자잘한 변경
ParkJuhan94 Feb 22, 2024
1f3d498
[refactor] : Bidding dto 관련 수정
ParkJuhan94 Feb 22, 2024
0fd4771
[feat] : 테스트 실패하는 BiddingApiControllerTest
ParkJuhan94 Feb 22, 2024
2032f5d
Merge branch 'dev' into feat/#27/create-bidding-api
ParkJuhan94 Feb 22, 2024
b4d12ba
[feat] : Auction 엔티티에 테스트용 새성자 추가 & 불필요한 Assert 제거
ParkJuhan94 Feb 22, 2024
1ecbdc1
[feat] : AuctionErrorCode 추가와 AuctionFixture 리팩토링과 생성자 추가
ParkJuhan94 Feb 22, 2024
b5b6470
[feat] : AuctionService 에 단건조회 추가와 테스트 구현
ParkJuhan94 Feb 22, 2024
af3fdf3
[feat] : BiddingApiController 와 테스트 구현
ParkJuhan94 Feb 22, 2024
5741188
[refactor] : BiddingMapper 정리와 RegisterBiddingRequest 필드 변경
ParkJuhan94 Feb 22, 2024
1c0d178
[feat] : BiddingRepositoryTest 추가와 DataJpaTestSupport 에 em 추가
ParkJuhan94 Feb 22, 2024
5c2d9d4
[feat] : BiddingService 와 테스트 추가
ParkJuhan94 Feb 22, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package dev.handsup.bidding.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import dev.handsup.auth.jwt.JwtAuthorization;
import dev.handsup.bidding.dto.request.RegisterBiddingRequest;
import dev.handsup.bidding.dto.response.RegisterBiddingResponse;
import dev.handsup.bidding.service.BiddingService;
import dev.handsup.user.domain.User;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;

@Tag(name = "Bidding API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/auctions")
public class BiddingApiController {

private final BiddingService biddingService;

@PostMapping("/{auctionId}/bids")
@Operation(summary = "입찰 등록 API", description = "입찰한다")
@ApiResponse(useReturnTypeSchema = true)
public ResponseEntity<RegisterBiddingResponse> registerBidding(
@PathVariable Long auctionId,
@RequestBody
@NotNull(message = "biddingPrice 값이 공백입니다.")
@Max(value = 1_000_000_000, message = "최대 입찰가는 10억입니다.")
int biddingPrice,
@Parameter(hidden = true) @JwtAuthorization User user
hyun2371 marked this conversation as resolved.
Show resolved Hide resolved
) {
RegisterBiddingRequest request = RegisterBiddingRequest.of(
biddingPrice,
auctionId,
user
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

biddingPrice만 validation이랑 같이 request로 빼고 auctionId, user는 service에 그대로 넘겨줘도 괜찮을 것 같아요!

);
RegisterBiddingResponse response = biddingService.registerBidding(request);
return ResponseEntity.ok(response);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

service.registerBidding(request) 만 작성하고 나머지는 서비스단으로 넘겨야 할 것 같습니다.

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public ResponseEntity<JoinUserResponse> join(
) {
JoinUserRequest joinUserRequest = UserApiMapper.toJoinUserRequest(request);
Long userId = userService.join(joinUserRequest);
return ResponseEntity.ok(new JoinUserResponse(userId));
JoinUserResponse response = JoinUserResponse.from(userId);
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class JwtAuthorizationArgumentResolverTest {
@DisplayName("[토큰이 유효하면 User 엔티티를 반환한다]")
void shouldResolveArgumentWithValidToken() {
// given
User user = UserFixture.testUser(1L);
User user = UserFixture.user(1L);
mockHttpServletRequest.addHeader(HttpHeaders.AUTHORIZATION, "validToken");
when(userService.getUserById(user.getId())).thenReturn(user);
when(webRequest.getNativeRequest(HttpServletRequest.class)).thenReturn(mockHttpServletRequest);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
import dev.handsup.user.dto.request.JoinUserRequest;
import dev.handsup.user.service.UserService;

@DisplayName("[AuthApiController API 테스트]")
@DisplayName("[AuthApiController 테스트]")
class AuthApiControllerTest extends ApiTestSupport {

private final User user = UserFixture.testUser(1L);
private final User user = UserFixture.user(1L);
@Autowired
private UserService userService;
@Autowired
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package dev.handsup.bidding.controller;

import static org.springframework.http.HttpHeaders.*;
import static org.springframework.http.MediaType.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

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.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import dev.handsup.auction.domain.Auction;
import dev.handsup.auction.domain.product.product_category.ProductCategory;
import dev.handsup.auction.repository.auction.AuctionRepository;
import dev.handsup.auction.repository.product.ProductCategoryRepository;
import dev.handsup.common.support.ApiTestSupport;
import dev.handsup.fixture.AuctionFixture;
import dev.handsup.fixture.ProductFixture;
import dev.handsup.fixture.UserFixture;
import dev.handsup.user.domain.User;
import dev.handsup.user.repository.UserRepository;

@DisplayName("[BiddingApiController 테스트]")
class BiddingApiControllerTest extends ApiTestSupport {

@Autowired
private AuctionRepository auctionRepository;
@Autowired
private ProductCategoryRepository productCategoryRepository;
@Autowired
private UserRepository userRepository;
private final String DIGITAL_DEVICE = "디지털 기기";
private final User user = UserFixture.user();
private Auction auction;

@BeforeEach
void setUp() {
ProductCategory productCategory = ProductFixture.productCategory(DIGITAL_DEVICE);
productCategoryRepository.save(productCategory);
auction = auctionRepository.save(AuctionFixture.auction(productCategory));
// 전체 테스트시 user 가 db 에서 삭제되는 오류
userRepository.save(user);
}

@Test
@DisplayName("[입찰 등록 API를 호출하면 입찰이 등록되고 입찰을 응답한다]")
void registerBiddingTest() throws Exception {
// when
ResultActions resultActions = mockMvc.perform(
MockMvcRequestBuilders
.post("/api/auctions/{auctionId}/bids", auction.getId())
.header(AUTHORIZATION, accessToken)
.contentType(APPLICATION_JSON)
.content(toJson(10000))
);

// then
resultActions.andExpectAll(
status().isOk(),
jsonPath("$.biddingPrice").value(10000),
jsonPath("$.auctionId").value(auction.getId()),
jsonPath("$.bidderId").value(user.getId())
);
}

}
64 changes: 64 additions & 0 deletions api/src/test/java/dev/handsup/common/support/ApiTestSupport.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
package dev.handsup.common.support;

import static org.springframework.http.MediaType.*;

import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import dev.handsup.auth.dto.request.AuthRequest;
import dev.handsup.auth.dto.response.AuthResponse;
import dev.handsup.fixture.UserFixture;
import dev.handsup.support.DatabaseCleaner;
import dev.handsup.support.DatabaseCleanerExtension;
import dev.handsup.support.TestContainerSupport;
import dev.handsup.user.domain.User;
import dev.handsup.user.dto.request.JoinUserRequest;
import dev.handsup.user.repository.UserRepository;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@SpringBootTest
@AutoConfigureMockMvc
@Import(DatabaseCleaner.class)
Expand All @@ -26,7 +39,58 @@ public abstract class ApiTestSupport extends TestContainerSupport {
@Autowired
protected ObjectMapper objectMapper;

@Autowired
protected UserRepository userRepository;

protected static String accessToken;
protected static String refreshToken;
protected String toJson(Object object) throws JsonProcessingException {
return objectMapper.writeValueAsString(object);
}

@PostConstruct
public void setUpUser() throws Exception {
// 캐싱해서 단 한번만 호출
if (accessToken != null && refreshToken != null) {
return;
}

User user = UserFixture.user();
JoinUserRequest joinUserRequest = JoinUserRequest.of(
user.getEmail(),
user.getPassword(),
user.getNickname(),
user.getAddress().getSi(),
user.getAddress().getGu(),
user.getAddress().getDong(),
user.getProfileImageUrl()
);

mockMvc.perform(
MockMvcRequestBuilders
.post("/api/users")
.contentType(APPLICATION_JSON)
.content(toJson(joinUserRequest))
);

AuthRequest authRequest = AuthRequest.of(
joinUserRequest.email(),
joinUserRequest.password()
);
MvcResult loginResult = mockMvc.perform(
MockMvcRequestBuilders
.post("/api/auth/login")
.contentType(APPLICATION_JSON)
.content(toJson(authRequest))
).andReturn();

String stringLoginResponse = loginResult.getResponse().getContentAsString();
AuthResponse authResponse = objectMapper.readValue(stringLoginResponse, AuthResponse.class);

accessToken = authResponse.accessToken();
refreshToken = authResponse.refreshToken();

log.info("setUpUser() is finished.");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,22 @@
import dev.handsup.user.dto.request.JoinUserRequest;
import dev.handsup.user.repository.UserRepository;

@DisplayName("[UserController API 테스트]")
@DisplayName("[UserApiController 테스트]")
class UserApiControllerTest extends ApiTestSupport {

@Autowired
private UserRepository userRepository;

private User user = UserFixture.user();
private JoinUserRequest request = new JoinUserRequest(
user.getEmail(), user.getPassword(), user.getNickname(),
user.getAddress().getSi(), user.getAddress().getGu(), user.getAddress().getDong(), user.getProfileImageUrl()

private JoinUserRequest request = JoinUserRequest.of(
user.getEmail(),
user.getPassword(),
user.getNickname(),
user.getAddress().getSi(),
user.getAddress().getGu(),
user.getAddress().getDong(),
user.getProfileImageUrl()
);

@Test
Expand Down
2 changes: 1 addition & 1 deletion api/src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ logging.level:
org.hibernate.orm.jdbc.bind: trace

jwt:
secret: prgrmsdevcoursehandsupjsonwebtokensecretkey202402
secret: prgrmsdevcoursehandsupjsonwebtokensecretkeytest123
token-validity-in-seconds: 864000
49 changes: 39 additions & 10 deletions core/src/main/java/dev/handsup/auction/domain/Auction.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.handsup.auction.domain;

import static dev.handsup.auction.domain.auction_field.AuctionStatus.*;
import static dev.handsup.common.exception.CommonValidationError.*;
import static jakarta.persistence.CascadeType.*;
import static jakarta.persistence.ConstraintMode.*;
Expand Down Expand Up @@ -40,7 +41,7 @@
@NoArgsConstructor(access = PROTECTED)
public class Auction extends TimeBaseEntity {

private static final String AUCTION = "auction";
private static final String AUCTION_STRING = "auction";

@Id
@GeneratedValue(strategy = IDENTITY)
Expand All @@ -61,7 +62,10 @@ public class Auction extends TimeBaseEntity {
private String title;

@OneToOne(fetch = LAZY, cascade = ALL)
@JoinColumn(name = "product_id", foreignKey = @ForeignKey(NO_CONSTRAINT))
@JoinColumn(
name = "product_id",
nullable = false,
foreignKey = @ForeignKey(NO_CONSTRAINT))
private Product product;

@Column(name = "init_price", nullable = false)
Expand All @@ -79,28 +83,24 @@ public class Auction extends TimeBaseEntity {

@Column(name = "auction_status", nullable = false)
@Enumerated(STRING)
private AuctionStatus status;
private AuctionStatus status = PROGRESS;

@Column(name = "bidding_count", nullable = false)
private int biddingCount;
private int biddingCount = 0;

@Column(name = "bookmark_count", nullable = false)
private int bookmarkCount;
private int bookmarkCount = 0;

@Builder
private Auction(String title, Product product, int initPrice, LocalDate endDate, TradingLocation tradingLocation,
TradeMethod tradeMethod) {
Assert.hasText(title, getNotEmptyMessage(AUCTION, "title"));
Assert.notNull(title, getNotEmptyMessage(AUCTION, "initPrice"));
Assert.hasText(title, getNotEmptyMessage(AUCTION_STRING, "title"));
this.title = title;
this.product = product;
this.initPrice = initPrice;
this.endDate = endDate;
this.tradingLocation = tradingLocation;
this.tradeMethod = tradeMethod;
biddingCount = 0;
bookmarkCount = 0;
status = AuctionStatus.PROGRESS;
}

public static Auction of(String title, ProductCategory productCategory, int initPrice, LocalDate endDate,
Expand All @@ -116,6 +116,35 @@ public static Auction of(String title, ProductCategory productCategory, int init
.build();
}

// 테스트용 생성자
private Auction(Long id, User seller, String title, Product product, int initPrice, LocalDate endDate,
TradingLocation tradingLocation, TradeMethod tradeMethod) {
Assert.hasText(title, getNotEmptyMessage(AUCTION_STRING, "title"));
this.id = id;
this.seller = seller;
this.title = title;
this.product = product;
this.initPrice = initPrice;
this.endDate = endDate;
this.tradingLocation = tradingLocation;
this.tradeMethod = tradeMethod;
}

public static Auction of(Long id, User seller, String title, ProductCategory productCategory, int initPrice,
LocalDate endDate, ProductStatus status, PurchaseTime purchaseTime, String description,
TradeMethod tradeMethod, String si, String gu, String dong) {
return new Auction(
id,
seller,
title,
Product.of(status, description, purchaseTime, productCategory),
initPrice,
endDate,
TradingLocation.of(si, gu, dong),
tradeMethod
);
}

public void changeAuctionStatusTrading() {
status = AuctionStatus.TRADING;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ public enum AuctionErrorCode implements ErrorCode {
NOT_FOUND_PRODUCT_STATUS("올바른 상품 상태를 입력해주세요", "AU_001"),
NOT_FOUND_PURCHASE_TIME("올바른 구매 시기를 입력해주세요", "AU_002"),
NOT_FOUND_TADE_METHOD("올바른 거래 방법을 입력해주세요", "AU_003"),
NOT_FOUND_PRODUCT_CATEGORY("존재하지 않는 상품 카테고리입니다.", "AU_004");
NOT_FOUND_PRODUCT_CATEGORY("존재하지 않는 상품 카테고리입니다.", "AU_004"),
NOT_FOUND_AUCTION("존재하지 않는 경매입니다.", "AU_005");

private final String message;
private final String code;
Expand Down
Loading
Loading