mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-11-02 14:11:49 +08:00
fix(chat): preserve directory attachments during upload validation
Follow up #652 by excluding blank stored names from upload validation and covering directory attachments with a regression test. Update caption persistence mocks for the origin-aware method. Verified with 148 targeted tests.
This commit is contained in:
parent
6f13a70709
commit
6647312162
@ -2126,7 +2126,7 @@ public class ChatController {
|
|||||||
List<MessageContentPart> parts) {
|
List<MessageContentPart> parts) {
|
||||||
if (parts == null) return;
|
if (parts == null) return;
|
||||||
for (MessageContentPart part : parts) {
|
for (MessageContentPart part : parts) {
|
||||||
if (part == null || part.getStoredName() == null) continue;
|
if (part == null || part.getStoredName() == null || part.getStoredName().isBlank()) continue;
|
||||||
Path file = uploadLocationResolver.resolveExistingFile(origin, part.getStoredName());
|
Path file = uploadLocationResolver.resolveExistingFile(origin, part.getStoredName());
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
throw new IllegalArgumentException("聊天附件不存在或不属于当前会话");
|
throw new IllegalArgumentException("聊天附件不存在或不属于当前会话");
|
||||||
|
|||||||
@ -64,7 +64,7 @@ class BaseAgentImageCaptionPersistTest {
|
|||||||
h.agent.callBuildCurrentTurn(msg, "报错的行号是多少");
|
h.agent.callBuildCurrentTurn(msg, "报错的行号是多少");
|
||||||
|
|
||||||
ArgumentCaptor<String> question = ArgumentCaptor.forClass(String.class);
|
ArgumentCaptor<String> question = ArgumentCaptor.forClass(String.class);
|
||||||
verify(h.caption).caption(any(), any(), any(), question.capture());
|
verify(h.caption).caption(any(), any(), any(), question.capture(), any());
|
||||||
assertEquals("报错的行号是多少", question.getValue(),
|
assertEquals("报错的行号是多少", question.getValue(),
|
||||||
"the user's question (text part) must drive a context-aware caption");
|
"the user's question (text part) must drive a context-aware caption");
|
||||||
}
|
}
|
||||||
@ -81,7 +81,7 @@ class BaseAgentImageCaptionPersistTest {
|
|||||||
h.agent.callBuildCurrentTurn(msg, "[图片]");
|
h.agent.callBuildCurrentTurn(msg, "[图片]");
|
||||||
|
|
||||||
ArgumentCaptor<String> question = ArgumentCaptor.forClass(String.class);
|
ArgumentCaptor<String> question = ArgumentCaptor.forClass(String.class);
|
||||||
verify(h.caption).caption(any(), any(), any(), question.capture());
|
verify(h.caption).caption(any(), any(), any(), question.capture(), any());
|
||||||
assertEquals(null, question.getValue(),
|
assertEquals(null, question.getValue(),
|
||||||
"no text part → null question → caption falls back to generic description");
|
"no text part → null question → caption falls back to generic description");
|
||||||
}
|
}
|
||||||
@ -115,7 +115,7 @@ class BaseAgentImageCaptionPersistTest {
|
|||||||
MultimodalRoutingDecision.sidecar(sidecar,
|
MultimodalRoutingDecision.sidecar(sidecar,
|
||||||
EnumSet.of(ModelCapabilityService.Modality.VISION),
|
EnumSet.of(ModelCapabilityService.Modality.VISION),
|
||||||
EnumSet.of(ModelCapabilityService.Modality.VISION)));
|
EnumSet.of(ModelCapabilityService.Modality.VISION)));
|
||||||
when(caption.caption(any(), any(), any(), any()))
|
when(caption.caption(any(), any(), any(), any(), any()))
|
||||||
.thenReturn(MediaCaptionService.CaptionResult.success(DESCRIPTION, 12L, false));
|
.thenReturn(MediaCaptionService.CaptionResult.success(DESCRIPTION, 12L, false));
|
||||||
|
|
||||||
TestAgent agent = new TestAgent(conv);
|
TestAgent agent = new TestAgent(conv);
|
||||||
|
|||||||
@ -20,6 +20,27 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
*/
|
*/
|
||||||
class ChatControllerUploadPathTest {
|
class ChatControllerUploadPathTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void directoryAttachmentWithEmptyStoredNameIsNotAnUpload() throws Exception {
|
||||||
|
ChatController controller = org.mockito.Mockito.mock(ChatController.class,
|
||||||
|
org.mockito.Mockito.CALLS_REAL_METHODS);
|
||||||
|
var resolver = org.mockito.Mockito.mock(
|
||||||
|
vip.mate.workspace.core.service.ChatUploadLocationResolver.class);
|
||||||
|
org.springframework.test.util.ReflectionTestUtils.setField(controller, "uploadLocationResolver", resolver);
|
||||||
|
var part = new vip.mate.workspace.conversation.model.MessageContentPart();
|
||||||
|
part.setType("file");
|
||||||
|
part.setContentType("inode/directory");
|
||||||
|
part.setStoredName("");
|
||||||
|
part.setPath("/workspace/project");
|
||||||
|
var origin = vip.mate.agent.context.ChatOrigin.web("conv", "alice", 7L, null);
|
||||||
|
|
||||||
|
org.junit.jupiter.api.Assertions.assertDoesNotThrow(() ->
|
||||||
|
org.springframework.test.util.ReflectionTestUtils.invokeMethod(controller,
|
||||||
|
"validateUploadedParts", "conv", origin, java.util.List.of(part)));
|
||||||
|
assertThat(part.getPath()).isEqualTo("/workspace/project");
|
||||||
|
org.mockito.Mockito.verifyNoInteractions(resolver);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("default root: returns chat-uploads/{convId}/{storedName}, not absolute")
|
@DisplayName("default root: returns chat-uploads/{convId}/{storedName}, not absolute")
|
||||||
void defaultRootIsRelative() {
|
void defaultRootIsRelative() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user