암복호화 function 개발

- 암호화모듈서비스(CryptoModuleService)를 호출하여 암복호화 수행하는 function
This commit is contained in:
curry772
2026-05-26 10:15:22 +09:00
parent 9fcce55966
commit bf91d02be7
3 changed files with 324 additions and 0 deletions
@@ -0,0 +1,220 @@
package com.eactive.eai.custom.transformer.function;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Stack;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.nfunk.jep.ParseException;
import org.nfunk.jep.function.PostfixMathCommand;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.support.GenericApplicationContext;
import com.eactive.eai.common.security.CryptoModuleService;
import com.eactive.eai.common.util.ApplicationContextProvider;
@TestMethodOrder(MethodOrderer.DisplayName.class)
class DJBErpCryptoFunctionTest {
private static final String MODULE_NAME = "TEST_MODULE";
private static final String PLAIN_TEXT = "plaintext-value";
private static final byte[] PLAIN_BYTES = PLAIN_TEXT.getBytes(StandardCharsets.UTF_8);
private static final byte[] CIPHER_BYTES = new byte[]{0x10, 0x20, 0x30, 0x40};
private static final String ENC_BASE64 = Base64.getEncoder().encodeToString(CIPHER_BYTES);
private static GenericApplicationContext ctx;
private static CryptoModuleService mockService;
private DJBErpDecryt decryt;
private DJBErpEncryt encryt;
// =========================================================================
// 클래스 초기화
// =========================================================================
@BeforeAll
static void setUpClass() throws Exception {
mockService = mock(CryptoModuleService.class);
ctx = new GenericApplicationContext();
ctx.getBeanFactory().registerSingleton("cryptoModuleService", mockService);
ctx.registerBeanDefinition("applicationContextProvider",
BeanDefinitionBuilder.genericBeanDefinition(ApplicationContextProvider.class)
.getBeanDefinition());
ctx.refresh();
}
@AfterAll
static void tearDownClass() {
if (ctx != null) ctx.close();
}
@BeforeEach
void setUp() throws Exception {
reset(mockService);
when(mockService.decrypt(any(), anyMap(), isNull(), any(byte[].class))).thenReturn(PLAIN_BYTES);
when(mockService.encrypt(any(), anyMap(), isNull(), any(byte[].class))).thenReturn(CIPHER_BYTES);
decryt = new DJBErpDecryt();
encryt = new DJBErpEncryt();
}
// =========================================================================
// 1. DJBErpDecryt
// =========================================================================
@Test
@DisplayName("1-1. 정상 복호화: (encBase64, moduleName) → 평문이 스택에 push된다")
void decryt_normalCase_pushesPlainText() throws Exception {
Stack<Object> stack = buildStack(ENC_BASE64, MODULE_NAME);
setParamCount(decryt, 2);
decryt.run(stack);
assertEquals(PLAIN_TEXT, stack.pop());
assertTrue(stack.isEmpty(), "결과 push 후 스택에 잔여 항목이 없어야 한다");
verify(mockService).decrypt(eq(MODULE_NAME), anyMap(), isNull(), any(byte[].class));
}
@Test
@DisplayName("1-2. 파라미터 수가 2가 아니면 ParseException")
void decryt_wrongParamCount_throwsParseException() throws Exception {
Stack<Object> stack = buildStack(ENC_BASE64, MODULE_NAME);
setParamCount(decryt, 3);
assertThrows(ParseException.class, () -> decryt.run(stack));
}
@Test
@DisplayName("1-3. 유효하지 않은 Base64 입력 → ParseException")
void decryt_invalidBase64_throwsParseException() throws Exception {
Stack<Object> stack = buildStack("not-valid-base64!!!", MODULE_NAME);
setParamCount(decryt, 2);
assertThrows(ParseException.class, () -> decryt.run(stack));
}
@Test
@DisplayName("1-4. 암호화 서비스 예외 → 'DJBErpDecryt 복호화 실패' ParseException으로 래핑")
void decryt_serviceThrows_wrapsInParseException() throws Exception {
when(mockService.decrypt(any(), anyMap(), isNull(), any(byte[].class)))
.thenThrow(new RuntimeException("모듈 오류"));
Stack<Object> stack = buildStack(ENC_BASE64, MODULE_NAME);
setParamCount(decryt, 2);
ParseException ex = assertThrows(ParseException.class, () -> decryt.run(stack));
assertTrue(ex.getMessage().contains("DJBErpDecryt 복호화 실패"));
}
@Test
@DisplayName("1-5. moduleName이 CryptoModuleService에 그대로 전달된다")
void decryt_moduleNamePassedToService() throws Exception {
String customModule = "CUSTOM_AES_MODULE";
Stack<Object> stack = buildStack(ENC_BASE64, customModule);
setParamCount(decryt, 2);
decryt.run(stack);
verify(mockService).decrypt(eq(customModule), anyMap(), isNull(), any(byte[].class));
}
// =========================================================================
// 2. DJBErpEncryt
// =========================================================================
@Test
@DisplayName("2-1. 정상 암호화: (plainText, moduleName) → Base64가 스택에 push된다")
void encryt_normalCase_pushesBase64() throws Exception {
Stack<Object> stack = buildStack(PLAIN_TEXT, MODULE_NAME);
setParamCount(encryt, 2);
encryt.run(stack);
assertEquals(ENC_BASE64, stack.pop());
assertTrue(stack.isEmpty(), "결과 push 후 스택에 잔여 항목이 없어야 한다");
verify(mockService).encrypt(eq(MODULE_NAME), anyMap(), isNull(), any(byte[].class));
}
@Test
@DisplayName("2-2. 파라미터 수가 2가 아니면 ParseException")
void encryt_wrongParamCount_throwsParseException() throws Exception {
Stack<Object> stack = buildStack(PLAIN_TEXT, MODULE_NAME);
setParamCount(encryt, 3);
assertThrows(ParseException.class, () -> encryt.run(stack));
}
@Test
@DisplayName("2-3. 암호화 서비스 예외 → 'DJBErpEncryt 암호화 실패' ParseException으로 래핑")
void encryt_serviceThrows_wrapsInParseException() throws Exception {
when(mockService.encrypt(any(), anyMap(), isNull(), any(byte[].class)))
.thenThrow(new RuntimeException("모듈 오류"));
Stack<Object> stack = buildStack(PLAIN_TEXT, MODULE_NAME);
setParamCount(encryt, 2);
ParseException ex = assertThrows(ParseException.class, () -> encryt.run(stack));
assertTrue(ex.getMessage().contains("DJBErpEncryt 암호화 실패"));
}
@Test
@DisplayName("2-4. moduleName이 CryptoModuleService에 그대로 전달된다")
void encryt_moduleNamePassedToService() throws Exception {
String customModule = "CUSTOM_AES_MODULE";
Stack<Object> stack = buildStack(PLAIN_TEXT, customModule);
setParamCount(encryt, 2);
encryt.run(stack);
verify(mockService).encrypt(eq(customModule), anyMap(), isNull(), any(byte[].class));
}
@Test
@DisplayName("2-5. plainText가 UTF-8 바이트로 변환되어 서비스에 전달된다")
void encryt_plainTextEncodedAsUtf8() throws Exception {
String korean = "한글평문";
Stack<Object> stack = buildStack(korean, MODULE_NAME);
setParamCount(encryt, 2);
encryt.run(stack);
verify(mockService).encrypt(eq(MODULE_NAME), anyMap(), isNull(),
eq(korean.getBytes(StandardCharsets.UTF_8)));
}
// =========================================================================
// 헬퍼
// =========================================================================
@SuppressWarnings({"rawtypes", "unchecked"})
private Stack<Object> buildStack(Object... params) {
Stack stack = new Stack();
for (Object p : params) {
stack.push(p);
}
return stack;
}
private void setParamCount(PostfixMathCommand cmd, int count) throws Exception {
Field field = PostfixMathCommand.class.getDeclaredField("curNumberOfParameters");
field.setAccessible(true);
field.set(cmd, count);
}
}