From e836c1fe17d338b97aecfe752468a8990f6b2cb3 Mon Sep 17 00:00:00 2001 From: Benjamin Mestrallet Date: Thu, 22 Oct 2026 09:55:43 +0100 Subject: [PATCH 1/2] fix: a project's description and its link are no longer swapped in the AI tools' project results EXO-91949 Co-Authored-By: Claude Opus 5.5 --- .../java/io/meeds/task/mcp/TaskMcpTool.java | 2 +- .../io/meeds/task/mcp/TaskMcpToolTest.java | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/services/src/main/java/io/meeds/task/mcp/TaskMcpTool.java b/services/src/main/java/io/meeds/task/mcp/TaskMcpTool.java index db9c55aef..fbc22730e 100644 --- a/services/src/main/java/io/meeds/task/mcp/TaskMcpTool.java +++ b/services/src/main/java/io/meeds/task/mcp/TaskMcpTool.java @@ -1440,8 +1440,8 @@ private ProjectModel toProjectModel(ProjectDto project) { Space space = getProjectSpace(project); return new ProjectModel(project.getId(), project.getName(), - getUrl(project), transformHtmlContent(project.getDescription(), aclIdentity, locale), + getUrl(project), getProjectStatuses(project.getId()), getProjectLabels(project.getId()), space == null ? null : space.getSpaceId(), diff --git a/services/src/test/java/io/meeds/task/mcp/TaskMcpToolTest.java b/services/src/test/java/io/meeds/task/mcp/TaskMcpToolTest.java index d2afd7d44..94d7c5606 100644 --- a/services/src/test/java/io/meeds/task/mcp/TaskMcpToolTest.java +++ b/services/src/test/java/io/meeds/task/mcp/TaskMcpToolTest.java @@ -1427,6 +1427,33 @@ public void listProjectsShouldUseProjectCountAndReturnCollection() throws Except verify(projectService).countProjects(any(), eq(null)); } + /** + * A listed project carries its description in the description and its + * permanent link in the url, not the one in the other's place. + * Mutant: the description and the link are passed in each other's position. + */ + @Test + public void listProjectsShouldKeepTheDescriptionAndTheUrlEachInItsOwnField() throws Exception { // NOSONAR + ProjectDto project = mock(ProjectDto.class); + + when(projectService.findProjects(any(), eq(null), any(), eq(0), eq(10))) + .thenReturn(Collections.singletonList(project)); + when(projectService.countProjects(any(), eq(null))).thenReturn(1); + when(project.getId()).thenReturn(PROJECT_ID); + when(project.getName()).thenReturn(PROJECT_NAME); + when(project.getDescription()).thenReturn("Reworks the Shortcuts portlet"); + when(project.getManager()).thenReturn(Collections.emptySet()); + when(statusService.getStatuses(PROJECT_ID)).thenReturn(Collections.emptyList()); + when(labelService.findLabelsByProject(PROJECT_ID, currentIdentity, 0, 100)).thenReturn(Collections.emptyList()); + when(permanentLinkService.getLink(any())).thenReturn("/project-link"); + + ProjectCollectionModel result = runWithDateFormatMockResult(() -> tool.listProjects(null, null, null)); + + ProjectModel model = result.getProjects().get(0); + assertTrue(model.getDescription(), model.getDescription().contains("Reworks the Shortcuts portlet")); + assertTrue(model.getUrl(), model.getUrl().endsWith("/project-link")); + } + /** * In a run limited to a space, the query gets the user's roles of that space * group and nothing of the user's other memberships, and only the projects From 3c0bfeb7f6745327a2b9973b4e4df995299e0288 Mon Sep 17 00:00:00 2001 From: Benjamin Mestrallet Date: Thu, 22 Oct 2026 11:47:18 +0100 Subject: [PATCH 2/2] fix: the description/link test reads the project as get_project_by_id returns it EXO-91949 Co-Authored-By: Claude Opus 5.5 --- .../java/io/meeds/task/mcp/TaskMcpToolTest.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/services/src/test/java/io/meeds/task/mcp/TaskMcpToolTest.java b/services/src/test/java/io/meeds/task/mcp/TaskMcpToolTest.java index 94d7c5606..bb795034c 100644 --- a/services/src/test/java/io/meeds/task/mcp/TaskMcpToolTest.java +++ b/services/src/test/java/io/meeds/task/mcp/TaskMcpToolTest.java @@ -1428,17 +1428,16 @@ public void listProjectsShouldUseProjectCountAndReturnCollection() throws Except } /** - * A listed project carries its description in the description and its - * permanent link in the url, not the one in the other's place. + * A project carries its description in the description and its permanent + * link in the url, not the one in the other's place. * Mutant: the description and the link are passed in each other's position. */ @Test - public void listProjectsShouldKeepTheDescriptionAndTheUrlEachInItsOwnField() throws Exception { // NOSONAR + public void getProjectByIdShouldKeepTheDescriptionAndTheUrlEachInItsOwnField() throws Exception { // NOSONAR ProjectDto project = mock(ProjectDto.class); - when(projectService.findProjects(any(), eq(null), any(), eq(0), eq(10))) - .thenReturn(Collections.singletonList(project)); - when(projectService.countProjects(any(), eq(null))).thenReturn(1); + when(projectService.getProject(PROJECT_ID)).thenReturn(project); + when(project.canView(currentIdentity)).thenReturn(true); when(project.getId()).thenReturn(PROJECT_ID); when(project.getName()).thenReturn(PROJECT_NAME); when(project.getDescription()).thenReturn("Reworks the Shortcuts portlet"); @@ -1447,9 +1446,8 @@ public void listProjectsShouldKeepTheDescriptionAndTheUrlEachInItsOwnField() thr when(labelService.findLabelsByProject(PROJECT_ID, currentIdentity, 0, 100)).thenReturn(Collections.emptyList()); when(permanentLinkService.getLink(any())).thenReturn("/project-link"); - ProjectCollectionModel result = runWithDateFormatMockResult(() -> tool.listProjects(null, null, null)); + ProjectModel model = runWithDateFormatMockResult(() -> tool.getProjectById(PROJECT_ID)); - ProjectModel model = result.getProjects().get(0); assertTrue(model.getDescription(), model.getDescription().contains("Reworks the Shortcuts portlet")); assertTrue(model.getUrl(), model.getUrl().endsWith("/project-link")); }