Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
73c44e0
fix: OrderStatusUpdateEventRequest에서 adminId 제외
ginsengcandy May 6, 2026
901dbcb
feat: 주문 상태 변경 이벤트 발행 시 주문자 id 포함
ginsengcandy May 6, 2026
df4dfc9
fix: findById -> findByIdWithBuyer로 수정
ginsengcandy May 6, 2026
5baa494
fix: bean definition overriding 활성화, redis repositories 자동설정 끄기
ginsengcandy May 6, 2026
19cf35c
fix: 알림서버 엔드포인트 불일치 문제 해결
ginsengcandy May 6, 2026
2de31da
feat: CI 환경에서 Redis 띄우기
ginsengcandy May 7, 2026
63292fd
fix: spring.jpa.properties.hibernate.dialect 를 mysql로 오버라이드
ginsengcandy May 9, 2026
8d2355a
chore: test용 jwt 시크릿 단순화
ginsengcandy May 9, 2026
4935913
feat: mock redissonClient 주입하여 RedissonAutoConfiguration 무력화
ginsengcandy May 9, 2026
5219348
Merge branch 'feat/orderStatusUpdateEvent' of github.com:all-in-marke…
ginsengcandy May 9, 2026
fc3a600
fix: ci에서 JWT_SECRET 환경변수 자체를 제거
ginsengcandy May 9, 2026
e68411a
fix: 서비스 컨테이너 접근 호스트를 localhost로 변경
ginsengcandy May 9, 2026
91b2321
feat: 직렬화 실패 예외 상위로 전파
ginsengcandy May 9, 2026
37e7728
feat: MySQL 테스트 컨테이너 8.0.36으로 패치 고정하여 재현성 보장
ginsengcandy May 9, 2026
d26ea09
fix: test 전용 JWT_SECRET 길이 충분히 늘림(45bytes)
ginsengcandy May 9, 2026
2aa1a1a
fix: test용 jwt secret base64 값으로 변경
ginsengcandy May 9, 2026
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
14 changes: 13 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ jobs:
runs-on: ubuntu-latest
needs: build

services:
redis:
image: redis:7
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- name: 코드 체크아웃
uses: actions/checkout@v4
Expand All @@ -61,7 +72,8 @@ jobs:
- name: 테스트 실행
run: ./gradlew test
env:
JWT_SECRET: VtHcoEqCTwJ981b7wHL5yb57mwhlE8UjzYhFu3vd5M1
SPRING_REDIS_HOST: localhost
SPRING_REDIS_PORT: 6379

- name: 테스트 결과 업로드
uses: actions/upload-artifact@v4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ public OrderDetailResponse updateStatus(Long adminId, Long orderId, AdminOrderUp

validationForbidden(adminId);

Order order = orderRepository.findById(orderId).orElseThrow(
Order order = orderRepository.findByIdWithBuyer(orderId).orElseThrow(
() -> new BaseException(ErrorEnum.ORDER_NOT_FOUND)
);

order.updateStatus(request.status());
eventPublisher.publishEvent(new OrderStatusUpdateEvent(adminId, orderId, request.status()));
eventPublisher.publishEvent(new OrderStatusUpdateEvent(order.getBuyer().getId(), orderId, request.status()));

return OrderDetailResponse.from(order);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import com.example.allinmarket.domain.order.enums.OrderStatus;

public record OrderStatusUpdateEvent(
Long adminId,
Long orderId,
OrderStatus status
){}
Long buyerId,
Long orderId,
OrderStatus status
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class OrderStatusUpdateEventListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderStatusUpdateEvent(OrderStatusUpdateEvent event) {
try{
OrderStatusUpdateEventRequest request = new OrderStatusUpdateEventRequest(event.adminId(), event.orderId(), event.status());
OrderStatusUpdateEventRequest request = new OrderStatusUpdateEventRequest(event.buyerId(), event.orderId(), event.status());

String body = objectMapper.writeValueAsString(request);

Expand All @@ -58,7 +58,7 @@ public void handleOrderStatusUpdateEvent(OrderStatusUpdateEvent event) {
);

restClient.post()
.uri(notificationServerUrl + "/internal/notifications/order")
.uri(notificationServerUrl + "/internal/notifications/orders")
.header("X-Client-Id", clientId)
.header("X-Timestamp", timestamp)
.header("X-Request-Id", requestId)
Expand All @@ -68,10 +68,11 @@ public void handleOrderStatusUpdateEvent(OrderStatusUpdateEvent event) {
.retrieve()
.toBodilessEntity();
} catch (RestClientException e) {
log.error("주문 상태 변경 알림 전송 실패. adminId = {}, orderId={}, status = {}", event.adminId(), event.orderId(), event.status(), e);
log.error("주문 상태 변경 알림 전송 실패. buyerId = {}, orderId={}, status = {}", event.buyerId(), event.orderId(), event.status(), e);
throw e;
} catch (JsonProcessingException e) {
log.error("주문 상태 변경 이벤트 직렬화 실패. adminId = {}, orderId={}, status = {}", event.adminId(), event.orderId(), event.status(), e);
log.error("주문 상태 변경 이벤트 직렬화 실패. orderId={}, status = {}", event.orderId(), event.status(), e);
throw new IllegalStateException("주문 상태 변경 이벤트 직렬화 실패", e);
}
Comment thread
ginsengcandy marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package com.example.allinmarket.domain.order.event.dto;

import com.example.allinmarket.domain.order.enums.OrderStatus;
import com.example.allinmarket.domain.order.event.OrderStatusUpdateEvent;

public record OrderStatusUpdateEventRequest(
Long adminId,
Long buyerId,
Long orderId,
OrderStatus status
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,23 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.Optional;

public interface OrderRepository extends JpaRepository<Order, Long> {

Page<Order> findByBuyerId(Long buyerId, Pageable pageable);

Page<Order> findByBuyerIdAndStatus(Long buyerId, OrderStatus status, Pageable pageable);

Optional<Order> findByIdAndBuyerId(Long orderId, Long buyerId);

Page<Order> findAllBy(Pageable pageable);

@Query("""
select o from Order o join fetch o.buyer b
where o.id = :orderId
""")
Optional<Order> findByIdWithBuyer(Long orderId);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.example.allinmarket.admin.order.service;

import com.example.allinmarket.admin.entity.Admin;
import com.example.allinmarket.admin.order.dto.request.AdminOrderUpdateStatusRequest;
import com.example.allinmarket.admin.repository.AdminRepository;
import com.example.allinmarket.buyer.entity.Buyer;
Expand All @@ -16,6 +15,7 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
Expand All @@ -38,6 +38,9 @@ class AdminOrderServiceTest {
@Mock
private AdminRepository adminRepository;

@Mock
private ApplicationEventPublisher eventPublisher;

@InjectMocks
private AdminOrderService adminOrderService;

Expand Down Expand Up @@ -179,7 +182,7 @@ private Order createOrderMock(Long orderId) {
Order order = createOrderMock(orderId);
AdminOrderUpdateStatusRequest request = new AdminOrderUpdateStatusRequest(OrderStatus.SHIPPED);

given(orderRepository.findById(orderId)).willReturn(Optional.of(order));
given(orderRepository.findByIdWithBuyer(orderId)).willReturn(Optional.of(order));

// when
OrderDetailResponse result = adminOrderService.updateStatus(adminId, orderId, request);
Expand Down Expand Up @@ -217,7 +220,7 @@ private Order createOrderMock(Long orderId) {

AdminOrderUpdateStatusRequest request = new AdminOrderUpdateStatusRequest(OrderStatus.SHIPPED);

given(orderRepository.findById(orderId)).willReturn(Optional.empty());
given(orderRepository.findByIdWithBuyer(orderId)).willReturn(Optional.empty());

// when & then
BaseException exception = assertThrows(
Expand All @@ -238,7 +241,7 @@ private Order createOrderMock(Long orderId) {
Order order = mock(Order.class);
AdminOrderUpdateStatusRequest request = new AdminOrderUpdateStatusRequest(OrderStatus.PAID);

given(orderRepository.findById(orderId)).willReturn(Optional.of(order));
given(orderRepository.findByIdWithBuyer(orderId)).willReturn(Optional.of(order));

org.mockito.Mockito.doThrow(new BaseException(ErrorEnum.ORDER_STATUS_IMMUTABLE))
.when(order).updateStatus(OrderStatus.PAID);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,17 @@
import org.springframework.context.annotation.Import;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.transaction.support.TransactionTemplate;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand All @@ -49,8 +55,15 @@
@SpringBootTest
@ActiveProfiles("test")
@Import(TestRedisConfig.class)
@Testcontainers
class AdminRefundOptimisticLockServiceTest {

private static final BigDecimal AMOUNT = BigDecimal.valueOf(10000);
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0.36")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@Autowired
private AdminRefundService adminRefundService;

Expand Down Expand Up @@ -80,12 +93,22 @@ class AdminRefundOptimisticLockServiceTest {

private Long adminId;
private Long refundId;

private static final String IMP_UID = "mock-imp-uid";
private static final BigDecimal AMOUNT = BigDecimal.valueOf(10000);
private String impUid;

@DynamicPropertySource
static void mysqlProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", mysql::getJdbcUrl);
registry.add("spring.datasource.username", mysql::getUsername);
registry.add("spring.datasource.password", mysql::getPassword);
registry.add("spring.datasource.driver-class-name", () -> "com.mysql.cj.jdbc.Driver");
registry.add("spring.jpa.hibernate.ddl-auto", () -> "create-only");
registry.add("spring.jpa.properties.hibernate.dialect", () -> "org.hibernate.dialect.MySQLDialect");
}

@BeforeEach
void setUp() {
impUid = "mock-imp-uid-" + UUID.randomUUID();

Admin admin = adminRepository.save(Admin.of("admin@test.com", "password", "관리자"));
adminId = admin.getId();

Expand All @@ -100,7 +123,7 @@ void setUp() {
order.updateStatus(OrderStatus.PAID);

Payment payment = Payment.of(order, "mock-merchant-uid", AMOUNT, MethodEnum.MOCK);
ReflectionTestUtils.setField(payment, "impUid", IMP_UID);
ReflectionTestUtils.setField(payment, "impUid", impUid);
payment.success(LocalDateTime.now());
paymentRepository.save(payment);

Expand Down Expand Up @@ -380,8 +403,8 @@ private PortOnePaymentResponse cancelledPaymentResponse() {
private PortOnePaymentResponse paymentResponse(String status) {
return new PortOnePaymentResponse(
status,
IMP_UID,
"tx_" + IMP_UID,
impUid,
"tx_" + impUid,
"mock-merchant-uid",
"store_mock",
null,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.allinmarket.common.config;

import org.mockito.Mockito;
import org.redisson.api.RedissonClient;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
Expand All @@ -18,4 +19,9 @@ public RedisConnectionFactory redisConnectionFactory() {
public RedisTemplate<String, Object> redisTemplate() {
return Mockito.mock(RedisTemplate.class);
}

@Bean
public RedissonClient redissonClient() {
return Mockito.mock(RedissonClient.class);
}
}
11 changes: 7 additions & 4 deletions src/test/resources/application-test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
spring:
main:
allow-bean-definition-overriding: true
data:
redis:
repositories:
enabled: false
sql:
init:
mode: never # 테스트 시 SQL 초기화 스크립트 실행 안 함
Expand All @@ -12,13 +18,10 @@ spring:
hibernate:
ddl-auto: create-drop # 테스트 시작 시 테이블 생성, 종료 시 삭제
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect # H2 DB에 맞는 방언 설정
defer-datasource-initialization: false

jwt:
secret: VtHcoEqCTwJ981b7wHL5yb57mwhlE8UjzYhFu3vd5M1 # 테스트 전용 고정값 사용
secret: T6+m5bxFPqbtBx/eFtnqEKoGGktPI0WNYzk5YBchIho=
expiration: 86400000 # 토큰 만료 시간 (단위: ms, 86400000 = 24시간)

app:
Expand Down
Loading