카드 매입 승인 경로 Spring Boot 3 전환 #12
8 changed files with 551 additions and 0 deletions
79
proj-acquirex/pom.xml
Normal file
79
proj-acquirex/pom.xml
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>com.acquirex</groupId>
|
||||
<artifactId>proj-acquirex</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>proj-acquirex</name>
|
||||
<description>카드 매입 승인 경로 - Spring Boot 3 Migration</description>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.2.5</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.acquirex;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class AcquirexApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AcquirexApplication.class, args);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package com.acquirex.approval.controller;
|
||||
|
||||
import com.acquirex.approval.domain.AcquisitionApproval.ApprovalStatus;
|
||||
import com.acquirex.approval.service.AcquisitionApprovalService;
|
||||
import com.acquirex.approval.service.AcquisitionApprovalService.ApprovalRequest;
|
||||
import com.acquirex.approval.service.AcquisitionApprovalService.ApprovalResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** 카드 매입 승인 REST 컨트롤러 - Spring Boot 3: jakarta.* 네임스페이스 적용 */
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/acquisitions")
|
||||
public class AcquisitionApprovalController {
|
||||
|
||||
private final AcquisitionApprovalService service;
|
||||
|
||||
public AcquisitionApprovalController(AcquisitionApprovalService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@PostMapping("/approvals")
|
||||
public ResponseEntity<ApprovalResponse> createApproval(@Valid @RequestBody ApprovalRequest request) {
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(service.processApproval(request));
|
||||
}
|
||||
|
||||
@GetMapping("/approvals/{approvalNumber}")
|
||||
public ResponseEntity<ApprovalResponse> getApproval(@PathVariable String approvalNumber) {
|
||||
return ResponseEntity.ok(service.findByApprovalNumber(approvalNumber));
|
||||
}
|
||||
|
||||
@GetMapping("/merchants/{merchantId}/approvals")
|
||||
public ResponseEntity<List<ApprovalResponse>> getApprovalsByMerchant(
|
||||
@PathVariable String merchantId, @RequestParam(required = false) ApprovalStatus status) {
|
||||
return ResponseEntity.ok(service.findByMerchantId(merchantId, status));
|
||||
}
|
||||
|
||||
@PostMapping("/approvals/{id}/cancel")
|
||||
public ResponseEntity<ApprovalResponse> cancelApproval(@PathVariable Long id) {
|
||||
return ResponseEntity.ok(service.cancelApproval(id));
|
||||
}
|
||||
|
||||
@ExceptionHandler(jakarta.validation.ConstraintViolationException.class)
|
||||
public ResponseEntity<Map<String, String>> handleConstraintViolation(jakarta.validation.ConstraintViolationException ex) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<Map<String, String>> handleIllegalArgument(IllegalArgumentException ex) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalStateException.class)
|
||||
public ResponseEntity<Map<String, String>> handleIllegalState(IllegalStateException ex) {
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(Map.of("error", ex.getMessage()));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package com.acquirex.approval.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 카드 매입 승인 도메인 엔티티
|
||||
* Spring Boot 3: javax.* → jakarta.* 마이그레이션
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "acquisition_approval")
|
||||
public class AcquisitionApproval {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "merchant_id", nullable = false, length = 20)
|
||||
private String merchantId;
|
||||
|
||||
@Column(name = "card_number", nullable = false, length = 19)
|
||||
private String cardNumber;
|
||||
|
||||
@Column(name = "approval_amount", nullable = false, precision = 15, scale = 2)
|
||||
private BigDecimal approvalAmount;
|
||||
|
||||
@Column(name = "approval_number", nullable = false, length = 12)
|
||||
private String approvalNumber;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private ApprovalStatus status;
|
||||
|
||||
@Column(name = "approval_datetime", nullable = false)
|
||||
private LocalDateTime approvalDatetime;
|
||||
|
||||
@Column(name = "created_at", nullable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public AcquisitionApproval() {}
|
||||
|
||||
public AcquisitionApproval(String merchantId, String cardNumber, BigDecimal approvalAmount,
|
||||
String approvalNumber, ApprovalStatus status, LocalDateTime approvalDatetime) {
|
||||
this.merchantId = merchantId;
|
||||
this.cardNumber = cardNumber;
|
||||
this.approvalAmount = approvalAmount;
|
||||
this.approvalNumber = approvalNumber;
|
||||
this.status = status;
|
||||
this.approvalDatetime = approvalDatetime;
|
||||
this.createdAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getMerchantId() { return merchantId; }
|
||||
public void setMerchantId(String merchantId) { this.merchantId = merchantId; }
|
||||
public String getCardNumber() { return cardNumber; }
|
||||
public void setCardNumber(String cardNumber) { this.cardNumber = cardNumber; }
|
||||
public BigDecimal getApprovalAmount() { return approvalAmount; }
|
||||
public void setApprovalAmount(BigDecimal approvalAmount) { this.approvalAmount = approvalAmount; }
|
||||
public String getApprovalNumber() { return approvalNumber; }
|
||||
public void setApprovalNumber(String approvalNumber) { this.approvalNumber = approvalNumber; }
|
||||
public ApprovalStatus getStatus() { return status; }
|
||||
public void setStatus(ApprovalStatus status) { this.status = status; }
|
||||
public LocalDateTime getApprovalDatetime() { return approvalDatetime; }
|
||||
public void setApprovalDatetime(LocalDateTime approvalDatetime) { this.approvalDatetime = approvalDatetime; }
|
||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||
public LocalDateTime getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() { this.updatedAt = LocalDateTime.now(); }
|
||||
|
||||
/** 매입 승인 상태 열거형 */
|
||||
public enum ApprovalStatus {
|
||||
PENDING("대기"), APPROVED("승인"), REJECTED("거절"), CANCELLED("취소");
|
||||
private final String description;
|
||||
ApprovalStatus(String description) { this.description = description; }
|
||||
public String getDescription() { return description; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.acquirex.approval.repository;
|
||||
|
||||
import com.acquirex.approval.domain.AcquisitionApproval;
|
||||
import com.acquirex.approval.domain.AcquisitionApproval.ApprovalStatus;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/** 카드 매입 승인 레포지토리 - Spring Boot 3: Spring Data JPA 3.x 호환 */
|
||||
@Repository
|
||||
public interface AcquisitionApprovalRepository extends JpaRepository<AcquisitionApproval, Long> {
|
||||
Optional<AcquisitionApproval> findByApprovalNumber(String approvalNumber);
|
||||
List<AcquisitionApproval> findByMerchantIdAndStatus(String merchantId, ApprovalStatus status);
|
||||
List<AcquisitionApproval> findByApprovalDatetimeBetween(LocalDateTime start, LocalDateTime end);
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package com.acquirex.approval.service;
|
||||
|
||||
import com.acquirex.approval.domain.AcquisitionApproval;
|
||||
import com.acquirex.approval.domain.AcquisitionApproval.ApprovalStatus;
|
||||
import com.acquirex.approval.repository.AcquisitionApprovalRepository;
|
||||
import jakarta.validation.constraints.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** 카드 매입 승인 서비스 - Spring Boot 3 전환 적용 */
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
public class AcquisitionApprovalService {
|
||||
|
||||
private final AcquisitionApprovalRepository repository;
|
||||
|
||||
public AcquisitionApprovalService(AcquisitionApprovalRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
/** 매입 승인 요청 처리 */
|
||||
@Transactional
|
||||
public ApprovalResponse processApproval(ApprovalRequest request) {
|
||||
String approvalNumber = generateApprovalNumber();
|
||||
AcquisitionApproval approval = new AcquisitionApproval(
|
||||
request.merchantId, maskCardNumber(request.cardNumber),
|
||||
request.approvalAmount, approvalNumber, ApprovalStatus.APPROVED, LocalDateTime.now());
|
||||
return toResponse(repository.save(approval));
|
||||
}
|
||||
|
||||
/** 승인 번호로 조회 */
|
||||
public ApprovalResponse findByApprovalNumber(String approvalNumber) {
|
||||
return repository.findByApprovalNumber(approvalNumber)
|
||||
.map(this::toResponse)
|
||||
.orElseThrow(() -> new IllegalArgumentException("승인 번호를 찾을 수 없습니다: " + approvalNumber));
|
||||
}
|
||||
|
||||
/** 가맹점별 승인 목록 조회 */
|
||||
public List<ApprovalResponse> findByMerchantId(String merchantId, ApprovalStatus status) {
|
||||
return repository.findByMerchantIdAndStatus(merchantId, status)
|
||||
.stream().map(this::toResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/** 승인 취소 처리 */
|
||||
@Transactional
|
||||
public ApprovalResponse cancelApproval(Long id) {
|
||||
AcquisitionApproval approval = repository.findById(id)
|
||||
.orElseThrow(() -> new IllegalArgumentException("승인 정보를 찾을 수 없습니다: " + id));
|
||||
if (approval.getStatus() == ApprovalStatus.CANCELLED) {
|
||||
throw new IllegalStateException("이미 취소된 승인입니다");
|
||||
}
|
||||
approval.setStatus(ApprovalStatus.CANCELLED);
|
||||
return toResponse(repository.save(approval));
|
||||
}
|
||||
|
||||
private String generateApprovalNumber() {
|
||||
return "AP" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 4).toUpperCase();
|
||||
}
|
||||
|
||||
private String maskCardNumber(String cardNumber) {
|
||||
if (cardNumber == null || cardNumber.length() < 10) return cardNumber;
|
||||
return cardNumber.substring(0, 6) + "****" + cardNumber.substring(cardNumber.length() - 4);
|
||||
}
|
||||
|
||||
private ApprovalResponse toResponse(AcquisitionApproval a) {
|
||||
return new ApprovalResponse(a.getId(), a.getMerchantId(), a.getCardNumber(),
|
||||
a.getApprovalAmount(), a.getApprovalNumber(), a.getStatus(), a.getApprovalDatetime());
|
||||
}
|
||||
|
||||
/** 카드 매입 승인 요청 DTO - Spring Boot 3: jakarta.validation 적용 */
|
||||
public static class ApprovalRequest {
|
||||
@NotBlank(message = "가맹점 ID는 필수입니다")
|
||||
@Size(max = 20)
|
||||
public String merchantId;
|
||||
|
||||
@NotBlank(message = "카드번호는 필수입니다")
|
||||
@Pattern(regexp = "\\d{13,19}", message = "카드번호는 13~19자리 숫자입니다")
|
||||
public String cardNumber;
|
||||
|
||||
@NotNull(message = "승인 금액은 필수입니다")
|
||||
@DecimalMin(value = "0.01", message = "승인 금액은 0보다 커야 합니다")
|
||||
@DecimalMax(value = "999999999.99")
|
||||
public BigDecimal approvalAmount;
|
||||
|
||||
public ApprovalRequest() {}
|
||||
public ApprovalRequest(String merchantId, String cardNumber, BigDecimal approvalAmount) {
|
||||
this.merchantId = merchantId; this.cardNumber = cardNumber; this.approvalAmount = approvalAmount;
|
||||
}
|
||||
}
|
||||
|
||||
/** 카드 매입 승인 응답 DTO */
|
||||
public static class ApprovalResponse {
|
||||
public Long id;
|
||||
public String merchantId;
|
||||
public String cardNumber;
|
||||
public BigDecimal approvalAmount;
|
||||
public String approvalNumber;
|
||||
public ApprovalStatus status;
|
||||
public LocalDateTime approvalDatetime;
|
||||
|
||||
public ApprovalResponse() {}
|
||||
public ApprovalResponse(Long id, String merchantId, String cardNumber, BigDecimal approvalAmount,
|
||||
String approvalNumber, ApprovalStatus status, LocalDateTime approvalDatetime) {
|
||||
this.id = id; this.merchantId = merchantId; this.cardNumber = cardNumber;
|
||||
this.approvalAmount = approvalAmount; this.approvalNumber = approvalNumber;
|
||||
this.status = status; this.approvalDatetime = approvalDatetime;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
proj-acquirex/src/main/resources/application.properties
Normal file
23
proj-acquirex/src/main/resources/application.properties
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Spring Boot 3.x Configuration
|
||||
spring.application.name=proj-acquirex
|
||||
server.port=8080
|
||||
|
||||
# H2 Database Configuration
|
||||
spring.datasource.url=jdbc:h2:mem:acquirex;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
|
||||
# JPA/Hibernate Configuration
|
||||
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||
spring.jpa.hibernate.ddl-auto=create-drop
|
||||
spring.jpa.show-sql=true
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
|
||||
# H2 Console
|
||||
spring.h2.console.enabled=true
|
||||
spring.h2.console.path=/h2-console
|
||||
|
||||
# Jackson Configuration
|
||||
spring.jackson.serialization.write-dates-as-timestamps=false
|
||||
spring.jackson.time-zone=Asia/Seoul
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
package com.acquirex.approval;
|
||||
|
||||
import com.acquirex.approval.domain.AcquisitionApproval;
|
||||
import com.acquirex.approval.domain.AcquisitionApproval.ApprovalStatus;
|
||||
import com.acquirex.approval.repository.AcquisitionApprovalRepository;
|
||||
import com.acquirex.approval.service.AcquisitionApprovalService;
|
||||
import com.acquirex.approval.service.AcquisitionApprovalService.ApprovalRequest;
|
||||
import com.acquirex.approval.service.AcquisitionApprovalService.ApprovalResponse;
|
||||
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.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
/** 카드 매입 승인 통합 테스트 - Spring Boot 3 전환 검증 */
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
class AcquisitionApprovalTest {
|
||||
|
||||
@Autowired private MockMvc mockMvc;
|
||||
@Autowired private AcquisitionApprovalRepository repository;
|
||||
@Autowired private AcquisitionApprovalService service;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() { repository.deleteAll(); }
|
||||
|
||||
// === Service Tests ===
|
||||
@Test
|
||||
@DisplayName("매입 승인 요청 처리 성공")
|
||||
void processApproval_Success() {
|
||||
ApprovalRequest request = new ApprovalRequest("MERCH001", "1234567890123456", new BigDecimal("50000.00"));
|
||||
ApprovalResponse response = service.processApproval(request);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals("MERCH001", response.merchantId);
|
||||
assertEquals(ApprovalStatus.APPROVED, response.status);
|
||||
assertTrue(response.approvalNumber.startsWith("AP"));
|
||||
assertTrue(response.cardNumber.matches("\\d{6}\*\*\*\*\\d{4}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("승인 번호로 조회 성공")
|
||||
void findByApprovalNumber_Success() {
|
||||
ApprovalRequest request = new ApprovalRequest("MERCH001", "1234567890123456", new BigDecimal("50000.00"));
|
||||
ApprovalResponse created = service.processApproval(request);
|
||||
|
||||
ApprovalResponse found = service.findByApprovalNumber(created.approvalNumber);
|
||||
assertNotNull(found);
|
||||
assertEquals(created.approvalNumber, found.approvalNumber);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("승인 번호로 조회 - 존재하지 않는 경우 예외 발생")
|
||||
void findByApprovalNumber_NotFound() {
|
||||
assertThrows(IllegalArgumentException.class, () -> service.findByApprovalNumber("NOTEXIST"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("승인 취소 성공")
|
||||
void cancelApproval_Success() {
|
||||
ApprovalRequest request = new ApprovalRequest("MERCH001", "1234567890123456", new BigDecimal("50000.00"));
|
||||
ApprovalResponse created = service.processApproval(request);
|
||||
|
||||
ApprovalResponse cancelled = service.cancelApproval(created.id);
|
||||
assertEquals(ApprovalStatus.CANCELLED, cancelled.status);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("승인 취소 - 이미 취소된 경우 예외 발생")
|
||||
void cancelApproval_AlreadyCancelled() {
|
||||
ApprovalRequest request = new ApprovalRequest("MERCH001", "1234567890123456", new BigDecimal("50000.00"));
|
||||
ApprovalResponse created = service.processApproval(request);
|
||||
service.cancelApproval(created.id);
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> service.cancelApproval(created.id));
|
||||
}
|
||||
|
||||
// === Controller Tests ===
|
||||
@Test
|
||||
@DisplayName("POST /api/v1/acquisitions/approvals - 승인 요청 성공")
|
||||
void createApproval_Success() throws Exception {
|
||||
String json = "{\"merchantId\":\"MERCH001\",\"cardNumber\":\"1234567890123456\",\"approvalAmount\":50000.00}";
|
||||
|
||||
mockMvc.perform(post("/api/v1/acquisitions/approvals")
|
||||
.contentType(MediaType.APPLICATION_JSON).content(json))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.merchantId").value("MERCH001"))
|
||||
.andExpect(jsonPath("$.status").value("APPROVED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("POST /api/v1/acquisitions/approvals - 유효성 검증 실패")
|
||||
void createApproval_ValidationError() throws Exception {
|
||||
String json = "{\"merchantId\":\"\",\"cardNumber\":\"123\",\"approvalAmount\":-100}";
|
||||
|
||||
mockMvc.perform(post("/api/v1/acquisitions/approvals")
|
||||
.contentType(MediaType.APPLICATION_JSON).content(json))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("GET /api/v1/acquisitions/approvals/{approvalNumber} - 승인 조회 성공")
|
||||
void getApproval_Success() throws Exception {
|
||||
String json = "{\"merchantId\":\"MERCH001\",\"cardNumber\":\"1234567890123456\",\"approvalAmount\":50000.00}";
|
||||
MvcResult result = mockMvc.perform(post("/api/v1/acquisitions/approvals")
|
||||
.contentType(MediaType.APPLICATION_JSON).content(json)).andReturn();
|
||||
|
||||
String approvalNumber = com.jayway.jsonpath.JsonPath.read(result.getResponse().getContentAsString(), "$.approvalNumber");
|
||||
|
||||
mockMvc.perform(get("/api/v1/acquisitions/approvals/{approvalNumber}", approvalNumber))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.approvalNumber").value(approvalNumber));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("POST /api/v1/acquisitions/approvals/{id}/cancel - 승인 취소 성공")
|
||||
void cancelApproval_Controller_Success() throws Exception {
|
||||
String json = "{\"merchantId\":\"MERCH001\",\"cardNumber\":\"1234567890123456\",\"approvalAmount\":50000.00}";
|
||||
MvcResult result = mockMvc.perform(post("/api/v1/acquisitions/approvals")
|
||||
.contentType(MediaType.APPLICATION_JSON).content(json)).andReturn();
|
||||
|
||||
Long id = com.jayway.jsonpath.JsonPath.read(result.getResponse().getContentAsString(), "$.id");
|
||||
|
||||
mockMvc.perform(post("/api/v1/acquisitions/approvals/{id}/cancel", id))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.status").value("CANCELLED"));
|
||||
}
|
||||
|
||||
// === Domain Tests ===
|
||||
@Test
|
||||
@DisplayName("ApprovalStatus 모든 상태 확인")
|
||||
void testAllStatuses() {
|
||||
assertEquals(4, ApprovalStatus.values().length);
|
||||
assertNotNull(ApprovalStatus.PENDING);
|
||||
assertNotNull(ApprovalStatus.APPROVED);
|
||||
assertNotNull(ApprovalStatus.REJECTED);
|
||||
assertNotNull(ApprovalStatus.CANCELLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("ApprovalStatus 설명 확인")
|
||||
void testStatusDescriptions() {
|
||||
assertEquals("대기", ApprovalStatus.PENDING.getDescription());
|
||||
assertEquals("승인", ApprovalStatus.APPROVED.getDescription());
|
||||
assertEquals("거절", ApprovalStatus.REJECTED.getDescription());
|
||||
assertEquals("취소", ApprovalStatus.CANCELLED.getDescription());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue