WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
package docparser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Tencent/WeKnora/internal/types"
|
||||
)
|
||||
|
||||
func TestScanMarkdownImageTargets(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
targets []string
|
||||
}{
|
||||
{
|
||||
name: "without title",
|
||||
input: `before  after`,
|
||||
targets: []string{`images/a.png`},
|
||||
},
|
||||
{
|
||||
name: "double quoted title",
|
||||
input: ``,
|
||||
targets: []string{`images/a.png "图"`},
|
||||
},
|
||||
{
|
||||
name: "single quoted title",
|
||||
input: ``,
|
||||
targets: []string{`images/a.png '图'`},
|
||||
},
|
||||
{
|
||||
name: "parenthesized title",
|
||||
input: `)`,
|
||||
targets: []string{`images/a.png (图)`},
|
||||
},
|
||||
{
|
||||
name: "title containing right paren",
|
||||
input: ` 结果")`,
|
||||
targets: []string{`images/a.png "阶段 1) 结果"`},
|
||||
},
|
||||
{
|
||||
name: "title containing both parens",
|
||||
input: ` 结果')`,
|
||||
targets: []string{`images/a.png '阶段 (1) 结果'`},
|
||||
},
|
||||
{
|
||||
name: "escaped quote in title",
|
||||
input: ``,
|
||||
targets: []string{`images/a.png "阶段 \"1\""`},
|
||||
},
|
||||
{
|
||||
name: "multiline title",
|
||||
input: "",
|
||||
targets: []string{"images/a.png\n \"多行 title\""},
|
||||
},
|
||||
{
|
||||
name: "spaced path",
|
||||
input: ``,
|
||||
targets: []string{`images/第 1 页.png`},
|
||||
},
|
||||
{
|
||||
name: "path containing balanced parens",
|
||||
input: `.png "title")`,
|
||||
targets: []string{`images/a_(1).png "title"`},
|
||||
},
|
||||
{
|
||||
name: "angle destination",
|
||||
input: ``,
|
||||
targets: []string{`<images/a b.png> "title"`},
|
||||
},
|
||||
{
|
||||
name: "malformed image is skipped",
|
||||
input: ` after `,
|
||||
targets: []string{`images/b.png`},
|
||||
},
|
||||
{
|
||||
name: "escaped image marker is skipped",
|
||||
input: `\ `,
|
||||
targets: []string{`images/b.png`},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
spans := scanMarkdownImageTargets(tt.input)
|
||||
if len(spans) != len(tt.targets) {
|
||||
t.Fatalf("got %d spans, want %d: %#v", len(spans), len(tt.targets), spans)
|
||||
}
|
||||
for i, span := range spans {
|
||||
got := tt.input[span.TargetStart:span.TargetEnd]
|
||||
if got != tt.targets[i] {
|
||||
t.Fatalf("target %d = %q, want %q", i, got, tt.targets[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitMarkdownImageTarget(t *testing.T) {
|
||||
refMap := map[string]types.ImageRef{
|
||||
"images/a.png": {OriginalRef: "images/a.png"},
|
||||
"images/第 1 页.png": {OriginalRef: "images/第 1 页.png"},
|
||||
"images/a_(1).png": {OriginalRef: "images/a_(1).png"},
|
||||
"images/第 1 页 (测试).gif": {OriginalRef: "images/第 1 页 (测试).gif"},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
raw string
|
||||
wantPath string
|
||||
wantText string
|
||||
wantOK bool
|
||||
}{
|
||||
{
|
||||
name: "without title",
|
||||
raw: `images/a.png`,
|
||||
wantPath: `images/a.png`,
|
||||
wantText: `local://stored`,
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "double quoted title",
|
||||
raw: `images/a.png "图"`,
|
||||
wantPath: `images/a.png`,
|
||||
wantText: `local://stored "图"`,
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "single quoted title",
|
||||
raw: `images/a.png '图'`,
|
||||
wantPath: `images/a.png`,
|
||||
wantText: `local://stored '图'`,
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "parenthesized title",
|
||||
raw: `images/a.png (阶段 (1) 结果)`,
|
||||
wantPath: `images/a.png`,
|
||||
wantText: `local://stored (阶段 (1) 结果)`,
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "multiline title",
|
||||
raw: "images/a.png\n \"多行 title\"",
|
||||
wantPath: `images/a.png`,
|
||||
wantText: "local://stored\n \"多行 title\"",
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "path with spaces wins without title",
|
||||
raw: `images/第 1 页.png`,
|
||||
wantPath: `images/第 1 页.png`,
|
||||
wantText: `local://stored`,
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "path with balanced parens",
|
||||
raw: `images/a_(1).png "title"`,
|
||||
wantPath: `images/a_(1).png`,
|
||||
wantText: `local://stored "title"`,
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "angle destination replaces only inner path",
|
||||
raw: `<images/第 1 页 (测试).gif> "阶段 1) 图片"`,
|
||||
wantPath: `images/第 1 页 (测试).gif`,
|
||||
wantText: `<local://stored> "阶段 1) 图片"`,
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "unknown reference",
|
||||
raw: `images/missing.png "title"`,
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "blank line in title is invalid",
|
||||
raw: "images/a.png \"line1\n\nline2\"",
|
||||
wantOK: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
path, pathStart, pathEnd, ok := splitMarkdownImageTarget(tt.raw, refMap)
|
||||
if ok != tt.wantOK {
|
||||
t.Fatalf("ok = %v, want %v", ok, tt.wantOK)
|
||||
}
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if path != tt.wantPath {
|
||||
t.Fatalf("path = %q, want %q", path, tt.wantPath)
|
||||
}
|
||||
replaced := tt.raw[:pathStart] + "local://stored" + tt.raw[pathEnd:]
|
||||
if replaced != tt.wantText {
|
||||
t.Fatalf("replacement = %q, want %q", replaced, tt.wantText)
|
||||
}
|
||||
if strings.Contains(replaced, tt.wantPath) {
|
||||
t.Fatalf("replacement still contains original path: %q", replaced)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user