diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..b45650c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,57 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.5
+
+
+
+ com.acquirex
+ proj-acquirex
+ 1.0.0-SNAPSHOT
+ proj-acquirex
+ 카드 매입 승인 시스템 - Spring Boot 3
+
+
+ 17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ com.h2database
+ h2
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/acquirex/AcquirexApplication.java b/src/main/java/com/acquirex/AcquirexApplication.java
new file mode 100644
index 0000000..daf6bca
--- /dev/null
+++ b/src/main/java/com/acquirex/AcquirexApplication.java
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/acquirex/controller/ApprovalController.java b/src/main/java/com/acquirex/controller/ApprovalController.java
new file mode 100644
index 0000000..79d9136
--- /dev/null
+++ b/src/main/java/com/acquirex/controller/ApprovalController.java
@@ -0,0 +1,47 @@
+package com.acquirex.controller;
+
+import com.acquirex.dto.ApprovalRequestDto;
+import com.acquirex.dto.ApprovalResponseDto;
+import com.acquirex.service.ApprovalService;
+import jakarta.validation.Valid;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/v1/approvals")
+public class ApprovalController {
+
+ private final ApprovalService approvalService;
+
+ public ApprovalController(ApprovalService approvalService) {
+ this.approvalService = approvalService;
+ }
+
+ @PostMapping
+ public ResponseEntity requestApproval(@Valid @RequestBody ApprovalRequestDto request) {
+ ApprovalResponseDto response = approvalService.requestApproval(request);
+ return ResponseEntity.status(HttpStatus.CREATED).body(response);
+ }
+
+ @PostMapping("/{id}/approve")
+ public ResponseEntity approveRequest(@PathVariable Long id) {
+ ApprovalResponseDto response = approvalService.approveRequest(id);
+ return ResponseEntity.ok(response);
+ }
+
+ @PostMapping("/{id}/reject")
+ public ResponseEntity rejectRequest(@PathVariable Long id, @RequestBody Map body) {
+ String reason = body.getOrDefault("reason", "사유 미지정");
+ ApprovalResponseDto response = approvalService.rejectRequest(id, reason);
+ return ResponseEntity.ok(response);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getApprovalStatus(@PathVariable Long id) {
+ ApprovalResponseDto response = approvalService.getApprovalStatus(id);
+ return ResponseEntity.ok(response);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/acquirex/domain/DomainClasses.java b/src/main/java/com/acquirex/domain/DomainClasses.java
new file mode 100644
index 0000000..ffd4576
--- /dev/null
+++ b/src/main/java/com/acquirex/domain/DomainClasses.java
@@ -0,0 +1,131 @@
+package com.acquirex.domain;
+
+import jakarta.persistence.*;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+// ===== AcquisitionCard Entity =====
+@Entity
+@Table(name = "acquisition_cards")
+class AcquisitionCard {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotBlank
+ @Column(name = "card_number", length = 20)
+ private String cardNumber;
+
+ @NotBlank
+ @Column(name = "card_company_code", length = 10)
+ private String cardCompanyCode;
+
+ @NotNull
+ @Column(name = "merchant_number", length = 15)
+ private String merchantNumber;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "status", length = 20)
+ private CardStatus status = CardStatus.PENDING;
+
+ @Column(name = "created_at")
+ private LocalDateTime createdAt;
+
+ @Column(name = "updated_at")
+ private LocalDateTime updatedAt;
+
+ public enum CardStatus { PENDING, APPROVED, REJECTED, CANCELLED }
+
+ @PrePersist
+ protected void onCreate() {
+ createdAt = LocalDateTime.now();
+ updatedAt = LocalDateTime.now();
+ }
+
+ @PreUpdate
+ protected void onUpdate() {
+ updatedAt = LocalDateTime.now();
+ }
+
+ public Long getId() { return id; }
+ public void setId(Long id) { this.id = id; }
+ public String getCardNumber() { return cardNumber; }
+ public void setCardNumber(String cardNumber) { this.cardNumber = cardNumber; }
+ public String getCardCompanyCode() { return cardCompanyCode; }
+ public void setCardCompanyCode(String cardCompanyCode) { this.cardCompanyCode = cardCompanyCode; }
+ public String getMerchantNumber() { return merchantNumber; }
+ public void setMerchantNumber(String merchantNumber) { this.merchantNumber = merchantNumber; }
+ public CardStatus getStatus() { return status; }
+ public void setStatus(CardStatus status) { this.status = status; }
+ public LocalDateTime getCreatedAt() { return createdAt; }
+ public LocalDateTime getUpdatedAt() { return updatedAt; }
+}
+
+// ===== ApprovalRequest Entity =====
+@Entity
+@Table(name = "approval_requests")
+class ApprovalRequest {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotNull
+ @Column(name = "card_id")
+ private Long cardId;
+
+ @NotBlank
+ @Column(name = "transaction_id", length = 30, unique = true)
+ private String transactionId;
+
+ @NotNull
+ @Column(name = "amount", precision = 15, scale = 2)
+ private BigDecimal amount;
+
+ @Column(name = "vat", precision = 15, scale = 2)
+ private BigDecimal vat;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "approval_status", length = 20)
+ private ApprovalStatus approvalStatus = ApprovalStatus.REQUESTED;
+
+ @Column(name = "approval_number", length = 20)
+ private String approvalNumber;
+
+ @Column(name = "requested_at")
+ private LocalDateTime requestedAt;
+
+ @Column(name = "approved_at")
+ private LocalDateTime approvedAt;
+
+ @Column(name = "rejection_reason", length = 200)
+ private String rejectionReason;
+
+ public enum ApprovalStatus { REQUESTED, APPROVED, REJECTED, CANCELLED }
+
+ @PrePersist
+ protected void onCreate() {
+ requestedAt = LocalDateTime.now();
+ }
+
+ public Long getId() { return id; }
+ public void setId(Long id) { this.id = id; }
+ public Long getCardId() { return cardId; }
+ public void setCardId(Long cardId) { this.cardId = cardId; }
+ public String getTransactionId() { return transactionId; }
+ public void setTransactionId(String transactionId) { this.transactionId = transactionId; }
+ public BigDecimal getAmount() { return amount; }
+ public void setAmount(BigDecimal amount) { this.amount = amount; }
+ public BigDecimal getVat() { return vat; }
+ public void setVat(BigDecimal vat) { this.vat = vat; }
+ public ApprovalStatus getApprovalStatus() { return approvalStatus; }
+ public void setApprovalStatus(ApprovalStatus approvalStatus) { this.approvalStatus = approvalStatus; }
+ public String getApprovalNumber() { return approvalNumber; }
+ public void setApprovalNumber(String approvalNumber) { this.approvalNumber = approvalNumber; }
+ public LocalDateTime getRequestedAt() { return requestedAt; }
+ public LocalDateTime getApprovedAt() { return approvedAt; }
+ public void setApprovedAt(LocalDateTime approvedAt) { this.approvedAt = approvedAt; }
+ public String getRejectionReason() { return rejectionReason; }
+ public void setRejectionReason(String rejectionReason) { this.rejectionReason = rejectionReason; }
+}
\ No newline at end of file
diff --git a/src/main/java/com/acquirex/dto/Dtos.java b/src/main/java/com/acquirex/dto/Dtos.java
new file mode 100644
index 0000000..4f835b3
--- /dev/null
+++ b/src/main/java/com/acquirex/dto/Dtos.java
@@ -0,0 +1,84 @@
+package com.acquirex.dto;
+
+import jakarta.validation.constraints.*;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+// ===== ApprovalRequestDto =====
+class ApprovalRequestDto {
+ @NotBlank(message = "카드 번호는 필수입니다")
+ @Size(max = 20)
+ private String cardNumber;
+
+ @NotBlank(message = "카드사 코드는 필수입니다")
+ @Size(max = 10)
+ private String cardCompanyCode;
+
+ @NotBlank(message = "가맹점 번호는 필수입니다")
+ @Size(max = 15)
+ private String merchantNumber;
+
+ @NotNull(message = "거래 금액은 필수입니다")
+ @DecimalMin(value = "0.01")
+ @Digits(integer = 13, fraction = 2)
+ private BigDecimal amount;
+
+ @DecimalMin(value = "0.00")
+ @Digits(integer = 13, fraction = 2)
+ private BigDecimal vat;
+
+ public String getCardNumber() { return cardNumber; }
+ public void setCardNumber(String cardNumber) { this.cardNumber = cardNumber; }
+ public String getCardCompanyCode() { return cardCompanyCode; }
+ public void setCardCompanyCode(String cardCompanyCode) { this.cardCompanyCode = cardCompanyCode; }
+ public String getMerchantNumber() { return merchantNumber; }
+ public void setMerchantNumber(String merchantNumber) { this.merchantNumber = merchantNumber; }
+ public BigDecimal getAmount() { return amount; }
+ public void setAmount(BigDecimal amount) { this.amount = amount; }
+ public BigDecimal getVat() { return vat; }
+ public void setVat(BigDecimal vat) { this.vat = vat; }
+}
+
+// ===== ApprovalResponseDto =====
+class ApprovalResponseDto {
+ private Long approvalId;
+ private String transactionId;
+ private String approvalNumber;
+ private String status;
+ private BigDecimal amount;
+ private BigDecimal vat;
+ private LocalDateTime approvedAt;
+ private String message;
+
+ public ApprovalResponseDto() {}
+
+ public ApprovalResponseDto(Long approvalId, String transactionId, String approvalNumber,
+ String status, BigDecimal amount, BigDecimal vat,
+ LocalDateTime approvedAt, String message) {
+ this.approvalId = approvalId;
+ this.transactionId = transactionId;
+ this.approvalNumber = approvalNumber;
+ this.status = status;
+ this.amount = amount;
+ this.vat = vat;
+ this.approvedAt = approvedAt;
+ this.message = message;
+ }
+
+ public Long getApprovalId() { return approvalId; }
+ public void setApprovalId(Long approvalId) { this.approvalId = approvalId; }
+ public String getTransactionId() { return transactionId; }
+ public void setTransactionId(String transactionId) { this.transactionId = transactionId; }
+ public String getApprovalNumber() { return approvalNumber; }
+ public void setApprovalNumber(String approvalNumber) { this.approvalNumber = approvalNumber; }
+ public String getStatus() { return status; }
+ public void setStatus(String status) { this.status = status; }
+ public BigDecimal getAmount() { return amount; }
+ public void setAmount(BigDecimal amount) { this.amount = amount; }
+ public BigDecimal getVat() { return vat; }
+ public void setVat(BigDecimal vat) { this.vat = vat; }
+ public LocalDateTime getApprovedAt() { return approvedAt; }
+ public void setApprovedAt(LocalDateTime approvedAt) { this.approvedAt = approvedAt; }
+ public String getMessage() { return message; }
+ public void setMessage(String message) { this.message = message; }
+}
\ No newline at end of file
diff --git a/src/main/java/com/acquirex/repository/Repositories.java b/src/main/java/com/acquirex/repository/Repositories.java
new file mode 100644
index 0000000..6da0c2f
--- /dev/null
+++ b/src/main/java/com/acquirex/repository/Repositories.java
@@ -0,0 +1,21 @@
+package com.acquirex.repository;
+
+import com.acquirex.domain.AcquisitionCard;
+import com.acquirex.domain.ApprovalRequest;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+interface AcquisitionCardRepository extends JpaRepository {
+ Optional findByCardNumberAndCardCompanyCode(String cardNumber, String cardCompanyCode);
+ Optional findByMerchantNumber(String merchantNumber);
+}
+
+@Repository
+interface ApprovalRequestRepository extends JpaRepository {
+ Optional findByTransactionId(String transactionId);
+ List findByCardId(Long cardId);
+ List findByApprovalStatus(ApprovalRequest.ApprovalStatus status);
+}
\ No newline at end of file
diff --git a/src/main/java/com/acquirex/service/ApprovalService.java b/src/main/java/com/acquirex/service/ApprovalService.java
new file mode 100644
index 0000000..3dffa02
--- /dev/null
+++ b/src/main/java/com/acquirex/service/ApprovalService.java
@@ -0,0 +1,110 @@
+package com.acquirex.service;
+
+import com.acquirex.domain.AcquisitionCard;
+import com.acquirex.domain.ApprovalRequest;
+import com.acquirex.dto.ApprovalRequestDto;
+import com.acquirex.dto.ApprovalResponseDto;
+import com.acquirex.repository.AcquisitionCardRepository;
+import com.acquirex.repository.ApprovalRequestRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.UUID;
+
+@Service
+public class ApprovalService {
+
+ private final ApprovalRequestRepository approvalRequestRepository;
+ private final AcquisitionCardRepository acquisitionCardRepository;
+
+ public ApprovalService(ApprovalRequestRepository approvalRequestRepository,
+ AcquisitionCardRepository acquisitionCardRepository) {
+ this.approvalRequestRepository = approvalRequestRepository;
+ this.acquisitionCardRepository = acquisitionCardRepository;
+ }
+
+ @Transactional
+ public ApprovalResponseDto requestApproval(ApprovalRequestDto dto) {
+ String transactionId = generateTransactionId();
+
+ AcquisitionCard card = acquisitionCardRepository
+ .findByCardNumberAndCardCompanyCode(dto.getCardNumber(), dto.getCardCompanyCode())
+ .orElseGet(() -> createNewCard(dto));
+
+ ApprovalRequest request = new ApprovalRequest();
+ request.setCardId(card.getId());
+ request.setTransactionId(transactionId);
+ request.setAmount(dto.getAmount());
+ request.setVat(dto.getVat());
+ request.setApprovalStatus(ApprovalRequest.ApprovalStatus.REQUESTED);
+
+ ApprovalRequest saved = approvalRequestRepository.save(request);
+ return buildApprovalResponse(saved, "매입 승인 요청이 완료되었습니다");
+ }
+
+ @Transactional
+ public ApprovalResponseDto approveRequest(Long requestId) {
+ ApprovalRequest request = approvalRequestRepository.findById(requestId)
+ .orElseThrow(() -> new IllegalArgumentException("승인 요청을 찾을 수 없습니다: " + requestId));
+
+ if (request.getApprovalStatus() != ApprovalRequest.ApprovalStatus.REQUESTED) {
+ throw new IllegalStateException("이미 처리된 요청입니다");
+ }
+
+ request.setApprovalNumber(generateApprovalNumber());
+ request.setApprovalStatus(ApprovalRequest.ApprovalStatus.APPROVED);
+ request.setApprovedAt(LocalDateTime.now());
+
+ ApprovalRequest saved = approvalRequestRepository.save(request);
+ return buildApprovalResponse(saved, "매입 승인이 완료되었습니다");
+ }
+
+ @Transactional
+ public ApprovalResponseDto rejectRequest(Long requestId, String reason) {
+ ApprovalRequest request = approvalRequestRepository.findById(requestId)
+ .orElseThrow(() -> new IllegalArgumentException("승인 요청을 찾을 수 없습니다: " + requestId));
+
+ if (request.getApprovalStatus() != ApprovalRequest.ApprovalStatus.REQUESTED) {
+ throw new IllegalStateException("이미 처리된 요청입니다");
+ }
+
+ request.setApprovalStatus(ApprovalRequest.ApprovalStatus.REJECTED);
+ request.setRejectionReason(reason);
+
+ ApprovalRequest saved = approvalRequestRepository.save(request);
+ return buildApprovalResponse(saved, "매입 승인이 거절되었습니다");
+ }
+
+ @Transactional(readOnly = true)
+ public ApprovalResponseDto getApprovalStatus(Long requestId) {
+ ApprovalRequest request = approvalRequestRepository.findById(requestId)
+ .orElseThrow(() -> new IllegalArgumentException("승인 요청을 찾을 수 없습니다: " + requestId));
+ return buildApprovalResponse(request, "조회가 완료되었습니다");
+ }
+
+ private AcquisitionCard createNewCard(ApprovalRequestDto dto) {
+ AcquisitionCard card = new AcquisitionCard();
+ card.setCardNumber(dto.getCardNumber());
+ card.setCardCompanyCode(dto.getCardCompanyCode());
+ card.setMerchantNumber(dto.getMerchantNumber());
+ card.setStatus(AcquisitionCard.CardStatus.PENDING);
+ return acquisitionCardRepository.save(card);
+ }
+
+ private String generateTransactionId() {
+ return "TX" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
+ }
+
+ private String generateApprovalNumber() {
+ return "AP" + System.currentTimeMillis();
+ }
+
+ private ApprovalResponseDto buildApprovalResponse(ApprovalRequest request, String message) {
+ return new ApprovalResponseDto(
+ request.getId(), request.getTransactionId(), request.getApprovalNumber(),
+ request.getApprovalStatus().name(), request.getAmount(), request.getVat(),
+ request.getApprovedAt(), message
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/acquirex/ApprovalServiceTest.java b/src/test/java/com/acquirex/ApprovalServiceTest.java
new file mode 100644
index 0000000..5facdf5
--- /dev/null
+++ b/src/test/java/com/acquirex/ApprovalServiceTest.java
@@ -0,0 +1,150 @@
+package com.acquirex;
+
+import com.acquirex.domain.AcquisitionCard;
+import com.acquirex.domain.ApprovalRequest;
+import com.acquirex.dto.ApprovalRequestDto;
+import com.acquirex.dto.ApprovalResponseDto;
+import com.acquirex.repository.AcquisitionCardRepository;
+import com.acquirex.repository.ApprovalRequestRepository;
+import com.acquirex.service.ApprovalService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.math.BigDecimal;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class ApprovalServiceTest {
+
+ @Mock
+ private ApprovalRequestRepository approvalRequestRepository;
+
+ @Mock
+ private AcquisitionCardRepository acquisitionCardRepository;
+
+ private ApprovalService approvalService;
+ private ApprovalRequestDto validRequestDto;
+ private AcquisitionCard testCard;
+ private ApprovalRequest testRequest;
+
+ @BeforeEach
+ void setUp() {
+ approvalService = new ApprovalService(approvalRequestRepository, acquisitionCardRepository);
+
+ validRequestDto = new ApprovalRequestDto();
+ validRequestDto.setCardNumber("1234567890123456");
+ validRequestDto.setCardCompanyCode("KB");
+ validRequestDto.setMerchantNumber("123456789012345");
+ validRequestDto.setAmount(new BigDecimal("50000.00"));
+ validRequestDto.setVat(new BigDecimal("5000.00"));
+
+ testCard = new AcquisitionCard();
+ testCard.setId(1L);
+ testCard.setCardNumber("1234567890123456");
+ testCard.setCardCompanyCode("KB");
+ testCard.setMerchantNumber("123456789012345");
+ testCard.setStatus(AcquisitionCard.CardStatus.PENDING);
+
+ testRequest = new ApprovalRequest();
+ testRequest.setId(1L);
+ testRequest.setCardId(1L);
+ testRequest.setTransactionId("TX12345678");
+ testRequest.setAmount(new BigDecimal("50000.00"));
+ testRequest.setVat(new BigDecimal("5000.00"));
+ testRequest.setApprovalStatus(ApprovalRequest.ApprovalStatus.REQUESTED);
+ }
+
+ @Test
+ @DisplayName("매입 승인 요청 성공 - 기존 카드")
+ void requestApproval_WithExistingCard_Success() {
+ when(acquisitionCardRepository.findByCardNumberAndCardCompanyCode(any(), any()))
+ .thenReturn(Optional.of(testCard));
+ when(approvalRequestRepository.save(any(ApprovalRequest.class))).thenReturn(testRequest);
+
+ ApprovalResponseDto response = approvalService.requestApproval(validRequestDto);
+
+ assertNotNull(response);
+ assertEquals("TX12345678", response.getTransactionId());
+ assertEquals("REQUESTED", response.getStatus());
+ verify(acquisitionCardRepository, never()).save(any());
+ verify(approvalRequestRepository, times(1)).save(any());
+ }
+
+ @Test
+ @DisplayName("매입 승인 요청 성공 - 신규 카드")
+ void requestApproval_WithNewCard_Success() {
+ when(acquisitionCardRepository.findByCardNumberAndCardCompanyCode(any(), any()))
+ .thenReturn(Optional.empty());
+ when(acquisitionCardRepository.save(any(AcquisitionCard.class))).thenReturn(testCard);
+ when(approvalRequestRepository.save(any(ApprovalRequest.class))).thenReturn(testRequest);
+
+ ApprovalResponseDto response = approvalService.requestApproval(validRequestDto);
+
+ assertNotNull(response);
+ assertEquals("REQUESTED", response.getStatus());
+ verify(acquisitionCardRepository, times(1)).save(any());
+ }
+
+ @Test
+ @DisplayName("매입 승인 처리 성공")
+ void approveRequest_Success() {
+ when(approvalRequestRepository.findById(1L)).thenReturn(Optional.of(testRequest));
+ when(approvalRequestRepository.save(any(ApprovalRequest.class)))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+
+ ApprovalResponseDto response = approvalService.approveRequest(1L);
+
+ assertNotNull(response);
+ assertEquals("APPROVED", response.getStatus());
+ assertNotNull(response.getApprovalNumber());
+ assertNotNull(response.getApprovedAt());
+ }
+
+ @Test
+ @DisplayName("매입 승인 처리 실패 - 요청 없음")
+ void approveRequest_NotFound() {
+ when(approvalRequestRepository.findById(999L)).thenReturn(Optional.empty());
+ assertThrows(IllegalArgumentException.class, () -> approvalService.approveRequest(999L));
+ }
+
+ @Test
+ @DisplayName("매입 승인 처리 실패 - 이미 처리됨")
+ void approveRequest_AlreadyProcessed() {
+ testRequest.setApprovalStatus(ApprovalRequest.ApprovalStatus.APPROVED);
+ when(approvalRequestRepository.findById(1L)).thenReturn(Optional.of(testRequest));
+ assertThrows(IllegalStateException.class, () -> approvalService.approveRequest(1L));
+ }
+
+ @Test
+ @DisplayName("매입 승인 거절 성공")
+ void rejectRequest_Success() {
+ when(approvalRequestRepository.findById(1L)).thenReturn(Optional.of(testRequest));
+ when(approvalRequestRepository.save(any(ApprovalRequest.class)))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+
+ ApprovalResponseDto response = approvalService.rejectRequest(1L, "한도 초과");
+
+ assertNotNull(response);
+ assertEquals("REJECTED", response.getStatus());
+ }
+
+ @Test
+ @DisplayName("승인 상태 조회 성공")
+ void getApprovalStatus_Success() {
+ when(approvalRequestRepository.findById(1L)).thenReturn(Optional.of(testRequest));
+
+ ApprovalResponseDto response = approvalService.getApprovalStatus(1L);
+
+ assertNotNull(response);
+ assertEquals(1L, response.getApprovalId());
+ assertEquals("TX12345678", response.getTransactionId());
+ }
+}
\ No newline at end of file