From bdb164d432f43a87f67a5f43dcf45d282fb2fca4 Mon Sep 17 00:00:00 2001 From: wizardchen Date: Fri, 29 May 2026 12:21:24 +0800 Subject: [PATCH] feat: add tests for incomplete Markdown image handling and improve stream flush logic This commit introduces a new test, `TestFindIncompleteMarkdownImage`, to validate the detection of incomplete Markdown images in various scenarios. Additionally, it enhances the `holdbackCutoff` function to prioritize handling incomplete Markdown images, ensuring that they are correctly managed during stream flush operations. The changes improve the robustness of image processing in the application, addressing potential issues with unclosed image URLs in Markdown content. --- internal/im/content_rewrite_test.go | 203 ++++++++++++++++++++++++++-- internal/im/service.go | 20 ++- 2 files changed, 214 insertions(+), 9 deletions(-) diff --git a/internal/im/content_rewrite_test.go b/internal/im/content_rewrite_test.go index 2470d42c..0450fd21 100644 --- a/internal/im/content_rewrite_test.go +++ b/internal/im/content_rewrite_test.go @@ -2,6 +2,7 @@ package im import ( "context" + "regexp" "strings" "testing" @@ -231,6 +232,28 @@ func TestRewriteStorageURLs_COSPathNotSignedAsLocalKey(t *testing.T) { } } +func TestFindIncompleteMarkdownImage(t *testing.T) { + tests := []struct { + name string + in string + want int + }{ + {"complete image", "![img](local://1/a.png)", -1}, + {"complete then text", "![img](local://1/a.png) trailing", -1}, + {"truncated provider URL in image", `![知识助理"知识库"管理视图界面](minio://wizard-test/10000/exports/c91cf852`, 0}, + {"open paren only", "text ![alt](", 5}, + {"bare provider suffix without markdown", "text minio://wizard-test/10000/exp", -1}, + {"two images complete", "![a](local://1/a.png) ![b](local://1/b.png)", -1}, + {"first complete second incomplete", "![a](local://1/a.png) ![b](minio://part", 22}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := findIncompleteMarkdownImage(tt.in) + assert.Equal(t, tt.want, got, "findIncompleteMarkdownImage(%q)", tt.in) + }) + } +} + func TestHoldbackCutoff(t *testing.T) { tests := []struct { name string @@ -240,13 +263,17 @@ func TestHoldbackCutoff(t *testing.T) { { "no holdback needed", "plain text with complete ![img](local://1/img.png) content", - // URL is terminated by `)` then space → no holdback - -1, // placeholder, computed below + -1, }, { - "truncated URL", + "truncated URL inside markdown image", "text ![img](local://1/abc/im", - 12, + 5, // hold back from ![ — not from local:// + }, + { + "truncated markdown image with quotes in alt", + "### 2\n![知识助理\"知识库\"管理视图界面](minio://wizard-test/10000/exports/c91cf852", + -2, // computed via strings.Index below }, { "truncated XML", @@ -254,19 +281,179 @@ func TestHoldbackCutoff(t *testing.T) { 5, }, { - "both truncated, URL earlier", + "both truncated, XML earlier", ", , or tag // that reaches the end of the string without a closing '>'. var incompleteXMLTagRe = regexp.MustCompile( @@ -155,7 +171,9 @@ func findIncompleteXMLTag(s string) int { // chunk, or len(chunk) if the chunk is safe to flush entirely. func holdbackCutoff(chunk string) int { cutoff := len(chunk) - if idx := findIncompleteStorageURL(chunk); idx >= 0 && idx < cutoff { + if idx := findIncompleteMarkdownImage(chunk); idx >= 0 && idx < cutoff { + cutoff = idx + } else if idx := findIncompleteStorageURL(chunk); idx >= 0 && idx < cutoff { cutoff = idx } if idx := findIncompleteXMLTag(chunk); idx >= 0 && idx < cutoff {