From de3c1f2d4a0acbcb7b43814e1782e354627fea4c Mon Sep 17 00:00:00 2001 From: 109an94 <109an94@gmail.com> Date: Sun, 15 Jun 2025 01:32:24 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20h2=20=ED=98=B8=EA=B0=80=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EA=B8=B0=20=EC=8B=9C=EC=9E=91=20=EC=98=A4=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #165 --- .../cleanengine/coin/realitybot/api/H2UnitPriceRefresher.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/cleanengine/coin/realitybot/api/H2UnitPriceRefresher.java b/src/main/java/com/cleanengine/coin/realitybot/api/H2UnitPriceRefresher.java index 74c08104..d8750944 100644 --- a/src/main/java/com/cleanengine/coin/realitybot/api/H2UnitPriceRefresher.java +++ b/src/main/java/com/cleanengine/coin/realitybot/api/H2UnitPriceRefresher.java @@ -10,11 +10,12 @@ @Slf4j @Component -@Profile("h2-mem") +@Profile({"(dev & !it & !mariadb-local) | h2-mem"}) @RequiredArgsConstructor public class H2UnitPriceRefresher implements ApplicationRunner { private final UnitPriceRefresher unitPriceRefresher; + @Override public void run(ApplicationArguments args){ log.info("Running Unit Price Refresher (h2-mem)..."); unitPriceRefresher.initializeUnitPrices(); From d12cae5fe5f88fd5707f09376498efa0ffe24c8d Mon Sep 17 00:00:00 2001 From: 109an94 <109an94@gmail.com> Date: Sun, 15 Jun 2025 02:16:41 +0900 Subject: [PATCH 2/2] =?UTF-8?q?test:=20test=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9D=BC=EB=B6=80=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coin/realitybot/api/ApiScheduler.java | 2 - .../realitybot/RealitybotCoreTestSuite.java | 2 - .../coin/realitybot/api/ApiSchedulerTest.java | 49 +++++++++++++------ .../service/PlatformVWAPServiceTest.java | 7 +++ .../realitybot/service/VWAPErrorInjector.java | 33 ------------- .../VWAPerrorInJectionSchedulerTest.java | 45 ----------------- 6 files changed, 41 insertions(+), 97 deletions(-) delete mode 100644 src/test/java/com/cleanengine/coin/realitybot/service/VWAPErrorInjector.java delete mode 100644 src/test/java/com/cleanengine/coin/realitybot/service/VWAPerrorInJectionSchedulerTest.java diff --git a/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java b/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java index 9bf89e12..7e003827 100644 --- a/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java +++ b/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java @@ -33,7 +33,6 @@ public class ApiScheduler { private final CoinoneAPIClient coinoneAPIClient; private final VWAPMetricsRecorder recorder; private final MeterRegistry meterRegistry; - private String ticker; // @Scheduled(fixedRate = 5000) public void MarketAllRequest() throws InterruptedException { @@ -48,7 +47,6 @@ public void MarketAllRequest() throws InterruptedException { } public void MarketDataRequest(String ticker){ - this.ticker = ticker; String rawJson = bithumbAPIClient.get(ticker); //api raw데이터 // String rawJson = getMarketDataWithFallback(ticker); List gson = tickParser.parseGson(rawJson); //json을 list로 변환 diff --git a/src/test/java/com/cleanengine/coin/realitybot/RealitybotCoreTestSuite.java b/src/test/java/com/cleanengine/coin/realitybot/RealitybotCoreTestSuite.java index 57fde90d..c005bf16 100644 --- a/src/test/java/com/cleanengine/coin/realitybot/RealitybotCoreTestSuite.java +++ b/src/test/java/com/cleanengine/coin/realitybot/RealitybotCoreTestSuite.java @@ -12,7 +12,6 @@ import com.cleanengine.coin.realitybot.parser.TickParserTest; import com.cleanengine.coin.realitybot.service.PlatformVWAPServiceTest; import com.cleanengine.coin.realitybot.service.TickServiceManagerTest; -import com.cleanengine.coin.realitybot.service.VWAPerrorInJectionSchedulerTest; import com.cleanengine.coin.realitybot.vo.DeviationPricePolicyTest; import com.cleanengine.coin.realitybot.vo.OrderPricePolicyTest; import com.cleanengine.coin.realitybot.vo.UnitPricePolicyTest; @@ -34,7 +33,6 @@ TicksTest.class, TickParserTest.class, OpeningPriceParserTest.class, - VWAPerrorInJectionSchedulerTest.class, OrderPricePolicyTest.class, DeviationPricePolicyTest.class }) diff --git a/src/test/java/com/cleanengine/coin/realitybot/api/ApiSchedulerTest.java b/src/test/java/com/cleanengine/coin/realitybot/api/ApiSchedulerTest.java index 1eaca43f..92ea3bce 100644 --- a/src/test/java/com/cleanengine/coin/realitybot/api/ApiSchedulerTest.java +++ b/src/test/java/com/cleanengine/coin/realitybot/api/ApiSchedulerTest.java @@ -3,14 +3,19 @@ import com.cleanengine.coin.order.adapter.out.persistentce.asset.AssetRepository; import com.cleanengine.coin.order.domain.Asset; import com.cleanengine.coin.realitybot.domain.APIVWAPState; +import com.cleanengine.coin.realitybot.domain.VWAPMetricsRecorder; import com.cleanengine.coin.realitybot.dto.Ticks; import com.cleanengine.coin.realitybot.parser.TickParser; import com.cleanengine.coin.realitybot.service.OrderGenerateService; import com.cleanengine.coin.realitybot.service.TickServiceManager; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import java.util.List; @@ -20,6 +25,7 @@ @ExtendWith(MockitoExtension.class) public class ApiSchedulerTest { + @Spy @InjectMocks private ApiScheduler apiScheduler; @@ -30,16 +36,32 @@ public class ApiSchedulerTest { @Mock private OrderGenerateService orderGenerateService; @Mock - APIVWAPState apiVWAPState; + private APIVWAPState apiVWAPState; @Mock private TickServiceManager tickServiceManager; @Mock private AssetRepository assetRepository; + @Mock + private MeterRegistry meterRegistry; + @Mock + private Timer timer; + @Mock + private VWAPMetricsRecorder recorder; - + @BeforeEach + void setUp() { + when(meterRegistry.timer(anyString())).thenReturn(timer); + // Timer의 record 메서드가 람다를 실행하도록 설정 + doAnswer(invocation -> { + Runnable runnable = invocation.getArgument(0); + runnable.run(); + return null; + }).when(timer).record(any(Runnable.class)); + } @Test void marketAllRequestCallsAllTickers() throws InterruptedException { + // Given List assets = List.of( new Asset("BTC", "비트코인", null), new Asset("TRUMP", "트럼프", null), @@ -53,26 +75,23 @@ void marketAllRequestCallsAllTickers() throws InterruptedException { new Asset("WLD", "월드코인", null) ); List testTicks = List.of( - new Ticks("BTC","2025-06-01","11:30:25","2025-06-01T11:30:25.123Z",95730000.0f,0.0082,95000000.0f,730000.0,"ASK",100001L), - new Ticks("ETH","2025-06-01","11:31:10","2025-06-01T11:31:10.456Z",4850000.0f,1.25,4800000.0f,50000.0,"BID",100002L), - new Ticks("DOGE","2025-06-01","11:32:45","2025-06-01T11:32:45.789Z",185.5f,9500.0,180.0f,5.5,"ASK", 100003L)); - Ticks ticks = new Ticks("BTC","2025-06-01","11:30:25","2025-06-01T11:30:25.123Z",95730000.0f,0.0082,95000000.0f,730000.0,"ASK",100001L); - //given + new Ticks("BTC", "2025-06-01", "11:30:25", "2025-06-01T11:30:25.123Z", 95730000.0f, 0.0082, 95000000.0f, 730000.0, "ASK", 100001L), + new Ticks("ETH", "2025-06-01", "11:31:10", "2025-06-01T11:31:10.456Z", 4850000.0f, 1.25, 4800000.0f, 50000.0, "BID", 100002L), + new Ticks("DOGE", "2025-06-01", "11:32:45", "2025-06-01T11:32:45.789Z", 185.5f, 9500.0, 180.0f, 5.5, "ASK", 100003L) + ); + when(assetRepository.findAll()).thenReturn(assets); when(apiClient.get(anyString())).thenReturn("[{data:...}]"); when(tickParser.parseGson(anyString())).thenReturn(testTicks); when(tickServiceManager.getService(anyString())).thenReturn(apiVWAPState); doNothing().when(apiVWAPState).addTick(any()); - System.out.println(assets.size()); - //when + doNothing().when(recorder).recordApiVwap(anyString(),anyDouble()); + + // When apiScheduler.MarketAllRequest(); - //then -// verify(apiScheduler,times(assets.size())).MarketDataRequest(anyString()); + // Then + verify(apiScheduler, times(assets.size())).MarketDataRequest(anyString()); verify(orderGenerateService,times(assets.size())).generateOrder(anyString(),anyDouble(),anyDouble()); } - - @Test - void marketDataRequest() { - } } \ No newline at end of file diff --git a/src/test/java/com/cleanengine/coin/realitybot/service/PlatformVWAPServiceTest.java b/src/test/java/com/cleanengine/coin/realitybot/service/PlatformVWAPServiceTest.java index 58105cc7..f3ea52c1 100644 --- a/src/test/java/com/cleanengine/coin/realitybot/service/PlatformVWAPServiceTest.java +++ b/src/test/java/com/cleanengine/coin/realitybot/service/PlatformVWAPServiceTest.java @@ -2,6 +2,7 @@ import com.cleanengine.coin.realitybot.domain.PlatformVWAPState; import com.cleanengine.coin.trade.entity.Trade; +import com.cleanengine.coin.trade.repository.TradeRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -22,6 +23,9 @@ public class PlatformVWAPServiceTest { @InjectMocks private PlatformVWAPService platformVWAPService; + @Mock + private TradeRepository tradeRepository; + @Mock private PlatformVWAPState platformVwapState; @@ -36,6 +40,7 @@ void testCalculateVWAPLessThan10Trades() { new Trade(3, "BTC", LocalDateTime.now(), 2, 1, 12000.0, 10.0) // 120000 );//이게 적용되면 10000원대 double apiVWAP = 1000.0; //0.1%의 보정값 + when(tradeRepository.findTop10ByTickerOrderByTradeTimeDesc(ticker)).thenReturn(trades); //when double result = platformVWAPService.calculateVWAPbyTrades(ticker, apiVWAP); @@ -64,8 +69,10 @@ void testCalculateVWAPMoreThan10Trades() { new Trade(11, "BTC", LocalDateTime.now(), 2, 1, 20000.0, 10.0) // 200000 ); double apiVWAP = 1000.0; + when(tradeRepository.findTop10ByTickerOrderByTradeTimeDesc(ticker)).thenReturn(trades); when(platformVwapState.getVWAP()).thenReturn(15000.0); platformVWAPService.vwapMap.put(ticker, platformVwapState); + //when double result = platformVWAPService.calculateVWAPbyTrades(ticker, apiVWAP); diff --git a/src/test/java/com/cleanengine/coin/realitybot/service/VWAPErrorInjector.java b/src/test/java/com/cleanengine/coin/realitybot/service/VWAPErrorInjector.java deleted file mode 100644 index 6806f901..00000000 --- a/src/test/java/com/cleanengine/coin/realitybot/service/VWAPErrorInjector.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.cleanengine.coin.realitybot.service; - -import com.cleanengine.coin.trade.entity.Trade; -import com.cleanengine.coin.trade.repository.TradeRepository; -import org.springframework.stereotype.Component; - -import java.time.LocalDateTime; - -import static com.cleanengine.coin.common.CommonValues.BUY_ORDER_BOT_ID; -import static com.cleanengine.coin.common.CommonValues.SELL_ORDER_BOT_ID; - -@Component -public class VWAPErrorInjector { - private final TradeRepository tradeRepository; - - public VWAPErrorInjector(TradeRepository tradeRepository) { - this.tradeRepository = tradeRepository; - } - - public void injectErrorTrade(){ - Trade fakeTrade = new Trade(); - fakeTrade.setTicker("TRUMP"); - fakeTrade.setBuyUserId(BUY_ORDER_BOT_ID); // 테스트용 유저 ID - fakeTrade.setSellUserId(SELL_ORDER_BOT_ID); // 테스트용 유저 ID - fakeTrade.setPrice(25000.0); // 말도 안되는 고가 (예: 시장 평균이 19,000일 때) -// fakeTrade.setPrice(18900.0); // 말도 안되는 고가 (예: 시장 평균이 19,000일 때) - fakeTrade.setSize(300.0); // 대량 체결 -// fakeTrade.setSize(100.0); // 대량 체결 - fakeTrade.setTradeTime(LocalDateTime.now()); - - tradeRepository.save(fakeTrade); - } -} diff --git a/src/test/java/com/cleanengine/coin/realitybot/service/VWAPerrorInJectionSchedulerTest.java b/src/test/java/com/cleanengine/coin/realitybot/service/VWAPerrorInJectionSchedulerTest.java deleted file mode 100644 index a5c884a8..00000000 --- a/src/test/java/com/cleanengine/coin/realitybot/service/VWAPerrorInJectionSchedulerTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.cleanengine.coin.realitybot.service; - -import com.cleanengine.coin.trade.entity.Trade; -import com.cleanengine.coin.trade.repository.TradeRepository; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -public class VWAPerrorInJectionSchedulerTest { - - @Mock - TradeRepository tradeRepository; - - @InjectMocks - VWAPErrorInjector vwapErrorInjector; - - - @Test - @DisplayName("enableInjection() 호출 전에는 작동 안한다") - void doNotingInjection(){ - vwapErrorInjector.injectErrorTrade(); - verify(tradeRepository,never()).save(any()); - } - - @Test - @DisplayName("호출 후에 fateTrade 삽입") - void injectOnceAfterEnable(){ - vwapErrorInjector.injectErrorTrade(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Trade.class); - verify(tradeRepository,times(1)).save(captor.capture()); - - Trade trade = captor.getValue(); - assertEquals("TRUMP",trade.getTicker()); - } -} \ No newline at end of file