From b8d5a6885478b84b28493199dfb19ea296e07932 Mon Sep 17 00:00:00 2001 From: Lee Jeong Yeon Date: Fri, 23 Aug 2024 16:33:37 +0900 Subject: [PATCH 1/9] =?UTF-8?q?test:=20=EC=84=A0=EB=AC=BC=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=20=EC=97=B4=EA=B8=B0=20=EB=8F=99=EC=8B=9C=EC=84=B1=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/GiftBoxConcurrencyTest.java | 148 ++++++++++++++++++ .../src/test/resources/application-test.yml | 5 + .../dilly/member/adaptor/MemberReader.java | 4 + .../java/com/dilly/MemberEnumFixture.java | 10 ++ 4 files changed, 167 insertions(+) create mode 100644 packy-api/src/test/java/com/dilly/gift/application/GiftBoxConcurrencyTest.java diff --git a/packy-api/src/test/java/com/dilly/gift/application/GiftBoxConcurrencyTest.java b/packy-api/src/test/java/com/dilly/gift/application/GiftBoxConcurrencyTest.java new file mode 100644 index 00000000..4473106e --- /dev/null +++ b/packy-api/src/test/java/com/dilly/gift/application/GiftBoxConcurrencyTest.java @@ -0,0 +1,148 @@ +package com.dilly.gift.application; + +import static com.dilly.GiftBoxFixture.sendGiftBoxFixtureWithGift; +import static com.dilly.LetterFixture.createLetterFixture; +import static com.dilly.MemberEnumFixture.NORMAL_MEMBER_RECEIVER; +import static com.dilly.MemberEnumFixture.NORMAL_MEMBER_SENDER; +import static org.assertj.core.api.Assertions.assertThat; + +import com.dilly.gift.adaptor.GiftBoxWriter; +import com.dilly.gift.adaptor.LetterWriter; +import com.dilly.gift.adaptor.ReceiverReader; +import com.dilly.gift.domain.giftbox.GiftBox; +import com.dilly.gift.domain.letter.Letter; +import com.dilly.gift.domain.receiver.Receiver; +import com.dilly.global.WithCustomMockUserSecurityContextFactory; +import com.dilly.member.adaptor.MemberReader; +import com.dilly.member.adaptor.MemberWriter; +import com.dilly.member.domain.Member; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +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.context.SpringBootTest; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.context.TestPropertySource; + +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = "spring.profiles.active=test" +) +@TestPropertySource(locations = {"classpath:application-test.yml"}) +class GiftBoxConcurrencyTest { + + @Autowired + private GiftBoxService giftBoxService; + + @Autowired + private MemberReader memberReader; + + @Autowired + private MemberWriter memberWriter; + + @Autowired + private GiftBoxWriter giftBoxWriter; + + @Autowired + private LetterWriter letterWriter; + + @Autowired + private ReceiverReader receiverReader; + + @Autowired + private WithCustomMockUserSecurityContextFactory withCustomMockUserSecurityContextFactory; + + private Member MEMBER_SENDER; + + private final String SENDER_ID = "1"; + + private Letter letter; + + @BeforeEach + void setUp() { + Long senderId = Long.parseLong(SENDER_ID); + + MEMBER_SENDER = memberWriter.save(NORMAL_MEMBER_SENDER.createMember(senderId)); + + letter = letterWriter.save(createLetterFixture()); + } + + @DisplayName("선물박스 열기 동시성 테스트") + @Test + void multipleUserOpenGiftBox() throws InterruptedException { + // given + int memberCount = 10; + int giftBoxAmount = 1; + + List receivers = new ArrayList<>(); + Long lastMemberId = memberReader.count(); + for (long i = lastMemberId + 1; i <= lastMemberId + memberCount; i++) { + Member member = memberWriter.save(NORMAL_MEMBER_RECEIVER.createMember()); + receivers.add(member); + } + + GiftBox giftBox = giftBoxWriter.save( + sendGiftBoxFixtureWithGift(MEMBER_SENDER, letter)); + + ExecutorService executorService = Executors.newFixedThreadPool(10); + CountDownLatch countDownLatch = new CountDownLatch(memberCount); + + AtomicInteger successCount = new AtomicInteger(); + AtomicInteger failCount = new AtomicInteger(); + + Long receiverBefore = receiverReader.countByGiftBox(giftBox); + + // when + for (int i = 0; i < memberCount; i++) { + Member member = receivers.get(i); + Long memberId = member.getId(); + executorService.submit(() -> { + try { + createSecurityContextWithMockUser(memberId.toString()); + giftBoxService.openGiftBox(giftBox.getId()); + successCount.incrementAndGet(); + } catch (Exception e) { + e.printStackTrace(); + failCount.incrementAndGet(); + } finally { + countDownLatch.countDown(); + } + }); + } + + countDownLatch.await(); + + Long receiverAfter = receiverReader.countByGiftBox(giftBox); + List receiverList = receiverReader.findByGiftBox(giftBox); + + System.out.println("successCount = " + successCount); + System.out.println("failCount = " + failCount); + + System.out.println("receiverBefore = " + receiverBefore); + System.out.println("receiverAfter = " + receiverAfter); + + System.out.println("receiver 목록 출력"); + for (Receiver receiver : receiverList) { + System.out.println("memberId = " + receiver.getMember().getId()); + } + + countDownLatch.await(); + + // then + assertThat(successCount.get()).isEqualTo(giftBoxAmount); + assertThat(receiverAfter).isEqualTo(giftBoxAmount); + } + + private void createSecurityContextWithMockUser(String memberId) { + SecurityContext securityContext = withCustomMockUserSecurityContextFactory.createSecurityContext( + memberId); + SecurityContextHolder.setContext(securityContext); + } +} diff --git a/packy-api/src/test/resources/application-test.yml b/packy-api/src/test/resources/application-test.yml index f6e3b797..a143e40e 100644 --- a/packy-api/src/test/resources/application-test.yml +++ b/packy-api/src/test/resources/application-test.yml @@ -8,6 +8,11 @@ spring: username: sa password: + data: + redis: + host: localhost + port: 6379 + jpa: hibernate: ddl-auto: create-drop diff --git a/packy-domain/src/main/java/com/dilly/member/adaptor/MemberReader.java b/packy-domain/src/main/java/com/dilly/member/adaptor/MemberReader.java index 48c092a6..60593310 100644 --- a/packy-domain/src/main/java/com/dilly/member/adaptor/MemberReader.java +++ b/packy-domain/src/main/java/com/dilly/member/adaptor/MemberReader.java @@ -20,4 +20,8 @@ public Member findById(Long id) { public Long countByStatus(Status status) { return memberRepository.countByStatus(status); } + + public Long count() { + return memberRepository.count(); + } } diff --git a/packy-domain/src/testFixtures/java/com/dilly/MemberEnumFixture.java b/packy-domain/src/testFixtures/java/com/dilly/MemberEnumFixture.java index e5a0bc84..94f1b0c0 100644 --- a/packy-domain/src/testFixtures/java/com/dilly/MemberEnumFixture.java +++ b/packy-domain/src/testFixtures/java/com/dilly/MemberEnumFixture.java @@ -33,6 +33,16 @@ public Member createMember(Long id) { .build(); } + public Member createMember() { + return Member.builder() + .provider(Provider.TEST) + .nickname(getNickname()) + .profileImg(createProfileImage()) + .pushNotification(true) + .marketingAgreement(true) + .build(); + } + private ProfileImage createProfileImage() { return ProfileImage.builder() .id(1L) From 097cf7d70abb9a01473ee885165d0964839acad9 Mon Sep 17 00:00:00 2001 From: Lee Jeong Yeon Date: Fri, 23 Aug 2024 16:36:41 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20=EB=B6=84=EC=82=B0=20=EB=9D=BD=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ConcurrencyFailedException.java | 8 +++ .../java/com/dilly/exception/ErrorCode.java | 1 + packy-domain/build.gradle | 8 ++- .../dilly/global/aop/AopForTransaction.java | 16 ++++++ .../com/dilly/global/aop/RedissonLock.java | 19 +++++++ .../dilly/global/aop/RedissonLockAspect.java | 55 +++++++++++++++++++ .../dilly/global/config/RedissonConfig.java | 29 ++++++++++ .../global/util/CustomSpringELParser.java | 18 ++++++ 8 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 packy-common/src/main/java/com/dilly/exception/ConcurrencyFailedException.java create mode 100644 packy-domain/src/main/java/com/dilly/global/aop/AopForTransaction.java create mode 100644 packy-domain/src/main/java/com/dilly/global/aop/RedissonLock.java create mode 100644 packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java create mode 100644 packy-domain/src/main/java/com/dilly/global/config/RedissonConfig.java create mode 100644 packy-domain/src/main/java/com/dilly/global/util/CustomSpringELParser.java diff --git a/packy-common/src/main/java/com/dilly/exception/ConcurrencyFailedException.java b/packy-common/src/main/java/com/dilly/exception/ConcurrencyFailedException.java new file mode 100644 index 00000000..046afc30 --- /dev/null +++ b/packy-common/src/main/java/com/dilly/exception/ConcurrencyFailedException.java @@ -0,0 +1,8 @@ +package com.dilly.exception; + +public class ConcurrencyFailedException extends BusinessException { + + public ConcurrencyFailedException() { + super(ErrorCode.FAILED_TO_ACCESS_CONCURRENCY); + } +} diff --git a/packy-common/src/main/java/com/dilly/exception/ErrorCode.java b/packy-common/src/main/java/com/dilly/exception/ErrorCode.java index 7ba76bab..9230ddbd 100644 --- a/packy-common/src/main/java/com/dilly/exception/ErrorCode.java +++ b/packy-common/src/main/java/com/dilly/exception/ErrorCode.java @@ -19,6 +19,7 @@ public enum ErrorCode { API_NOT_FOUND(HttpStatus.NOT_FOUND, "요청한 API를 찾을 수 없습니다."), QUERY_PARAMETER_REQUIRED(HttpStatus.BAD_REQUEST, "쿼리 파라미터가 필요한 API입니다."), INVALID_INPUT_VALUE(HttpStatus.BAD_REQUEST, "입력값이 올바르지 않습니다."), + FAILED_TO_ACCESS_CONCURRENCY(HttpStatus.INTERNAL_SERVER_ERROR, "동시 접근에 실패했습니다."), // Kakao KAKAO_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "카카오 서버 연동에 오류가 발생했습니다."), diff --git a/packy-domain/build.gradle b/packy-domain/build.gradle index 40ecc050..1870c01b 100644 --- a/packy-domain/build.gradle +++ b/packy-domain/build.gradle @@ -22,9 +22,12 @@ dependencies { // orm implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - // database + // mysql runtimeOnly 'com.mysql:mysql-connector-j' + + // redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' + implementation 'org.redisson:redisson-spring-boot-starter:3.34.1' // logging implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.0' @@ -40,6 +43,9 @@ dependencies { // tsid implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.6' + + // aop + implementation 'org.springframework.boot:spring-boot-starter-aop' } test { diff --git a/packy-domain/src/main/java/com/dilly/global/aop/AopForTransaction.java b/packy-domain/src/main/java/com/dilly/global/aop/AopForTransaction.java new file mode 100644 index 00000000..bb902b4c --- /dev/null +++ b/packy-domain/src/main/java/com/dilly/global/aop/AopForTransaction.java @@ -0,0 +1,16 @@ +package com.dilly.global.aop; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class AopForTransaction { + + @Transactional(propagation = Propagation.REQUIRES_NEW) + public Object proceed(ProceedingJoinPoint joinPoint) throws Throwable { + return joinPoint.proceed(); + } + +} diff --git a/packy-domain/src/main/java/com/dilly/global/aop/RedissonLock.java b/packy-domain/src/main/java/com/dilly/global/aop/RedissonLock.java new file mode 100644 index 00000000..a59d5641 --- /dev/null +++ b/packy-domain/src/main/java/com/dilly/global/aop/RedissonLock.java @@ -0,0 +1,19 @@ +package com.dilly.global.aop; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RedissonLock { + + String value(); // 락의 이름 + TimeUnit timeUnit() default TimeUnit.MILLISECONDS; // 시간 단위 + long waitTime() default 5_000L; // 락 획득을 위해 waitTime만큼 대기 + long leaseTime() default 5_000L; // 락 획득 후 leaseTime만큼 락 유지 +} diff --git a/packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java b/packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java new file mode 100644 index 00000000..f45ea4ed --- /dev/null +++ b/packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java @@ -0,0 +1,55 @@ +package com.dilly.global.aop; + +import com.dilly.exception.ConcurrencyFailedException; +import com.dilly.global.util.CustomSpringELParser; +import java.lang.reflect.Method; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.stereotype.Component; + +@Slf4j +@Aspect +@Component +@RequiredArgsConstructor +public class RedissonLockAspect { + + private final RedissonClient redissonClient; + private final AopForTransaction aopForTransaction; + + @Around("@annotation(com.dilly.global.aop.RedissonLock)") + public Object redissonLock(ProceedingJoinPoint joinPoint) throws Throwable { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + RedissonLock annotation = method.getAnnotation(RedissonLock.class); + String lockKey = method.getName() + CustomSpringELParser.getDynamicValue( + signature.getParameterNames(), joinPoint.getArgs(), annotation.value()); + + RLock lock = redissonClient.getLock(lockKey); + + try { + boolean lockable = lock.tryLock(annotation.waitTime(), annotation.leaseTime(), + annotation.timeUnit()); + if (!lockable) { + log.info("Lock 획득 실패: {}", lockKey); + throw new ConcurrencyFailedException(); + } + + log.info("Lock 획득 성공: {}", lockKey); + return aopForTransaction.proceed(joinPoint); + } catch (InterruptedException e) { + log.info("에러 발생"); + throw e; + } finally { + if (lock != null && lock.isHeldByCurrentThread()) { + log.info("Lock 해제"); + lock.unlock(); + } + } + } +} diff --git a/packy-domain/src/main/java/com/dilly/global/config/RedissonConfig.java b/packy-domain/src/main/java/com/dilly/global/config/RedissonConfig.java new file mode 100644 index 00000000..73899bf5 --- /dev/null +++ b/packy-domain/src/main/java/com/dilly/global/config/RedissonConfig.java @@ -0,0 +1,29 @@ +package com.dilly.global.config; + +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RedissonConfig { + + @Value("${spring.data.redis.host}") + private String host; + + @Value("${spring.data.redis.port}") + private int port; + + private static final String REDISSION_HOST_PREFIX = "redis://"; + + @Bean + public RedissonClient redissonClient() { + Config config = new Config(); + String address = REDISSION_HOST_PREFIX + host + ":" + port; + config.useSingleServer().setAddress(address); + + return Redisson.create(config); + } +} diff --git a/packy-domain/src/main/java/com/dilly/global/util/CustomSpringELParser.java b/packy-domain/src/main/java/com/dilly/global/util/CustomSpringELParser.java new file mode 100644 index 00000000..b8830760 --- /dev/null +++ b/packy-domain/src/main/java/com/dilly/global/util/CustomSpringELParser.java @@ -0,0 +1,18 @@ +package com.dilly.global.util; + +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +public class CustomSpringELParser { + + public static Object getDynamicValue(String[] parameterNames, Object[] args, String key) { + SpelExpressionParser parser = new SpelExpressionParser(); + StandardEvaluationContext context = new StandardEvaluationContext(); + + for (int i = 0; i < parameterNames.length; i++) { + context.setVariable(parameterNames[i], args[i]); + } + + return parser.parseExpression(key).getValue(context); + } +} From 12cfa5203f98682457ad7d5ede4b9023eb74605c Mon Sep 17 00:00:00 2001 From: Lee Jeong Yeon Date: Fri, 23 Aug 2024 16:37:13 +0900 Subject: [PATCH 3/9] =?UTF-8?q?fix:=20=EC=84=A0=EB=AC=BC=EB=B0=95=EC=8A=A4?= =?UTF-8?q?=20=EC=97=B4=EA=B8=B0=EC=97=90=20=EB=B6=84=EC=82=B0=20=EB=9D=BD?= =?UTF-8?q?=EC=9D=84=20=EC=A0=81=EC=9A=A9=ED=95=98=EC=97=AC=20=EB=8F=99?= =?UTF-8?q?=EC=8B=9C=EC=84=B1=20=EC=A0=9C=EC=96=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/dilly/gift/application/GiftBoxService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packy-api/src/main/java/com/dilly/gift/application/GiftBoxService.java b/packy-api/src/main/java/com/dilly/gift/application/GiftBoxService.java index 1c54be15..a13184a0 100644 --- a/packy-api/src/main/java/com/dilly/gift/application/GiftBoxService.java +++ b/packy-api/src/main/java/com/dilly/gift/application/GiftBoxService.java @@ -44,6 +44,7 @@ import com.dilly.gift.dto.response.PhotoResponseDto.PhotoResponse; import com.dilly.gift.dto.response.StickerResponse; import com.dilly.gift.dto.response.WaitingGiftBoxResponse; +import com.dilly.global.aop.RedissonLock; import com.dilly.global.util.SecurityUtil; import com.dilly.global.util.validator.UuidValidator; import com.dilly.member.adaptor.MemberReader; @@ -121,6 +122,7 @@ public GiftBoxIdResponse createGiftBox(GiftBoxRequest giftBoxRequest) { giftBox.getBox().getKakaoMessageImgUrl()); } + @RedissonLock(value = "#giftBoxId") public GiftBoxResponse openGiftBox(Long giftBoxId) { Long memberId = SecurityUtil.getMemberId(); Member member = memberReader.findById(memberId); From e6de5501700e03dbd2ee20e9f6b7c9a6e26bf70a Mon Sep 17 00:00:00 2001 From: Lee Jeong Yeon Date: Fri, 23 Aug 2024 17:07:39 +0900 Subject: [PATCH 4/9] =?UTF-8?q?fix:=20Redisson=20lockKey=EC=97=90=20profil?= =?UTF-8?q?e=20prefix=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dilly/global/aop/RedissonLockAspect.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java b/packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java index f45ea4ed..f96484ae 100644 --- a/packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java +++ b/packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java @@ -11,6 +11,7 @@ import org.aspectj.lang.reflect.MethodSignature; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Slf4j @@ -19,6 +20,9 @@ @RequiredArgsConstructor public class RedissonLockAspect { + @Value("${spring.profiles.active}") + private String profilePrefix; + private final RedissonClient redissonClient; private final AopForTransaction aopForTransaction; @@ -27,7 +31,9 @@ public Object redissonLock(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); RedissonLock annotation = method.getAnnotation(RedissonLock.class); - String lockKey = method.getName() + CustomSpringELParser.getDynamicValue( + + String lockKey = + profilePrefix + ":" + method.getName() + CustomSpringELParser.getDynamicValue( signature.getParameterNames(), joinPoint.getArgs(), annotation.value()); RLock lock = redissonClient.getLock(lockKey); From db68d9b30240637d8b35d836987ce6f1cd7539dd Mon Sep 17 00:00:00 2001 From: Lee Jeong Yeon Date: Fri, 23 Aug 2024 17:14:28 +0900 Subject: [PATCH 5/9] =?UTF-8?q?chore:=20gitignore=EC=97=90=20spy.log=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 02e7ec94..c42d379e 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,6 @@ packy-support/src/main/resources/*.yml ### QueryDsl QClass ### packy-domain/src/main/generated + +### p6spy ### +spy.log From 5adb044697a7f92056f22a9322a1acfc441af73f Mon Sep 17 00:00:00 2001 From: Lee Jeong Yeon Date: Wed, 28 Aug 2024 10:58:29 +0900 Subject: [PATCH 6/9] =?UTF-8?q?test:=20IntegrationTest=EC=97=90=EC=84=9C?= =?UTF-8?q?=EB=8A=94=20=EB=B6=84=EC=82=B0=20=EB=9D=BD=EC=9D=B4=20=EB=8F=99?= =?UTF-8?q?=EC=9E=91=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/test/java/com/dilly/global/IntegrationTestSupport.java | 1 + .../src/main/java/com/dilly/global/aop/RedissonLockAspect.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packy-api/src/test/java/com/dilly/global/IntegrationTestSupport.java b/packy-api/src/test/java/com/dilly/global/IntegrationTestSupport.java index 42f08c0e..c300b7d1 100644 --- a/packy-api/src/test/java/com/dilly/global/IntegrationTestSupport.java +++ b/packy-api/src/test/java/com/dilly/global/IntegrationTestSupport.java @@ -47,6 +47,7 @@ properties = "spring.profiles.active=test" ) @TestPropertySource(locations = {"classpath:application-test.yml"}) +@TestPropertySource(properties = "ableRedissonLock=false") @Transactional public abstract class IntegrationTestSupport { diff --git a/packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java b/packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java index f96484ae..601cffdf 100644 --- a/packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java +++ b/packy-domain/src/main/java/com/dilly/global/aop/RedissonLockAspect.java @@ -12,12 +12,14 @@ import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; @Slf4j @Aspect @Component @RequiredArgsConstructor +@ConditionalOnExpression("${ableRedissonLock:true}") public class RedissonLockAspect { @Value("${spring.profiles.active}") From f8a67da71d3fe2b3008d739a01f2acd4f9815445 Mon Sep 17 00:00:00 2001 From: Lee Jeong Yeon Date: Wed, 28 Aug 2024 10:59:19 +0900 Subject: [PATCH 7/9] =?UTF-8?q?test:=20assert=20=EC=8B=9C=20=ED=95=98?= =?UTF-8?q?=EB=93=9C=EC=BD=94=EB=94=A9=EB=90=9C=20=EA=B0=92=EC=9D=84=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dilly/mypage/application/MyPageServiceTest.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packy-api/src/test/java/com/dilly/mypage/application/MyPageServiceTest.java b/packy-api/src/test/java/com/dilly/mypage/application/MyPageServiceTest.java index c53a2ec8..74fd5600 100644 --- a/packy-api/src/test/java/com/dilly/mypage/application/MyPageServiceTest.java +++ b/packy-api/src/test/java/com/dilly/mypage/application/MyPageServiceTest.java @@ -6,6 +6,7 @@ import com.dilly.global.WithCustomMockUser; import com.dilly.global.util.SecurityUtil; import com.dilly.member.domain.Member; +import com.dilly.member.domain.ProfileImage; import com.dilly.member.dto.request.ProfileRequest; import com.dilly.member.dto.response.ProfileResponse; import org.junit.jupiter.api.DisplayName; @@ -78,16 +79,21 @@ void updateNickname() { @WithCustomMockUser void updateProfileImage() { // given + Long memberId = SecurityUtil.getMemberId(); + Member member = memberReader.findById(memberId); + ProfileRequest profileRequest = ProfileRequest.builder() .profileImg(2L) .build(); + ProfileImage profileImage = profileImageReader.findById(profileRequest.profileImg()); + // when ProfileResponse response = myPageService.updateProfile(profileRequest); // then - assertThat(response.nickname()).isEqualTo("1번유저"); - assertThat(response.imgUrl()).isEqualTo("www.example2.com"); + assertThat(response.nickname()).isEqualTo(member.getNickname()); + assertThat(response.imgUrl()).isEqualTo(profileImage.getImgUrl()); } } } From fcc9e16fa10b3f3bdc3554060e468f94edc03579 Mon Sep 17 00:00:00 2001 From: Lee Jeong Yeon Date: Wed, 28 Aug 2024 11:04:01 +0900 Subject: [PATCH 8/9] =?UTF-8?q?test:=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EB=A1=A4=EB=B0=B1=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dilly/gift/application/GiftBoxServiceTest.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packy-api/src/test/java/com/dilly/gift/application/GiftBoxServiceTest.java b/packy-api/src/test/java/com/dilly/gift/application/GiftBoxServiceTest.java index 73642b96..5451a778 100644 --- a/packy-api/src/test/java/com/dilly/gift/application/GiftBoxServiceTest.java +++ b/packy-api/src/test/java/com/dilly/gift/application/GiftBoxServiceTest.java @@ -47,7 +47,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DynamicTest; @@ -91,11 +90,6 @@ void setUp() { letter = letterWriter.save(createLetterFixture()); } - @AfterEach - void tearDown() { - memberWriter.deleteAll(); - } - @Nested @DisplayName("선물박스를 만들 때") @WithCustomMockUser(id = SENDER_ID) From e2583b3e60df3af039b24af873fffefd29df12b1 Mon Sep 17 00:00:00 2001 From: Lee Jeong Yeon Date: Thu, 29 Aug 2024 18:57:48 +0900 Subject: [PATCH 9/9] =?UTF-8?q?remove:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dilly/gift/application/GiftBoxConcurrencyTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/packy-api/src/test/java/com/dilly/gift/application/GiftBoxConcurrencyTest.java b/packy-api/src/test/java/com/dilly/gift/application/GiftBoxConcurrencyTest.java index 4473106e..ec9266c7 100644 --- a/packy-api/src/test/java/com/dilly/gift/application/GiftBoxConcurrencyTest.java +++ b/packy-api/src/test/java/com/dilly/gift/application/GiftBoxConcurrencyTest.java @@ -133,8 +133,6 @@ void multipleUserOpenGiftBox() throws InterruptedException { System.out.println("memberId = " + receiver.getMember().getId()); } - countDownLatch.await(); - // then assertThat(successCount.get()).isEqualTo(giftBoxAmount); assertThat(receiverAfter).isEqualTo(giftBoxAmount);