SessionManager 캐시 조회 관리 API 추가 (/manage/session)
SessionManagerForIgnite/SessionManagerForEhcache가 관리하는 login, http-login, evict-master, terminal, convert-user-id, socket, outbound-access-token 캐시를 조회할 수 있는 관리용 API를 추가한다. outbound-access-token 캐시는 Ignite 백엔드에서만 지원하며, Ehcache 백엔드에서는 success=false로 응답하고 500을 반환하지 않는다.
This commit is contained in:
@@ -0,0 +1,312 @@
|
||||
{
|
||||
"info": {
|
||||
"_postman_id": "c3f4a1e0-9b2d-4a11-8e5a-session-cache-0001",
|
||||
"name": "SessionCacheManage API (/manage/session)",
|
||||
"description": "SessionManager(SessionManagerForIgnite/SessionManagerForEhcache) 캐시 조회 API 샘플 모음.\n\n사용 전 확인사항:\n1. Collection Variables 의 baseUrl 을 실제 서버 주소로 변경\n2. 서버가 Ignite 캐시 백엔드로 기동된 경우에만 outbound-access-token 캐시가 채워짐 (Ehcache 백엔드는 success=false로 응답, 정상 동작임)\n3. 모든 요청은 조회 전용(GET)이며 실제 세션/캐시 데이터를 변경하지 않는다.",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
"variable": [
|
||||
{ "key": "baseUrl", "value": "http://localhost:8080", "type": "string" }
|
||||
],
|
||||
"item": [
|
||||
{
|
||||
"name": "1. 캐시 요약 / 마스터 인스턴스",
|
||||
"item": [
|
||||
{
|
||||
"name": "전체 캐시 요약 (구현체별 XML)",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/manage/session/cache/status",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["manage", "session", "cache", "status"]
|
||||
},
|
||||
"description": "Ignite/Ehcache 구현체가 각자 형식으로 만든 전체 캐시 이름/크기 요약 텍스트(XML)를 반환한다."
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"const json = pm.response.json();",
|
||||
"pm.test('status 200', () => pm.response.to.have.status(200));",
|
||||
"pm.test('success = true', () => pm.expect(json.success).to.eql(true));",
|
||||
"pm.test('data는 문자열(XML)', () => pm.expect(json.data).to.be.a('string'));",
|
||||
"pm.test('Caches 루트 태그 포함', () => pm.expect(json.data).to.include('<Caches'));"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "현재 evict master 인스턴스 조회",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/manage/session/master-inst-id",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["manage", "session", "master-inst-id"]
|
||||
},
|
||||
"description": "싱글 어댑터 처리를 담당하는 현재 master 서버 인스턴스명을 반환한다. 아직 선출되지 않았으면 data가 null일 수 있다."
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"const json = pm.response.json();",
|
||||
"pm.test('status 200', () => pm.response.to.have.status(200));",
|
||||
"pm.test('success = true', () => pm.expect(json.success).to.eql(true));",
|
||||
"console.log('master instance id:', json.data);"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "2. 개별 캐시 조회",
|
||||
"item": [
|
||||
{
|
||||
"name": "로그인(WebSocket) 캐시",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/manage/session/cache/login",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["manage", "session", "cache", "login"]
|
||||
}
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"const json = pm.response.json();",
|
||||
"pm.test('status 200', () => pm.response.to.have.status(200));",
|
||||
"pm.test('success = true', () => pm.expect(json.success).to.eql(true));",
|
||||
"pm.test('name = loginSession', () => pm.expect(json.data.name).to.eql('loginSession'));",
|
||||
"pm.test('keys는 배열', () => pm.expect(json.data.keys).to.be.an('array'));",
|
||||
"pm.test('size는 keys 개수와 일치', () => pm.expect(json.data.size).to.eql(json.data.keys.length));"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "HTTP 로그인 캐시",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/manage/session/cache/http-login",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["manage", "session", "cache", "http-login"]
|
||||
}
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"const json = pm.response.json();",
|
||||
"pm.test('status 200', () => pm.response.to.have.status(200));",
|
||||
"pm.test('success = true', () => pm.expect(json.success).to.eql(true));",
|
||||
"pm.test('name = httpLogin', () => pm.expect(json.data.name).to.eql('httpLogin'));",
|
||||
"pm.test('keys는 배열', () => pm.expect(json.data.keys).to.be.an('array'));"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "싱글 어댑터 evict-master 캐시",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/manage/session/cache/evict-master",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["manage", "session", "cache", "evict-master"]
|
||||
}
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"const json = pm.response.json();",
|
||||
"pm.test('status 200', () => pm.response.to.have.status(200));",
|
||||
"pm.test('success = true', () => pm.expect(json.success).to.eql(true));",
|
||||
"pm.test('name = evictMasterCache', () => pm.expect(json.data.name).to.eql('evictMasterCache'));",
|
||||
"pm.test('keys는 배열', () => pm.expect(json.data.keys).to.be.an('array'));"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "단말(ATM 등) 캐시",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/manage/session/cache/terminal",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["manage", "session", "cache", "terminal"]
|
||||
}
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"const json = pm.response.json();",
|
||||
"pm.test('status 200', () => pm.response.to.have.status(200));",
|
||||
"pm.test('success = true', () => pm.expect(json.success).to.eql(true));",
|
||||
"pm.test('name = terminalSession', () => pm.expect(json.data.name).to.eql('terminalSession'));",
|
||||
"pm.test('keys는 배열', () => pm.expect(json.data.keys).to.be.an('array'));"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "단말ID→사용자ID 변환 캐시",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/manage/session/cache/convert-user-id",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["manage", "session", "cache", "convert-user-id"]
|
||||
}
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"const json = pm.response.json();",
|
||||
"pm.test('status 200', () => pm.response.to.have.status(200));",
|
||||
"pm.test('success = true', () => pm.expect(json.success).to.eql(true));",
|
||||
"pm.test('name = convertUserIdSession', () => pm.expect(json.data.name).to.eql('convertUserIdSession'));",
|
||||
"pm.test('keys는 배열', () => pm.expect(json.data.keys).to.be.an('array'));"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "소켓 세션 캐시",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/manage/session/cache/socket",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["manage", "session", "cache", "socket"]
|
||||
}
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"const json = pm.response.json();",
|
||||
"pm.test('status 200', () => pm.response.to.have.status(200));",
|
||||
"pm.test('success = true', () => pm.expect(json.success).to.eql(true));",
|
||||
"pm.test('name = adapterSession', () => pm.expect(json.data.name).to.eql('adapterSession'));",
|
||||
"pm.test('keys는 배열', () => pm.expect(json.data.keys).to.be.an('array'));"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "아웃바운드 AccessToken 캐시 (Ignite 전용)",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/manage/session/cache/outbound-access-token",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["manage", "session", "cache", "outbound-access-token"]
|
||||
},
|
||||
"description": "Ignite 백엔드에서만 지원한다. Ehcache 백엔드로 기동된 서버라면 success=false + message로 응답하며, 이는 정상 동작이다(서버 500 아님)."
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"const json = pm.response.json();",
|
||||
"pm.test('status 200', () => pm.response.to.have.status(200));",
|
||||
"if (json.success) {",
|
||||
" pm.test('data.name 존재', () => pm.expect(json.data.name).to.be.a('string'));",
|
||||
" pm.test('data.keys는 배열', () => pm.expect(json.data.keys).to.be.an('array'));",
|
||||
"} else {",
|
||||
" pm.test('미지원 백엔드는 message 포함', () => pm.expect(json.message).to.be.a('string'));",
|
||||
" console.log('OutboundAccessToken 캐시 미지원 백엔드:', json.message);",
|
||||
"}"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "3. 전체 캐시 목록",
|
||||
"item": [
|
||||
{
|
||||
"name": "모든 캐시 한번에 조회",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/manage/session/cache/all",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["manage", "session", "cache", "all"]
|
||||
},
|
||||
"description": "login/http-login/evict-master/terminal/convert-user-id/socket 6종은 항상 포함되고, outbound-access-token은 Ignite 백엔드일 때만 추가되어 count가 6 또는 7이 된다."
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
"const json = pm.response.json();",
|
||||
"pm.test('status 200', () => pm.response.to.have.status(200));",
|
||||
"pm.test('success = true', () => pm.expect(json.success).to.eql(true));",
|
||||
"pm.test('data는 배열', () => pm.expect(json.data).to.be.an('array'));",
|
||||
"pm.test('count는 6 또는 7 (백엔드에 따라 outbound-access-token 포함 여부 다름)', () => {",
|
||||
" pm.expect(json.count).to.be.oneOf([6, 7]);",
|
||||
"});",
|
||||
"pm.test('count와 data.length 일치', () => pm.expect(json.count).to.eql(json.data.length));",
|
||||
"json.data.forEach((cache) => {",
|
||||
" pm.test(`캐시 [${cache.name}] 는 keys 배열을 가짐`, () => pm.expect(cache.keys).to.be.an('array'));",
|
||||
"});"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -246,6 +246,8 @@ public abstract class SessionManager implements Lifecycle {
|
||||
|
||||
public abstract CacheInfoVO getSocketCache();
|
||||
|
||||
public abstract CacheInfoVO getOutboundAccessTokenCache();
|
||||
|
||||
public abstract String getMasterInstId();
|
||||
|
||||
// Convert terminalId to userId
|
||||
|
||||
@@ -887,7 +887,12 @@ public class SessionManagerForEhcache extends SessionManager {
|
||||
public CacheInfoVO getSocketCache() {
|
||||
return convert(cacheSocket);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CacheInfoVO getOutboundAccessTokenCache() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putWebSocketTimeout(String key, SessionVO value) {
|
||||
cacheWebSocketTimeout.put(new Element(key, value));
|
||||
|
||||
@@ -818,6 +818,11 @@ public class SessionManagerForIgnite extends SessionManager {
|
||||
public CacheInfoVO getSocketCache() {
|
||||
return convert(cacheSocket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheInfoVO getOutboundAccessTokenCache() {
|
||||
return convert(cacheOutBoundAccessToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putWebSocketTimeout(String key, SessionVO value) {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.eactive.eai.manage.session;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.eactive.eai.common.session.CacheInfoVO;
|
||||
|
||||
/**
|
||||
* SessionManager(SessionManagerForIgnite/SessionManagerForEhcache) 캐시 상태 모니터링 API.
|
||||
*
|
||||
* GET /manage/session/cache/status → 전체 캐시 요약 (구현체별 XML 텍스트, Ignite/Ehcache)
|
||||
* GET /manage/session/cache/all → 개별 캐시(login/http-login/evict-master/terminal/convert-user-id/socket) 목록
|
||||
* GET /manage/session/cache/login → 로그인(WebSocket) 캐시
|
||||
* GET /manage/session/cache/http-login → HTTP 로그인 캐시
|
||||
* GET /manage/session/cache/evict-master → 싱글 어댑터 evict master 캐시
|
||||
* GET /manage/session/cache/terminal → 단말(ATM 등) 캐시
|
||||
* GET /manage/session/cache/convert-user-id → 단말ID → 사용자ID 변환 캐시
|
||||
* GET /manage/session/cache/socket → 소켓 세션 캐시
|
||||
* GET /manage/session/cache/outbound-access-token → 아웃바운드 AccessTokenVO 캐시 (Ignite 백엔드에서만 지원)
|
||||
* GET /manage/session/master-inst-id → 현재 evict master 인스턴스명
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/manage/session")
|
||||
public class SessionCacheManageController {
|
||||
|
||||
@Autowired
|
||||
private SessionCacheManageService sessionCacheManageService;
|
||||
|
||||
@GetMapping("/cache/status")
|
||||
public ResponseEntity<?> getCacheStatus() {
|
||||
return ok(sessionCacheManageService.getCacheStatus());
|
||||
}
|
||||
|
||||
@GetMapping("/master-inst-id")
|
||||
public ResponseEntity<?> getMasterInstId() {
|
||||
return ok(sessionCacheManageService.getMasterInstId());
|
||||
}
|
||||
|
||||
@GetMapping("/cache/all")
|
||||
public ResponseEntity<?> getAllCaches() {
|
||||
List<CacheInfoVO> list = sessionCacheManageService.getAllCaches();
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("data", list);
|
||||
result.put("count", list.size());
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
@GetMapping("/cache/login")
|
||||
public ResponseEntity<?> getLoginCache() {
|
||||
return ok(sessionCacheManageService.getLoginCache());
|
||||
}
|
||||
|
||||
@GetMapping("/cache/http-login")
|
||||
public ResponseEntity<?> getHttpLoginCache() {
|
||||
return ok(sessionCacheManageService.getHttpLoginCache());
|
||||
}
|
||||
|
||||
@GetMapping("/cache/evict-master")
|
||||
public ResponseEntity<?> getEvictMasterCache() {
|
||||
return ok(sessionCacheManageService.getEvictMasterCache());
|
||||
}
|
||||
|
||||
@GetMapping("/cache/terminal")
|
||||
public ResponseEntity<?> getTerminalCache() {
|
||||
return ok(sessionCacheManageService.getTerminalCache());
|
||||
}
|
||||
|
||||
@GetMapping("/cache/convert-user-id")
|
||||
public ResponseEntity<?> getConvertUserIdCache() {
|
||||
return ok(sessionCacheManageService.getConvertUserIdCache());
|
||||
}
|
||||
|
||||
@GetMapping("/cache/socket")
|
||||
public ResponseEntity<?> getSocketCache() {
|
||||
return ok(sessionCacheManageService.getSocketCache());
|
||||
}
|
||||
|
||||
@GetMapping("/cache/outbound-access-token")
|
||||
public ResponseEntity<?> getOutboundAccessTokenCache() {
|
||||
try {
|
||||
return ok(sessionCacheManageService.getOutboundAccessTokenCache());
|
||||
} catch (UnsupportedOperationException e) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", false);
|
||||
result.put("message", "현재 SessionManager 백엔드는 OutboundAccessToken 캐시를 지원하지 않습니다.");
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseEntity<?> ok(Object data) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("data", data);
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.eactive.eai.manage.session;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.eactive.eai.common.session.CacheInfoVO;
|
||||
import com.eactive.eai.common.session.SessionManager;
|
||||
|
||||
/**
|
||||
* SessionManager(SessionManagerForIgnite/SessionManagerForEhcache) 캐시 조회를 위임하는 서비스.
|
||||
*
|
||||
* SessionManager.getInstance() 는 설정(session_manager.cacheType)에 따라 Ignite/Ehcache
|
||||
* 구현체 중 하나의 싱글턴을 반환하며, 두 구현체 모두 아래 조회 메서드를 지원한다.
|
||||
*/
|
||||
@Service
|
||||
public class SessionCacheManageService {
|
||||
|
||||
public String getCacheStatus() {
|
||||
return SessionManager.getInstance().getCacheStatus();
|
||||
}
|
||||
|
||||
public String getMasterInstId() {
|
||||
return SessionManager.getInstance().getMasterInstId();
|
||||
}
|
||||
|
||||
public CacheInfoVO getLoginCache() {
|
||||
return SessionManager.getInstance().getLoginCache();
|
||||
}
|
||||
|
||||
public CacheInfoVO getHttpLoginCache() {
|
||||
return SessionManager.getInstance().getHttpLoginCache();
|
||||
}
|
||||
|
||||
public CacheInfoVO getEvictMasterCache() {
|
||||
return SessionManager.getInstance().getEvictMasterCache();
|
||||
}
|
||||
|
||||
public CacheInfoVO getTerminalCache() {
|
||||
return SessionManager.getInstance().getTerminalCache();
|
||||
}
|
||||
|
||||
public CacheInfoVO getConvertUserIdCache() {
|
||||
return SessionManager.getInstance().getConvertUserIdCache();
|
||||
}
|
||||
|
||||
public CacheInfoVO getSocketCache() {
|
||||
return SessionManager.getInstance().getSocketCache();
|
||||
}
|
||||
|
||||
public CacheInfoVO getOutboundAccessTokenCache() {
|
||||
return SessionManager.getInstance().getOutboundAccessTokenCache();
|
||||
}
|
||||
|
||||
public List<CacheInfoVO> getAllCaches() {
|
||||
List<CacheInfoVO> result = new ArrayList<>();
|
||||
result.add(getLoginCache());
|
||||
result.add(getHttpLoginCache());
|
||||
result.add(getEvictMasterCache());
|
||||
result.add(getTerminalCache());
|
||||
result.add(getConvertUserIdCache());
|
||||
result.add(getSocketCache());
|
||||
try {
|
||||
result.add(getOutboundAccessTokenCache());
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Ehcache 백엔드는 OutboundAccessToken 캐시를 지원하지 않는다.
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package com.eactive.eai.manage.session;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.eactive.eai.common.session.CacheInfoVO;
|
||||
|
||||
/**
|
||||
* SessionCacheManageController 단위 테스트.
|
||||
*
|
||||
* <p>Spring MVC를 기동하지 않고 컨트롤러를 직접 인스턴스화하여
|
||||
* 응답 구조(success, data, count, message)를 검증한다.
|
||||
*/
|
||||
class SessionCacheManageControllerTest {
|
||||
|
||||
private SessionCacheManageController controller;
|
||||
private SessionCacheManageService mockService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
controller = new SessionCacheManageController();
|
||||
mockService = mock(SessionCacheManageService.class);
|
||||
ReflectionTestUtils.setField(controller, "sessionCacheManageService", mockService);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helper
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private CacheInfoVO makeCacheInfo(String name) {
|
||||
CacheInfoVO vo = new CacheInfoVO();
|
||||
vo.setName(name);
|
||||
vo.setSize(3);
|
||||
vo.setKeys(Arrays.asList("a", "b", "c"));
|
||||
vo.setTimeToLive(60000);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> body(ResponseEntity<?> response) {
|
||||
return (Map<String, Object>) response.getBody();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 캐시 요약 / 마스터 인스턴스
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("getCacheStatus() — success=true, data에 원문 그대로 반환")
|
||||
void getCacheStatus_returnsRawStatusString() {
|
||||
when(mockService.getCacheStatus()).thenReturn("<Caches type=\"Ignite\"></Caches>");
|
||||
|
||||
Map<String, Object> body = body(controller.getCacheStatus());
|
||||
|
||||
assertEquals(Boolean.TRUE, body.get("success"));
|
||||
assertEquals("<Caches type=\"Ignite\"></Caches>", body.get("data"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getMasterInstId() — success=true, data에 인스턴스명 반환")
|
||||
void getMasterInstId_returnsInstanceName() {
|
||||
when(mockService.getMasterInstId()).thenReturn("SERVER01");
|
||||
|
||||
Map<String, Object> body = body(controller.getMasterInstId());
|
||||
|
||||
assertEquals(Boolean.TRUE, body.get("success"));
|
||||
assertEquals("SERVER01", body.get("data"));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 개별 캐시 조회
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("getLoginCache() — success=true, CacheInfoVO 반환")
|
||||
void getLoginCache_returnsCacheInfo() {
|
||||
CacheInfoVO vo = makeCacheInfo("LoginCache");
|
||||
when(mockService.getLoginCache()).thenReturn(vo);
|
||||
|
||||
ResponseEntity<?> response = controller.getLoginCache();
|
||||
Map<String, Object> body = body(response);
|
||||
|
||||
assertEquals(200, response.getStatusCodeValue());
|
||||
assertEquals(Boolean.TRUE, body.get("success"));
|
||||
assertSame(vo, body.get("data"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getHttpLoginCache() — success=true")
|
||||
void getHttpLoginCache_returnsCacheInfo() {
|
||||
CacheInfoVO vo = makeCacheInfo("HttpLoginCache");
|
||||
when(mockService.getHttpLoginCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, body(controller.getHttpLoginCache()).get("data"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getEvictMasterCache() — success=true")
|
||||
void getEvictMasterCache_returnsCacheInfo() {
|
||||
CacheInfoVO vo = makeCacheInfo("EvictMasterCache");
|
||||
when(mockService.getEvictMasterCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, body(controller.getEvictMasterCache()).get("data"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getTerminalCache() — success=true")
|
||||
void getTerminalCache_returnsCacheInfo() {
|
||||
CacheInfoVO vo = makeCacheInfo("TerminalCache");
|
||||
when(mockService.getTerminalCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, body(controller.getTerminalCache()).get("data"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getConvertUserIdCache() — success=true")
|
||||
void getConvertUserIdCache_returnsCacheInfo() {
|
||||
CacheInfoVO vo = makeCacheInfo("ConvertUserIdCache");
|
||||
when(mockService.getConvertUserIdCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, body(controller.getConvertUserIdCache()).get("data"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getSocketCache() — success=true")
|
||||
void getSocketCache_returnsCacheInfo() {
|
||||
CacheInfoVO vo = makeCacheInfo("SocketCache");
|
||||
when(mockService.getSocketCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, body(controller.getSocketCache()).get("data"));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 전체 캐시 목록
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("getAllCaches() — 목록과 개수 반환")
|
||||
void getAllCaches_returnsListAndCount() {
|
||||
List<CacheInfoVO> list = Arrays.asList(makeCacheInfo("A"), makeCacheInfo("B"));
|
||||
when(mockService.getAllCaches()).thenReturn(list);
|
||||
|
||||
Map<String, Object> body = body(controller.getAllCaches());
|
||||
|
||||
assertEquals(Boolean.TRUE, body.get("success"));
|
||||
assertSame(list, body.get("data"));
|
||||
assertEquals(2, body.get("count"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getAllCaches() — 빈 목록이면 count=0")
|
||||
void getAllCaches_emptyList_countZero() {
|
||||
when(mockService.getAllCaches()).thenReturn(Arrays.asList());
|
||||
|
||||
Map<String, Object> body = body(controller.getAllCaches());
|
||||
|
||||
assertEquals(Boolean.TRUE, body.get("success"));
|
||||
assertEquals(0, body.get("count"));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// OutboundAccessToken 캐시 — 지원/미지원 백엔드
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("getOutboundAccessTokenCache() — 지원 시 success=true, CacheInfoVO 반환")
|
||||
void getOutboundAccessTokenCache_supported_returnsCacheInfo() {
|
||||
CacheInfoVO vo = makeCacheInfo("OutBoundAccessToken");
|
||||
when(mockService.getOutboundAccessTokenCache()).thenReturn(vo);
|
||||
|
||||
Map<String, Object> body = body(controller.getOutboundAccessTokenCache());
|
||||
|
||||
assertEquals(Boolean.TRUE, body.get("success"));
|
||||
assertSame(vo, body.get("data"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getOutboundAccessTokenCache() — Ehcache 등 미지원 백엔드는 500이 아니라 success=false로 응답")
|
||||
void getOutboundAccessTokenCache_unsupported_successFalseNotServerError() {
|
||||
when(mockService.getOutboundAccessTokenCache()).thenThrow(new UnsupportedOperationException());
|
||||
|
||||
ResponseEntity<?> response = controller.getOutboundAccessTokenCache();
|
||||
Map<String, Object> body = body(response);
|
||||
|
||||
assertEquals(200, response.getStatusCodeValue());
|
||||
assertEquals(Boolean.FALSE, body.get("success"));
|
||||
assertNotNull(body.get("message"));
|
||||
assertFalse(body.containsKey("data"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package com.eactive.eai.manage.session;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
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.Test;
|
||||
|
||||
import com.eactive.eai.common.session.CacheInfoVO;
|
||||
import com.eactive.eai.common.session.SessionManager;
|
||||
|
||||
/**
|
||||
* SessionCacheManageService 단위 테스트.
|
||||
*
|
||||
* <p>SessionManager.getInstance()는 static singleton(private static instance 필드)이라
|
||||
* 실제 Ignite/Ehcache를 기동하지 않고, 리플렉션으로 그 필드를 Mockito mock으로 교체하여 검증한다.
|
||||
* 테스트가 끝나면 반드시 필드를 원복(null)하여 다른 테스트에 영향을 주지 않는다.
|
||||
*/
|
||||
class SessionCacheManageServiceTest {
|
||||
|
||||
private SessionManager mockSessionManager;
|
||||
private final SessionCacheManageService service = new SessionCacheManageService();
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
mockSessionManager = mock(SessionManager.class);
|
||||
setInstance(mockSessionManager);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws Exception {
|
||||
setInstance(null);
|
||||
}
|
||||
|
||||
private void setInstance(SessionManager manager) throws Exception {
|
||||
Field field = SessionManager.class.getDeclaredField("instance");
|
||||
field.setAccessible(true);
|
||||
field.set(null, manager);
|
||||
}
|
||||
|
||||
private CacheInfoVO makeCacheInfo(String name, int size) {
|
||||
CacheInfoVO vo = new CacheInfoVO();
|
||||
vo.setName(name);
|
||||
vo.setSize(size);
|
||||
vo.setKeys(Arrays.asList("k1", "k2"));
|
||||
vo.setTimeToLive(60000);
|
||||
return vo;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 1. 단건 위임
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("1-1. getCacheStatus() — SessionManager.getCacheStatus() 위임")
|
||||
void testGetCacheStatus_delegates() {
|
||||
when(mockSessionManager.getCacheStatus()).thenReturn("<Caches type=\"Ignite\"></Caches>");
|
||||
|
||||
assertEquals("<Caches type=\"Ignite\"></Caches>", service.getCacheStatus());
|
||||
verify(mockSessionManager).getCacheStatus();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-2. getMasterInstId() — SessionManager.getMasterInstId() 위임")
|
||||
void testGetMasterInstId_delegates() {
|
||||
when(mockSessionManager.getMasterInstId()).thenReturn("SERVER01");
|
||||
|
||||
assertEquals("SERVER01", service.getMasterInstId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-3. getLoginCache() — 위임")
|
||||
void testGetLoginCache_delegates() {
|
||||
CacheInfoVO vo = makeCacheInfo("LoginCache", 2);
|
||||
when(mockSessionManager.getLoginCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, service.getLoginCache());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-4. getHttpLoginCache() — 위임")
|
||||
void testGetHttpLoginCache_delegates() {
|
||||
CacheInfoVO vo = makeCacheInfo("HttpLoginCache", 1);
|
||||
when(mockSessionManager.getHttpLoginCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, service.getHttpLoginCache());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-5. getEvictMasterCache() — 위임")
|
||||
void testGetEvictMasterCache_delegates() {
|
||||
CacheInfoVO vo = makeCacheInfo("EvictMasterCache", 1);
|
||||
when(mockSessionManager.getEvictMasterCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, service.getEvictMasterCache());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-6. getTerminalCache() — 위임")
|
||||
void testGetTerminalCache_delegates() {
|
||||
CacheInfoVO vo = makeCacheInfo("TerminalCache", 5);
|
||||
when(mockSessionManager.getTerminalCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, service.getTerminalCache());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-7. getConvertUserIdCache() — 위임")
|
||||
void testGetConvertUserIdCache_delegates() {
|
||||
CacheInfoVO vo = makeCacheInfo("ConvertUserIdCache", 3);
|
||||
when(mockSessionManager.getConvertUserIdCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, service.getConvertUserIdCache());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-8. getSocketCache() — 위임")
|
||||
void testGetSocketCache_delegates() {
|
||||
CacheInfoVO vo = makeCacheInfo("SocketCache", 4);
|
||||
when(mockSessionManager.getSocketCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, service.getSocketCache());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-9. getOutboundAccessTokenCache() — 위임")
|
||||
void testGetOutboundAccessTokenCache_delegates() {
|
||||
CacheInfoVO vo = makeCacheInfo("OutBoundAccessToken", 7);
|
||||
when(mockSessionManager.getOutboundAccessTokenCache()).thenReturn(vo);
|
||||
|
||||
assertSame(vo, service.getOutboundAccessTokenCache());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-10. getOutboundAccessTokenCache() — 미지원 백엔드는 예외 그대로 전파")
|
||||
void testGetOutboundAccessTokenCache_unsupported_throwsException() {
|
||||
when(mockSessionManager.getOutboundAccessTokenCache()).thenThrow(new UnsupportedOperationException());
|
||||
|
||||
assertThrows(UnsupportedOperationException.class, service::getOutboundAccessTokenCache);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 2. getAllCaches() 집계
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("2-1. getAllCaches() — OutboundAccessToken 포함 7개 캐시 반환")
|
||||
void testGetAllCaches_outboundSupported_returnsSevenCaches() {
|
||||
when(mockSessionManager.getLoginCache()).thenReturn(makeCacheInfo("LoginCache", 1));
|
||||
when(mockSessionManager.getHttpLoginCache()).thenReturn(makeCacheInfo("HttpLoginCache", 1));
|
||||
when(mockSessionManager.getEvictMasterCache()).thenReturn(makeCacheInfo("EvictMasterCache", 1));
|
||||
when(mockSessionManager.getTerminalCache()).thenReturn(makeCacheInfo("TerminalCache", 1));
|
||||
when(mockSessionManager.getConvertUserIdCache()).thenReturn(makeCacheInfo("ConvertUserIdCache", 1));
|
||||
when(mockSessionManager.getSocketCache()).thenReturn(makeCacheInfo("SocketCache", 1));
|
||||
when(mockSessionManager.getOutboundAccessTokenCache()).thenReturn(makeCacheInfo("OutBoundAccessToken", 1));
|
||||
|
||||
List<CacheInfoVO> all = service.getAllCaches();
|
||||
|
||||
assertEquals(7, all.size());
|
||||
assertTrue(all.stream().anyMatch(c -> "OutBoundAccessToken".equals(c.getName())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("2-2. getAllCaches() — OutboundAccessToken 미지원(Ehcache)이면 예외 없이 6개만 반환")
|
||||
void testGetAllCaches_outboundUnsupported_excludedGracefully() {
|
||||
when(mockSessionManager.getLoginCache()).thenReturn(makeCacheInfo("LoginCache", 1));
|
||||
when(mockSessionManager.getHttpLoginCache()).thenReturn(makeCacheInfo("HttpLoginCache", 1));
|
||||
when(mockSessionManager.getEvictMasterCache()).thenReturn(makeCacheInfo("EvictMasterCache", 1));
|
||||
when(mockSessionManager.getTerminalCache()).thenReturn(makeCacheInfo("TerminalCache", 1));
|
||||
when(mockSessionManager.getConvertUserIdCache()).thenReturn(makeCacheInfo("ConvertUserIdCache", 1));
|
||||
when(mockSessionManager.getSocketCache()).thenReturn(makeCacheInfo("SocketCache", 1));
|
||||
when(mockSessionManager.getOutboundAccessTokenCache()).thenThrow(new UnsupportedOperationException());
|
||||
|
||||
List<CacheInfoVO> all = assertDoesNotThrow(service::getAllCaches);
|
||||
|
||||
assertEquals(6, all.size());
|
||||
assertTrue(all.stream().noneMatch(c -> "OutBoundAccessToken".equals(c.getName())));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user