Files
vpd-permission-poc/vpd-backoffice/src/test/java/com/cloudhandson/vpdbackoffice/service/McpSseServiceTest.java

259 lines
11 KiB
Java

package com.cloudhandson.vpdbackoffice.service;
import static org.assertj.core.api.Assertions.assertThat;
import com.cloudhandson.vpdbackoffice.config.BackofficeProperties;
import com.cloudhandson.vpdbackoffice.config.McpProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.jupiter.api.Test;
class McpSseServiceTest {
private final ObjectMapper objectMapper = new ObjectMapper();
private final SelectAiService selectAiService = new CapturingSelectAiService();
private final QaVectorService qaVectorService = new CapturingQaVectorService();
private final McpSseService service = new McpSseService(
selectAiService,
objectMapper,
new BackofficeProperties(
null,
null,
null,
null,
new BackofficeProperties.SelectAi("", "", "", "SGMP_POC_OCI_GPT54MINI")
),
new McpProperties(
"oracle.select_ai.test_data_text2sql",
"테스트 데이터 Text2SQL",
"테스트 데이터용 읽기 전용 SQL을 생성하고 실행합니다.",
"테스트 데이터의 조회 내용을 입력합니다.",
"oracle.select_ai.test_data_showprompt",
"테스트 데이터 SHOWPROMPT",
"테스트 데이터의 Select AI prompt를 조회합니다.",
"oracle.select_ai.test_qa_vector_search",
"테스트 예제 SQL 조회",
"테스트 질문에 사용할 예제 SQL을 조회합니다.",
"oracle.select_ai.test_qa_vector_store",
"테스트 예제 SQL 저장",
"테스트 예제 SQL을 저장합니다."
),
qaVectorService
);
@Test
void listsQueryShowpromptAndQaVectorToolsWithTheirInputs() {
ObjectNode response = service.handle("default", request(1, "tools/list"));
var tools = response.path("result").path("tools");
assertThat(tools).hasSize(4);
var selectAi = tools.get(0);
assertThat(selectAi.path("name").asText()).isEqualTo("oracle.select_ai.test_data_text2sql");
assertThat(selectAi.path("description").asText()).contains("SGMP_POC_OCI_GPT54MINI");
assertThat(selectAi.path("inputSchema").path("required"))
.extracting(node -> node.asText())
.contains("prompt");
assertThat(selectAi.path("inputSchema").path("properties").has("bearerToken")).isFalse();
assertThat(selectAi.path("inputSchema").path("properties").has("limit")).isFalse();
assertThat(selectAi.path("inputSchema").path("properties").has("conversationId")).isFalse();
var showprompt = tools.get(1);
assertThat(showprompt.path("name").asText())
.isEqualTo("oracle.select_ai.test_data_showprompt");
assertThat(showprompt.path("description").asText())
.contains("SGMP_POC_OCI_GPT54MINI")
.contains("Select AI prompt");
assertThat(showprompt.path("inputSchema").path("required"))
.extracting(node -> node.asText())
.contains("prompt");
var vectorSearch = tools.get(2);
assertThat(vectorSearch.path("name").asText())
.isEqualTo("oracle.select_ai.test_qa_vector_search");
assertThat(vectorSearch.path("inputSchema").path("properties").path("question").path("type").asText())
.isEqualTo("string");
assertThat(vectorSearch.path("inputSchema").path("properties").path("topK").path("default").asInt())
.isEqualTo(3);
var vectorStore = tools.get(3);
assertThat(vectorStore.path("name").asText())
.isEqualTo("oracle.select_ai.test_qa_vector_store");
assertThat(vectorStore.path("inputSchema").path("required"))
.extracting(node -> node.asText())
.contains("question", "answerSql");
}
@Test
void callsShowpromptWithoutExecutingGeneratedSql() {
ObjectNode request = request(4, "tools/call");
ObjectNode params = (ObjectNode) request.putObject("params");
params.put("name", "oracle.select_ai.test_data_showprompt");
params.putObject("arguments").put("prompt", "버블리즈 유저 마스터 조회해줘.");
ObjectNode response = service.handle("default", request, "user-bearer");
CapturingSelectAiService agentService = (CapturingSelectAiService) selectAiService;
assertThat(agentService.bearerToken).isEqualTo("user-bearer");
assertThat(agentService.prompt).isEqualTo("버블리즈 유저 마스터 조회해줘.");
assertThat(agentService.showpromptCalled).isTrue();
assertThat(response.path("result").path("isError").asBoolean()).isFalse();
assertThat(response.path("result").path("content").get(0).path("text").asText())
.contains("oracle.select_ai.test_data_showprompt")
.contains("SHOWPROMPT")
.contains("assembled Select AI prompt");
}
@Test
void callsVpdSelectAiThroughOrdsService() {
ObjectNode request = request(2, "tools/call");
ObjectNode params = (ObjectNode) request.putObject("params");
params.put("name", "oracle.select_ai.test_data_text2sql");
ObjectNode arguments = params.putObject("arguments");
arguments.put("prompt", "카제나 AU를 조회해 줘");
ObjectNode response = service.handle("default", request, "user-bearer");
CapturingSelectAiService agentService = (CapturingSelectAiService) selectAiService;
assertThat(agentService.bearerToken).isEqualTo("user-bearer");
assertThat(agentService.prompt).isEqualTo("카제나 AU를 조회해 줘");
assertThat(response.path("error").isMissingNode()).isTrue();
assertThat(response.path("result").path("isError").asBoolean()).isFalse();
String payload = response.path("result").path("content").get(0).path("text").asText();
assertThat(payload)
.contains("SGMP_POC_OCI_GPT54MINI")
.contains("SELECT 1 FROM DUAL")
.contains("SHOWSQL_AND_EXECUTED")
.contains("READ_ONLY_EXECUTED")
.contains("RESULT");
}
@Test
void returnsToolLevelDeniedResultWhenVpdTokenIsMissing() {
ObjectNode request = request(3, "tools/call");
ObjectNode params = (ObjectNode) request.putObject("params");
params.put("name", "oracle.select_ai.test_data_text2sql");
params.putObject("arguments").put("prompt", "카제나 AU를 조회해 줘");
ObjectNode response = service.handle("default", request, "");
assertThat(response.path("error").isMissingNode()).isTrue();
assertThat(response.path("result").path("isError").asBoolean()).isTrue();
assertThat(response.path("result").path("content").get(0).path("text").asText())
.contains("VPD_TOKEN_DENIED")
.contains("권한이 없습니다");
}
@Test
void searchesQaVectorExamplesForFewShotContext() {
ObjectNode request = request(5, "tools/call");
ObjectNode params = (ObjectNode) request.putObject("params");
params.put("name", "oracle.select_ai.test_qa_vector_search");
params.putObject("arguments").put("question", "active users by game").put("topK", 2);
ObjectNode response = service.handle("default", request, "user-bearer");
CapturingQaVectorService vectorService = (CapturingQaVectorService) qaVectorService;
assertThat(vectorService.bearerToken).isEqualTo("user-bearer");
assertThat(vectorService.question).isEqualTo("active users by game");
assertThat(vectorService.topK).isEqualTo(2);
assertThat(response.path("result").path("content").get(0).path("text").asText())
.contains("QA_VECTOR_SEARCH")
.contains("few-shot")
.contains("SELECT COUNT(*) FROM APP_USER");
}
@Test
void storesReviewedSelectAiExampleSql() {
ObjectNode request = request(6, "tools/call");
ObjectNode params = (ObjectNode) request.putObject("params");
params.put("name", "oracle.select_ai.test_qa_vector_store");
params.putObject("arguments")
.put("question", "active users by game")
.put("answerSql", "SELECT COUNT(*) FROM APP_USER")
.put("answer", "AU count");
ObjectNode response = service.handle("default", request, "user-bearer");
CapturingQaVectorService vectorService = (CapturingQaVectorService) qaVectorService;
assertThat(vectorService.answerSql).isEqualTo("SELECT COUNT(*) FROM APP_USER");
assertThat(response.path("result").path("content").get(0).path("text").asText())
.contains("QA_VECTOR_STORED")
.contains("exampleId");
}
private ObjectNode request(int id, String method) {
ObjectNode request = objectMapper.createObjectNode();
request.put("jsonrpc", "2.0");
request.put("id", id);
request.put("method", method);
return request;
}
private static final class CapturingSelectAiService extends SelectAiService {
private String bearerToken;
private String prompt;
private boolean showpromptCalled;
private CapturingSelectAiService() {
super(null, null, null, new ObjectMapper(), null);
}
@Override
public JsonNode generateAndExecute(String bearerToken, String prompt) {
this.bearerToken = bearerToken;
this.prompt = prompt;
ObjectNode response = new ObjectMapper().createObjectNode()
.put("profile", "SGMP_POC_OCI_GPT54MINI")
.put("generatedSql", "SELECT 1 FROM DUAL")
.put("status", "SHOWSQL_AND_EXECUTED")
.put("execution", "READ_ONLY_EXECUTED")
.put("rowCount", 1);
response.putArray("items").addObject().put("RESULT", 1);
return response;
}
@Override
public JsonNode generatePrompt(String bearerToken, String prompt) {
this.bearerToken = bearerToken;
this.prompt = prompt;
this.showpromptCalled = true;
return new ObjectMapper().createObjectNode()
.put("profile", "SGMP_POC_OCI_GPT54MINI")
.put("status", "SHOWPROMPT")
.put("selectAiPrompt", "assembled Select AI prompt");
}
}
private static final class CapturingQaVectorService extends QaVectorService {
private String bearerToken;
private String question;
private String answerSql;
private int topK;
private CapturingQaVectorService() {
super(null, null, null);
}
@Override
public VectorSearchResult search(String bearerToken, String question, int topK) {
this.bearerToken = bearerToken;
this.question = question;
this.topK = topK;
return new VectorSearchResult(question, topK, java.util.List.of(new VectorExample(
42L, "active user count", "SELECT COUNT(*) FROM APP_USER", "AU count",
"cohere.embed-v4.0", 0.12
)));
}
@Override
public VectorStoreResult store(String bearerToken, String question, String answerSql, String answer) {
this.bearerToken = bearerToken;
this.question = question;
this.answerSql = answerSql;
return new VectorStoreResult(77L, question, "cohere.embed-v4.0");
}
}
}