fix: AbstractInflowControlManager Bandwidth.simple → classic+intervally 버킷 생성 버그 수정 및 테스트 추가
- makeBucketUsingInflowVO: 어댑터/인터페이스 단일 버킷 threshold 리필을 greedy → intervally 변경 - makeGroupBucket: 그룹 threshold 버킷 동일하게 수정 - DualInflowControlManagerMakeBucketTest: 버킷 생성 로직 직접 검증 테스트 추가 - InflowControlManagerTest: @BeforeEach reload + spike/burst 경계 케이스 테스트 추가 - SQL 픽스처: MIN 단위 TEST_ADAPTER/TEST_INTERFACE/test-group-quota 항목 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package com.eactive.eai.common.inflow;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -36,9 +38,13 @@ class InflowControlManagerTest {
|
||||
InflowControlManager inflowControlManager;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws LifecycleException {
|
||||
void setUp() throws Exception {
|
||||
if (!inflowControlManager.isStarted()) {
|
||||
inflowControlManager.start();
|
||||
} else {
|
||||
inflowControlManager.reloadAdapter();
|
||||
inflowControlManager.reloadInterface();
|
||||
inflowControlManager.reloadGroup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,4 +63,22 @@ class InflowControlManagerTest {
|
||||
assertNull(inflowControlManager.getGroupBucket("non-existent-group"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void adapter_spikePasses_thenBurstBlocked() {
|
||||
for (int i = 0; i < 5; i++) assertTrue(inflowControlManager.isAdapterPass("TEST_ADAPTER"));
|
||||
assertFalse(inflowControlManager.isAdapterPass("TEST_ADAPTER"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void interface_spikePasses_thenBurstBlocked() {
|
||||
for (int i = 0; i < 5; i++) assertTrue(inflowControlManager.isInterfacePass("TEST_INTERFACE"));
|
||||
assertFalse(inflowControlManager.isInterfacePass("TEST_INTERFACE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void group_spikePasses_thenBurstBlocked() {
|
||||
for (int i = 0; i < 5; i++) assertNull(inflowControlManager.isGroupPass("test-group-quota"));
|
||||
assertEquals(CustomGroupBucket.RESULT_BLOCKED_THRESHOLD, inflowControlManager.isGroupPass("test-group-quota"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package com.eactive.eai.common.inflow.dual;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.eactive.eai.common.inflow.InflowTargetVO;
|
||||
|
||||
/**
|
||||
* DualInflowControlManager.makeBucket() 버킷 생성 동작 검증.
|
||||
*
|
||||
* <p>검증 포인트:
|
||||
* - threshold/perSecond 버킷이 실제로 생성되는지
|
||||
* - 할당량 이내 spike는 통과하고 초과 burst는 차단되는지
|
||||
* - perSecond/threshold 중 어느 쪽이 binding 제약인지 구분되는지
|
||||
*/
|
||||
class DualInflowControlManagerMakeBucketTest {
|
||||
|
||||
private final DualInflowControlManager manager = new DualInflowControlManager();
|
||||
|
||||
private InflowTargetVO vo(long perSecond, long threshold, String timeUnit) {
|
||||
InflowTargetVO vo = new InflowTargetVO();
|
||||
vo.setName("TEST");
|
||||
vo.setThresholdPerSecond(perSecond);
|
||||
vo.setThreshold(threshold);
|
||||
vo.setThresholdTimeUnit(timeUnit);
|
||||
vo.setActivate(true);
|
||||
return vo;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// threshold 단독 (5건/분)
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
void thresholdOnly_spikePasses_thenBurstBlocked() {
|
||||
DualCustomBucket bucket = manager.makeBucket(vo(0, 5, "MIN"));
|
||||
|
||||
for (int i = 0; i < 5; i++) assertNull(bucket.tryConsume());
|
||||
assertEquals(DualCustomBucket.RESULT_BLOCKED_THRESHOLD, bucket.tryConsume());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// perSecond 단독
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
void perSecondOnly_spikePasses_thenBurstBlocked() {
|
||||
DualCustomBucket bucket = manager.makeBucket(vo(5, 0, null));
|
||||
|
||||
for (int i = 0; i < 5; i++) assertNull(bucket.tryConsume());
|
||||
assertEquals(DualCustomBucket.RESULT_BLOCKED_PER_SECOND, bucket.tryConsume());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// perSecond + threshold 동시 설정 — binding 제약 구분
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
void both_perSecondIsBinding_blockedByPerSecond() {
|
||||
// perSecond=3 이 binding: threshold=100/MIN은 여유 있음
|
||||
DualCustomBucket bucket = manager.makeBucket(vo(3, 100, "MIN"));
|
||||
|
||||
for (int i = 0; i < 3; i++) assertNull(bucket.tryConsume());
|
||||
assertEquals(DualCustomBucket.RESULT_BLOCKED_PER_SECOND, bucket.tryConsume());
|
||||
}
|
||||
|
||||
@Test
|
||||
void both_thresholdIsBinding_blockedByThreshold() {
|
||||
// threshold=3/MIN 이 binding: perSecond=100은 여유 있음
|
||||
DualCustomBucket bucket = manager.makeBucket(vo(100, 3, "MIN"));
|
||||
|
||||
for (int i = 0; i < 3; i++) assertNull(bucket.tryConsume());
|
||||
assertEquals(DualCustomBucket.RESULT_BLOCKED_THRESHOLD, bucket.tryConsume());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 비활성 / 조건 미충족 → null 반환
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
void inactiveVO_returnsNull() {
|
||||
InflowTargetVO vo = vo(5, 0, null);
|
||||
vo.setActivate(false);
|
||||
assertNull(manager.makeBucket(vo));
|
||||
}
|
||||
|
||||
@Test
|
||||
void noLimitsSet_returnsNull() {
|
||||
assertNull(manager.makeBucket(vo(0, 0, null)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user