From 7bf18e6af878bf20d9737075c86d46a2b09fdb9e Mon Sep 17 00:00:00 2001 From: matevip Date: Thu, 13 Aug 2026 03:56:03 -0400 Subject: [PATCH] fix(wiki): preserve agent id tool inputs --- .../java/vip/mate/wiki/tool/WikiTool.java | 74 +++++++++++++------ .../mate/wiki/tool/WikiToolIdSchemaTest.java | 47 ++++++++++++ .../wiki/tool/WikiToolKbNameRoutingTest.java | 21 +++--- .../wiki/tool/WikiToolPermissionTest.java | 23 +++--- 4 files changed, 120 insertions(+), 45 deletions(-) create mode 100644 mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolIdSchemaTest.java diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/tool/WikiTool.java b/mateclaw-server/src/main/java/vip/mate/wiki/tool/WikiTool.java index 9e10133d..51bd195c 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/tool/WikiTool.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/tool/WikiTool.java @@ -154,9 +154,10 @@ public class WikiTool { - boundToAgent — true if the KB is explicitly bound to this agent """) public String wiki_list_kbs( - @ToolParam(description = "Agent ID") Long agentId) { - List kbs = kbService.listByAgentId(agentId); - WikiKnowledgeBaseEntity primary = kbService.resolvePrimaryKb(agentId); + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId) { + Long parsedAgentId = parseAgentId(agentId); + List kbs = kbService.listByAgentId(parsedAgentId); + WikiKnowledgeBaseEntity primary = kbService.resolvePrimaryKb(parsedAgentId); Long primaryId = primary == null ? null : primary.getId(); JSONArray arr = new JSONArray(); @@ -195,7 +196,7 @@ public class WikiTool { consult that page first — call this tool with the bare slug before answering. """) public String wiki_read_page( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Page slug") String slug, @ToolParam(description = "Max characters to return (null = full page)", required = false) Integer maxChars, @ToolParam(description = "Section heading to extract (null = all sections)", required = false) String sectionHeading, @@ -247,7 +248,7 @@ public class WikiTool { Without query returns all pages (use only for small KBs). """) public String wiki_list_pages( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Title keyword filter (optional)", required = false) String query, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @ToolParam(description = "Numeric KB id from wiki_list_kbs. Use when `kbName` returns an ambiguous-name error.", required = false) Long kbId) { @@ -307,7 +308,7 @@ public class WikiTool { When using wiki information in your answer, always cite the source page title. """) public String wiki_search_pages( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Search query") String query, @ToolParam(description = "Mode: keyword|semantic|hybrid (default: hybrid)", required = false) String mode, @ToolParam(description = "Max results (default 5, max 20)", required = false) Integer topK, @@ -374,7 +375,7 @@ public class WikiTool { When using retrieved content in your answer, cite the source page title shown in each result. """) public String wiki_semantic_search( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Natural language query") String query, @ToolParam(description = "Max results (default 5)", required = false) Integer topK, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @@ -441,7 +442,7 @@ public class WikiTool { Returns file names, types, and paths of the original documents. """) public String wiki_trace_source( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Page slug") String slug, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @ToolParam(description = "Numeric KB id from wiki_list_kbs. Use when `kbName` returns an ambiguous-name error.", required = false) Long kbId) { @@ -474,7 +475,7 @@ public class WikiTool { Content should be Markdown. Slug is auto-generated from title. """) public String wiki_create_page( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Page title") String title, @ToolParam(description = "Page content (Markdown)") String content, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @@ -561,7 +562,7 @@ public class WikiTool { Set slug to control the page slug; otherwise it's derived from the topic. """) public String wiki_compile_page( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Topic to compile a page about (natural language)") String topic, @ToolParam(description = "Optional explicit slug for the page", required = false) String slug, @ToolParam(description = "Max evidence chunks (default 8, max 20)", required = false) Integer maxEvidenceChunks, @@ -618,7 +619,7 @@ public class WikiTool { page; protected/system pages can still be read explicitly here. """) public String wiki_read_many( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Comma-separated slugs (max 10)") String slugs, @ToolParam(description = "Max chars returned per page (default 2000, max 8000)", required = false) Integer maxCharsPerPage, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @@ -670,7 +671,7 @@ public class WikiTool { System pages (overview / log) cannot be archived. """) public String wiki_archive_page( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Page slug to archive") String slug, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @ToolParam(description = "Numeric KB id from wiki_list_kbs. Use when `kbName` returns an ambiguous-name error.", required = false) Long kbId) { @@ -682,14 +683,14 @@ public class WikiTool { list / search / related results again. No-op when the page wasn't archived. """) public String wiki_unarchive_page( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Page slug to unarchive") String slug, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @ToolParam(description = "Numeric KB id from wiki_list_kbs. Use when `kbName` returns an ambiguous-name error.", required = false) Long kbId) { return setArchivedTool(agentId, slug, false, "unarchived", kbName, kbId); } - private String setArchivedTool(Long agentId, String slug, boolean archive, String verb, String kbName, Long kbId) { + private String setArchivedTool(String agentId, String slug, boolean archive, String verb, String kbName, Long kbId) { if (slug == null || slug.isBlank()) return error("slug is required"); KbResolution kbRes = resolveKb(agentId, kbName, kbId); if (kbRes.hasError()) return kbRes.errorJson(); @@ -725,7 +726,7 @@ public class WikiTool { Delete an AI-generated wiki page. Cannot delete manually curated pages. """) public String wiki_delete_page( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Page slug to delete") String slug, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @ToolParam(description = "Numeric KB id from wiki_list_kbs. Use when `kbName` returns an ambiguous-name error.", required = false) Long kbId) { @@ -781,7 +782,7 @@ public class WikiTool { semantic similarity). More reliable than keyword search for discovering connected knowledge. """) public String wiki_related_pages( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Page slug") String slug, @ToolParam(description = "Max results (default 5, max 10)", required = false) Integer topK, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @@ -819,7 +820,7 @@ public class WikiTool { Explain why two wiki pages are related. Returns signal breakdown with scores. """) public String wiki_explain_relation( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "First page slug") String slugA, @ToolParam(description = "Second page slug") String slugB, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @@ -854,7 +855,7 @@ public class WikiTool { Does NOT regenerate content — only adds [[wikilink]] cross-references. """) public String wiki_enrich_page( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Page slug") String slug, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @ToolParam(description = "Numeric KB id from wiki_list_kbs. Use when `kbName` returns an ambiguous-name error.", required = false) Long kbId) { @@ -898,7 +899,7 @@ public class WikiTool { is re-derived from the new content unless you pass one explicitly. """) public String wiki_update_page( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Slug of the page to update (from wiki_list_pages / wiki_read_page)") String slug, @ToolParam(description = "New full Markdown content for the page body") String content, @ToolParam(description = "New one-line summary (optional; omit to auto-derive from content)", required = false) String summary, @@ -944,7 +945,7 @@ public class WikiTool { relying on experience/analysis pages. """) public String wiki_stale_pages( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @ToolParam(description = "Numeric KB id from wiki_list_kbs. Use when `kbName` returns an ambiguous-name error.", required = false) Long kbId) { @@ -980,7 +981,7 @@ public class WikiTool { human title, and a description of what the prompt produces. """) public String wiki_list_transformations( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @ToolParam(description = "Numeric KB id from wiki_list_kbs. Use when `kbName` returns an ambiguous-name error.", required = false) Long kbId) { KbResolution kbRes = resolveKb(agentId, kbName, kbId); @@ -1010,7 +1011,7 @@ public class WikiTool { The run is also persisted so the result is visible in the wiki UI. """) public String wiki_apply_transformation( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Transformation name (from wiki_list_transformations)") String name, @ToolParam(description = "Raw material ID to run the transformation against") Long rawId, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @@ -1065,7 +1066,7 @@ public class WikiTool { is persisted in the wiki UI; pass slug (not page id) for convenience. """) public String wiki_apply_transformation_to_page( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Transformation name (from wiki_list_transformations)") String name, @ToolParam(description = "Source wiki page slug to run the transformation against") String slug, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @@ -1127,7 +1128,7 @@ public class WikiTool { account). Idempotent — re-running upserts the same slug. """) public String wiki_aggregate_transformation( - @ToolParam(description = "Agent ID") Long agentId, + @ToolParam(description = "Agent ID. Must be passed as a string to preserve large integer precision") String agentId, @ToolParam(description = "Transformation name (from wiki_list_transformations)") String name, @ToolParam(description = "Target knowledge base name (from wiki_list_kbs). Omit to use the agent's primary KB; switch to `kbId` when two KBs share the name.", required = false) String kbName, @ToolParam(description = "Numeric KB id from wiki_list_kbs. Use when `kbName` returns an ambiguous-name error.", required = false) Long kbId) { @@ -1183,6 +1184,18 @@ public class WikiTool { return resolveKbId(agentId, null, null); } + private Long parseAgentId(String agentId) { + String trimmed = agentId != null ? agentId.trim() : ""; + if (trimmed.isEmpty()) { + throw new IllegalArgumentException("agentId is required"); + } + try { + return Long.parseLong(trimmed); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("agentId must be a numeric string"); + } + } + /** * Outcome of resolving a KB for a tool call. Exactly one of * {@code kbId} / {@code errorJson} is non-null: @@ -1210,6 +1223,10 @@ public class WikiTool { return pageTypePermissionService.resolve(agentId, kbId); } + private WikiPageTypePermissionService.Access pageTypeAccess(String agentId, Long kbId) { + return pageTypeAccess(parseAgentId(agentId), kbId); + } + /** Whether the resolved access permits reading {@code page}. Null-safe. */ private boolean canRead(WikiPageTypePermissionService.Access access, WikiPageEntity page) { return access == null || page == null || access.canRead(page.getPageType()); @@ -1273,6 +1290,11 @@ public class WikiTool { }; } + private String checkWrite(String agentId, Long kbId, String pageType, + WikiPageTypePermissionService.WriteOp op) { + return checkWrite(parseAgentId(agentId), kbId, pageType, op); + } + /** * Record a pending approval for an {@code APPROVAL_REQUIRED} wiki write, * keyed to the current conversation via {@link ChatOriginHolder}. Best-effort: @@ -1390,6 +1412,10 @@ public class WikiTool { return KbResolution.ok(primary.getId()); } + private KbResolution resolveKb(String agentId, String kbName, Long kbId) { + return resolveKb(parseAgentId(agentId), kbName, kbId); + } + /** * Legacy 2-arg routing kept for the tool methods that haven't been * widened to accept {@code kbId} yet. Always returns null when the diff --git a/mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolIdSchemaTest.java b/mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolIdSchemaTest.java new file mode 100644 index 00000000..a9372013 --- /dev/null +++ b/mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolIdSchemaTest.java @@ -0,0 +1,47 @@ +package vip.mate.wiki.tool; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.ai.support.ToolCallbacks; +import org.springframework.ai.tool.ToolCallback; +import vip.mate.wiki.service.HybridRetriever; +import vip.mate.wiki.service.WikiKnowledgeBaseService; +import vip.mate.wiki.service.WikiPageService; +import vip.mate.wiki.service.WikiPageTypePermissionService; +import vip.mate.wiki.service.WikiRawMaterialService; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class WikiToolIdSchemaTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + @Test + @DisplayName("wiki tools publish agentId as a string parameter so LLM tool calls preserve precision") + void wikiToolAgentIdSchemasAreString() throws Exception { + WikiTool tool = new WikiTool( + mock(WikiPageService.class), + mock(WikiKnowledgeBaseService.class), + mock(WikiRawMaterialService.class), + mock(HybridRetriever.class), + new ObjectMapper(), + mock(WikiPageTypePermissionService.class)); + + int checked = 0; + for (ToolCallback callback : ToolCallbacks.from(tool)) { + JsonNode root = MAPPER.readTree(callback.getToolDefinition().inputSchema()); + JsonNode agentId = root.at("/properties/agentId/type"); + if (!agentId.isMissingNode()) { + assertThat(agentId.asText()) + .as(callback.getToolDefinition().name()) + .isEqualTo("string"); + checked++; + } + } + + assertThat(checked).isGreaterThan(0); + } +} diff --git a/mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolKbNameRoutingTest.java b/mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolKbNameRoutingTest.java index bde61503..c9c609b1 100644 --- a/mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolKbNameRoutingTest.java +++ b/mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolKbNameRoutingTest.java @@ -51,6 +51,7 @@ import static org.mockito.Mockito.when; class WikiToolKbNameRoutingTest { private static final Long AGENT = 7L; + private static final String AGENT_PARAM = String.valueOf(AGENT); private static final long PRIMARY_KB = 100L; private static final long OTHER_KB = 200L; private static final long DUP_BOUND_KB = 300L; @@ -109,7 +110,7 @@ class WikiToolKbNameRoutingTest { wirePages(); when(kbService.resolvePrimaryKb(AGENT)).thenReturn(kb(PRIMARY_KB, "Primary", AGENT)); - String json = tool.wiki_list_pages(AGENT, null, null, null); + String json = tool.wiki_list_pages(AGENT_PARAM, null, null, null); JSONObject obj = JSONUtil.parseObj(json); JSONArray pages = obj.getJSONArray("pages"); @@ -126,7 +127,7 @@ class WikiToolKbNameRoutingTest { when(kbService.resolvePrimaryKb(AGENT)).thenReturn(kb(PRIMARY_KB, "Primary", AGENT)); when(kbService.findAllByName(AGENT, "Other")).thenReturn(List.of(kb(OTHER_KB, "Other", null))); - String json = tool.wiki_list_pages(AGENT, null, "Other", null); + String json = tool.wiki_list_pages(AGENT_PARAM, null, "Other", null); JSONObject obj = JSONUtil.parseObj(json); JSONArray pages = obj.getJSONArray("pages"); @@ -141,7 +142,7 @@ class WikiToolKbNameRoutingTest { when(kbService.resolvePrimaryKb(AGENT)).thenReturn(kb(PRIMARY_KB, "Primary", AGENT)); when(kbService.findVisibleById(AGENT, OTHER_KB)).thenReturn(kb(OTHER_KB, "Other", null)); - String json = tool.wiki_list_pages(AGENT, null, null, OTHER_KB); + String json = tool.wiki_list_pages(AGENT_PARAM, null, null, OTHER_KB); JSONObject obj = JSONUtil.parseObj(json); assertThat(obj.getJSONArray("pages").getJSONObject(0).getStr("slug")) @@ -156,7 +157,7 @@ class WikiToolKbNameRoutingTest { // Deliberately do NOT stub findAllByName — if the tool consulted // kbName at all (or fell back to primary), the call would NPE. - String json = tool.wiki_list_pages(AGENT, null, "anything", OTHER_KB); + String json = tool.wiki_list_pages(AGENT_PARAM, null, "anything", OTHER_KB); JSONObject obj = JSONUtil.parseObj(json); assertThat(obj.getJSONArray("pages").getJSONObject(0).getStr("slug")) .isEqualTo("other-only-slug"); @@ -171,7 +172,7 @@ class WikiToolKbNameRoutingTest { // Primary still mockable; the routing must NOT silently fall through. when(kbService.resolvePrimaryKb(AGENT)).thenReturn(kb(PRIMARY_KB, "Primary", AGENT)); - String json = tool.wiki_list_pages(AGENT, null, "Bogus", null); + String json = tool.wiki_list_pages(AGENT_PARAM, null, "Bogus", null); JSONObject obj = JSONUtil.parseObj(json); assertThat(obj.getStr("error")) @@ -188,7 +189,7 @@ class WikiToolKbNameRoutingTest { kb(DUP_SHARED_KB, "Docs", null))); when(kbService.resolvePrimaryKb(AGENT)).thenReturn(kb(PRIMARY_KB, "Primary", AGENT)); - String json = tool.wiki_list_pages(AGENT, null, "Docs", null); + String json = tool.wiki_list_pages(AGENT_PARAM, null, "Docs", null); JSONObject obj = JSONUtil.parseObj(json); // Error must be ambiguity-flavoured so the LLM knows to retry with kbId. @@ -217,7 +218,7 @@ class WikiToolKbNameRoutingTest { // would return null and surface a spurious "kbId=0 not visible" error, // which is exactly the production regression this test prevents. - String json = tool.wiki_list_pages(AGENT, null, null, 0L); + String json = tool.wiki_list_pages(AGENT_PARAM, null, null, 0L); JSONObject obj = JSONUtil.parseObj(json); assertThat(obj.getStr("error")) @@ -234,7 +235,7 @@ class WikiToolKbNameRoutingTest { when(kbService.findVisibleById(AGENT, 99999L)).thenReturn(null); when(kbService.resolvePrimaryKb(AGENT)).thenReturn(kb(PRIMARY_KB, "Primary", AGENT)); - String json = tool.wiki_list_pages(AGENT, null, null, 99999L); + String json = tool.wiki_list_pages(AGENT_PARAM, null, null, 99999L); JSONObject obj = JSONUtil.parseObj(json); assertThat(obj.getStr("error")) @@ -248,7 +249,7 @@ class WikiToolKbNameRoutingTest { void noResolvableKbReturnsLegacyError() { when(kbService.resolvePrimaryKb(AGENT)).thenReturn(null); - String json = tool.wiki_list_pages(AGENT, null, null, null); + String json = tool.wiki_list_pages(AGENT_PARAM, null, null, null); JSONObject obj = JSONUtil.parseObj(json); assertThat(obj.getStr("error")).contains("No wiki knowledge base found"); @@ -264,7 +265,7 @@ class WikiToolKbNameRoutingTest { kb(PRIMARY_KB, "Primary", AGENT))); when(kbService.resolvePrimaryKb(AGENT)).thenReturn(kb(PRIMARY_KB, "Primary", AGENT)); - String json = tool.wiki_list_kbs(AGENT); + String json = tool.wiki_list_kbs(AGENT_PARAM); JSONObject obj = JSONUtil.parseObj(json); assertThat(obj.getInt("kbCount")).isEqualTo(2); diff --git a/mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolPermissionTest.java b/mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolPermissionTest.java index 84666890..b319faf9 100644 --- a/mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolPermissionTest.java +++ b/mateclaw-server/src/test/java/vip/mate/wiki/tool/WikiToolPermissionTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.when; class WikiToolPermissionTest { private static final long AGENT = 11L; + private static final String AGENT_PARAM = String.valueOf(AGENT); private static final long KB = 7L; private record Harness(WikiTool tool, WikiPageService pageService, @@ -90,7 +91,7 @@ class WikiToolPermissionTest { Harness h = harness(List.of(row("*", 0, 0, 0, 0, "deny"))); when(h.pageService().getBySlug(KB, "secret")).thenReturn(page("secret", "analysis")); - String out = h.tool().wiki_read_page(AGENT, "secret", null, null, null, KB); + String out = h.tool().wiki_read_page(AGENT_PARAM, "secret", null, null, null, KB); assertTrue(out.contains("Page not found"), out); } @@ -100,7 +101,7 @@ class WikiToolPermissionTest { Harness h = harness(List.of(row("*", 1, 0, 0, 0, "deny"))); when(h.pageService().getBySlug(KB, "ok")).thenReturn(page("ok", "concept")); - String out = h.tool().wiki_read_page(AGENT, "ok", null, null, null, KB); + String out = h.tool().wiki_read_page(AGENT_PARAM, "ok", null, null, null, KB); assertTrue(out.contains("\"content\""), out); assertFalse(out.contains("Page not found"), out); @@ -112,7 +113,7 @@ class WikiToolPermissionTest { Harness h = harness(List.of(row("concept", 1, 0, 0, 0, "deny"))); when(h.pageService().getBySlug(KB, "p")).thenReturn(page("p", "concept")); - String out = h.tool().wiki_delete_page(AGENT, "p", null, KB); + String out = h.tool().wiki_delete_page(AGENT_PARAM, "p", null, KB); assertTrue(out.contains("Not permitted"), out); verify(h.pageService(), never()).delete(anyLong(), any()); @@ -123,7 +124,7 @@ class WikiToolPermissionTest { Harness h = harness(List.of(row("concept", 1, 0, 0, 1, "approval_required"))); when(h.pageService().getBySlug(KB, "p")).thenReturn(page("p", "concept")); - String out = h.tool().wiki_delete_page(AGENT, "p", null, KB); + String out = h.tool().wiki_delete_page(AGENT_PARAM, "p", null, KB); assertTrue(out.contains("Approval required"), out); verify(h.pageService(), never()).delete(anyLong(), any()); @@ -134,7 +135,7 @@ class WikiToolPermissionTest { Harness h = harness(List.of(row("concept", 1, 1, 1, 1, "allow"))); when(h.pageService().getBySlug(KB, "p")).thenReturn(page("p", "concept")); - String out = h.tool().wiki_delete_page(AGENT, "p", null, KB); + String out = h.tool().wiki_delete_page(AGENT_PARAM, "p", null, KB); assertTrue(out.contains("\"ok\":true"), out); verify(h.pageService(), times(1)).delete(eq(KB), eq("p")); @@ -145,7 +146,7 @@ class WikiToolPermissionTest { // a row exists for 'episode' only → KB is gated, wildcard create not granted Harness h = harness(List.of(row("episode", 1, 1, 1, 1, "allow"))); - String out = h.tool().wiki_create_page(AGENT, "New Page", "content here", null, KB); + String out = h.tool().wiki_create_page(AGENT_PARAM, "New Page", "content here", null, KB); assertTrue(out.contains("Not permitted"), out); verify(h.pageService(), never()).createPage(anyLong(), any(), any(), any(), any(), any()); @@ -161,7 +162,7 @@ class WikiToolPermissionTest { updated.setVersion(2); when(h.pageService().updatePageManually(eq(KB), eq("p"), any(), any())).thenReturn(updated); - String out = h.tool().wiki_update_page(AGENT, "p", "new body", null, null, KB); + String out = h.tool().wiki_update_page(AGENT_PARAM, "p", "new body", null, null, KB); assertTrue(out.contains("\"ok\":true"), out); assertTrue(out.contains("updated in place"), out); @@ -177,7 +178,7 @@ class WikiToolPermissionTest { Harness h = harness(List.of(row("concept", 1, 1, 0, 0, "allow"))); when(h.pageService().getBySlug(KB, "p")).thenReturn(page("p", "concept")); - String out = h.tool().wiki_update_page(AGENT, "p", "new body", null, null, KB); + String out = h.tool().wiki_update_page(AGENT_PARAM, "p", "new body", null, null, KB); assertTrue(out.contains("Not permitted"), out); verify(h.pageService(), never()).updatePageManually(anyLong(), any(), any(), any()); @@ -188,7 +189,7 @@ class WikiToolPermissionTest { Harness h = harness(List.of(row("*", 1, 1, 1, 1, "allow"))); when(h.pageService().getBySlug(KB, "ghost")).thenReturn(null); - String out = h.tool().wiki_update_page(AGENT, "ghost", "body", null, null, KB); + String out = h.tool().wiki_update_page(AGENT_PARAM, "ghost", "body", null, null, KB); assertTrue(out.contains("Page not found"), out); verify(h.pageService(), never()).updatePageManually(anyLong(), any(), any(), any()); @@ -212,7 +213,7 @@ class WikiToolPermissionTest { WikiPageEntity staleHidden = stalePage("classified", "secret", "{\"reason\":\"x\"}"); when(h.pageService().listByKbId(KB)).thenReturn(List.of(fresh, staleOk, staleHidden)); - String out = h.tool().wiki_stale_pages(AGENT, null, KB); + String out = h.tool().wiki_stale_pages(AGENT_PARAM, null, KB); assertTrue(out.contains("\"staleCount\":1"), out); assertTrue(out.contains("aged"), out); @@ -225,7 +226,7 @@ class WikiToolPermissionTest { Harness h = harness(List.of()); when(h.pageService().listByKbId(KB)).thenReturn(List.of(page("a", "concept"), page("b", "episode"))); - String out = h.tool().wiki_stale_pages(AGENT, null, KB); + String out = h.tool().wiki_stale_pages(AGENT_PARAM, null, KB); assertTrue(out.contains("\"staleCount\":0"), out); }