refs #743: validate advertised ADB MCP tools at startup

This commit is contained in:
devmrko
2026-08-03 11:07:15 +09:00
parent 22c0b571e2
commit 750bfbab5b
6 changed files with 303 additions and 2 deletions

View File

@@ -0,0 +1,117 @@
package com.cloudhandson.vpdbackoffice.service;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.core.JdbcTemplate;
class McpAgentToolStartupValidatorTest {
private final McpToolCatalog toolCatalog = mock(McpToolCatalog.class);
private final JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
private final McpAgentToolStartupValidator validator =
new McpAgentToolStartupValidator(toolCatalog, jdbcTemplate);
@Test
void acceptsEnabledAgentToolsAndChecksDuplicateTargetOnce() {
when(toolCatalog.tools()).thenReturn(List.of(
agentTool("resolve_hr_term", "HMM_HR_TERM_RESOLVER"),
agentTool("resolve_hr_alias", "HMM_HR_TERM_RESOLVER"),
agentTool("search_hr_policy", "HMM_HR_POLICY_SEARCH")
));
when(jdbcTemplate.queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL))
.thenReturn(List.of(
Map.of("TOOL_NAME", "HMM_HR_TERM_RESOLVER", "STATUS", "ENABLED"),
Map.of("TOOL_NAME", "HMM_HR_POLICY_SEARCH", "STATUS", "enabled")
));
validator.validateAdvertisedAgentTools();
verify(jdbcTemplate).queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL);
}
@Test
void rejectsMissingAgentTool() {
when(toolCatalog.tools()).thenReturn(List.of(
agentTool("search_hr_policy", "HMM_HR_POLICY_SEARCH")
));
when(jdbcTemplate.queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL))
.thenReturn(List.of());
assertThatThrownBy(validator::validateAdvertisedAgentTools)
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("missing=[HMM_HR_POLICY_SEARCH]");
}
@Test
void rejectsDisabledAgentTool() {
when(toolCatalog.tools()).thenReturn(List.of(
agentTool("resolve_hr_term", "HMM_HR_TERM_RESOLVER")
));
when(jdbcTemplate.queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL))
.thenReturn(List.of(
Map.of("tool_name", "hmm_hr_term_resolver", "status", "DISABLED")
));
assertThatThrownBy(validator::validateAdvertisedAgentTools)
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("notEnabled=[HMM_HR_TERM_RESOLVER=DISABLED]");
}
@Test
void skipsDatabaseLookupForApplicationNativeTools() {
when(toolCatalog.tools()).thenReturn(List.of(selectAiTool("search_hr_data")));
validator.validateAdvertisedAgentTools();
verify(jdbcTemplate, never())
.queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL);
}
@Test
void rejectsMetadataLookupFailure() {
when(toolCatalog.tools()).thenReturn(List.of(
agentTool("search_hr_policy", "HMM_HR_POLICY_SEARCH")
));
when(jdbcTemplate.queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL))
.thenThrow(new DataAccessResourceFailureException("metadata unavailable"));
assertThatThrownBy(validator::validateAdvertisedAgentTools)
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("USER_AI_AGENT_TOOLS")
.hasCauseInstanceOf(DataAccessResourceFailureException.class);
}
private McpToolDefinition agentTool(String publicName, String targetName) {
return new McpToolDefinition(
publicName,
publicName,
"description",
"query",
"query description",
"AGENT_TOOL",
targetName,
"P_QUERY"
);
}
private McpToolDefinition selectAiTool(String publicName) {
return new McpToolDefinition(
publicName,
publicName,
"description",
"query",
"query description",
"SELECT_AI",
"",
""
);
}
}