mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
test(skill): fix listAvailableSkills arity and cover agent-binding enforcement
This commit is contained in:
parent
3d8d266e3b
commit
39b6bdc522
@ -2,12 +2,17 @@ package vip.mate.tool.builtin;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.chat.model.ToolContext;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import vip.mate.agent.context.ChatOrigin;
|
||||
import vip.mate.llm.routing.AgentBindingResolver;
|
||||
import vip.mate.skill.runtime.SkillFileAccessPolicy;
|
||||
import vip.mate.skill.runtime.SkillRuntimeService;
|
||||
import vip.mate.skill.runtime.model.ResolvedSkill;
|
||||
import vip.mate.skill.usage.SkillUsageService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@ -30,7 +35,7 @@ class SkillFileToolTest {
|
||||
skill("ckjia-shopping", "mcp", false),
|
||||
skill("claude-code", "acp", false)));
|
||||
|
||||
String result = tool.listAvailableSkills("code", "acp", "ready", 1);
|
||||
String result = tool.listAvailableSkills("code", "acp", "ready", 1, null);
|
||||
|
||||
assertTrue(result.contains("claude-code"));
|
||||
assertFalse(result.contains("ckjia-shopping"));
|
||||
@ -136,6 +141,95 @@ class SkillFileToolTest {
|
||||
"No truncation banner should appear when caller did not opt into pagination");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("listAvailableSkills hides skills the agent is not bound to")
|
||||
void listAvailableSkillsRespectsAgentBindings() {
|
||||
SkillRuntimeService runtimeService = mock(SkillRuntimeService.class);
|
||||
SkillFileAccessPolicy accessPolicy = mock(SkillFileAccessPolicy.class);
|
||||
SkillUsageService usageService = mock(SkillUsageService.class);
|
||||
AgentBindingResolver bindingResolver = mock(AgentBindingResolver.class);
|
||||
SkillFileTool tool = new SkillFileTool(runtimeService, accessPolicy, usageService);
|
||||
ReflectionTestUtils.setField(tool, "agentBindingResolver", bindingResolver);
|
||||
|
||||
ResolvedSkill bound = skill("alpha-skill", "database", true);
|
||||
ResolvedSkill unbound = skill("beta-skill", "database", true);
|
||||
when(runtimeService.getActiveSkills()).thenReturn(List.of(bound, unbound));
|
||||
// Agent 42 is bound only to alpha-skill; beta-skill must not surface.
|
||||
when(bindingResolver.getBoundSkillIds(42L)).thenReturn(Set.of(bound.getId()));
|
||||
|
||||
ToolContext ctx = ChatOrigin.EMPTY.withAgent(42L).toToolContext();
|
||||
String result = tool.listAvailableSkills(null, null, null, 20, ctx);
|
||||
|
||||
assertTrue(result.contains("alpha-skill"));
|
||||
assertFalse(result.contains("beta-skill"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("readSkillFile denies a skill the agent is not bound to")
|
||||
void readSkillFileDeniesUnboundSkill() {
|
||||
SkillRuntimeService runtimeService = mock(SkillRuntimeService.class);
|
||||
SkillFileAccessPolicy accessPolicy = mock(SkillFileAccessPolicy.class);
|
||||
SkillUsageService usageService = mock(SkillUsageService.class);
|
||||
AgentBindingResolver bindingResolver = mock(AgentBindingResolver.class);
|
||||
SkillFileTool tool = new SkillFileTool(runtimeService, accessPolicy, usageService);
|
||||
ReflectionTestUtils.setField(tool, "agentBindingResolver", bindingResolver);
|
||||
|
||||
ResolvedSkill beta = skill("beta-skill", "database", true);
|
||||
beta.setContent("# Beta\nsecret body");
|
||||
when(runtimeService.findActiveSkill("beta-skill")).thenReturn(beta);
|
||||
// Bound to some other skill id, never beta's.
|
||||
when(bindingResolver.getBoundSkillIds(42L)).thenReturn(Set.of(999L));
|
||||
|
||||
ToolContext ctx = ChatOrigin.EMPTY.withAgent(42L).toToolContext();
|
||||
String result = tool.readSkillFile("beta-skill", "SKILL.md", null, null, ctx);
|
||||
|
||||
assertTrue(result.contains("is not available for this agent"));
|
||||
assertFalse(result.contains("secret body"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("listSkillFiles denies a skill the agent is not bound to")
|
||||
void listSkillFilesDeniesUnboundSkill() {
|
||||
SkillRuntimeService runtimeService = mock(SkillRuntimeService.class);
|
||||
SkillFileAccessPolicy accessPolicy = mock(SkillFileAccessPolicy.class);
|
||||
SkillUsageService usageService = mock(SkillUsageService.class);
|
||||
AgentBindingResolver bindingResolver = mock(AgentBindingResolver.class);
|
||||
SkillFileTool tool = new SkillFileTool(runtimeService, accessPolicy, usageService);
|
||||
ReflectionTestUtils.setField(tool, "agentBindingResolver", bindingResolver);
|
||||
|
||||
ResolvedSkill beta = skill("beta-skill", "database", true);
|
||||
when(runtimeService.findActiveSkill("beta-skill")).thenReturn(beta);
|
||||
when(bindingResolver.getBoundSkillIds(42L)).thenReturn(Set.of(999L));
|
||||
|
||||
ToolContext ctx = ChatOrigin.EMPTY.withAgent(42L).toToolContext();
|
||||
String result = tool.listSkillFiles("beta-skill", ctx);
|
||||
|
||||
assertTrue(result.contains("is not available for this agent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("agents with no explicit bindings keep full skill access")
|
||||
void readSkillFileAllowsWhenAgentHasNoBindings() {
|
||||
SkillRuntimeService runtimeService = mock(SkillRuntimeService.class);
|
||||
SkillFileAccessPolicy accessPolicy = mock(SkillFileAccessPolicy.class);
|
||||
SkillUsageService usageService = mock(SkillUsageService.class);
|
||||
AgentBindingResolver bindingResolver = mock(AgentBindingResolver.class);
|
||||
SkillFileTool tool = new SkillFileTool(runtimeService, accessPolicy, usageService);
|
||||
ReflectionTestUtils.setField(tool, "agentBindingResolver", bindingResolver);
|
||||
|
||||
ResolvedSkill beta = skill("beta-skill", "database", true);
|
||||
beta.setContent("# Beta\nvisible body");
|
||||
when(runtimeService.findActiveSkill("beta-skill")).thenReturn(beta);
|
||||
// null == no explicit binding restriction → inherit every enabled skill.
|
||||
when(bindingResolver.getBoundSkillIds(42L)).thenReturn(null);
|
||||
|
||||
ToolContext ctx = ChatOrigin.EMPTY.withAgent(42L).toToolContext();
|
||||
String result = tool.readSkillFile("beta-skill", "SKILL.md", null, null, ctx);
|
||||
|
||||
assertTrue(result.contains("visible body"));
|
||||
assertFalse(result.contains("is not available for this agent"));
|
||||
}
|
||||
|
||||
private static ResolvedSkill skill(String name, String source, boolean builtin) {
|
||||
return ResolvedSkill.builder()
|
||||
.id((long) name.hashCode())
|
||||
|
||||
Loading…
Reference in New Issue
Block a user