d7478094a2
The heuristic splitter relied on two regexes that were too strict for real-world Chinese technical documents: - ChineseChapterPattern required 第 / numeral / unit to be adjacent, so the very common "第 1 章 引言" form never matched. - NumberedSectionPattern required a trailing dot after the numeral, so multi-level numbering such as "1.1 文档目的" or "2.2.1 用户与权限" was missed. As a result, documents like the CHAPTER_SAMPLE shipped with the chunking debug drawer collected zero heuristic markers, and ProfileDocument fell all the way through to the character-level Legacy tier, producing chunks that ignored the document's explicit chapter structure. - Loosen ChineseChapterPattern to tolerate spaces around 第 / 数字 / 单位. - Allow the multi-level branch of NumberedSectionPattern to drop its trailing dot; keep the single-level / roman branch dot-required to avoid false positives on version strings. - Extend patterns_test.go with positive and negative cases covering the CHAPTER_SAMPLE wording plus deep-nesting and lone-numeral regressions.
196 lines
5.1 KiB
Go
196 lines
5.1 KiB
Go
package chunker
|
|
|
|
import "testing"
|
|
|
|
func TestMarkdownHeadingPattern_BasicLevels(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
match bool
|
|
}{
|
|
{"# Heading 1", true},
|
|
{"## Heading 2", true},
|
|
{"###### Heading 6", true},
|
|
{"####### Too many", false},
|
|
{"#NoSpace", false},
|
|
{" # Indented", false},
|
|
{"plain text", false},
|
|
}
|
|
for _, c := range cases {
|
|
got := MarkdownHeadingPattern.MatchString(c.in)
|
|
if got != c.match {
|
|
t.Errorf("MarkdownHeadingPattern(%q): got %v want %v", c.in, got, c.match)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNumberedSectionPattern(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
match bool
|
|
}{
|
|
{"1. Introduction", true},
|
|
{"2.3 Methodology", true}, // multi-level numerals no longer require trailing dot
|
|
{"2.3. Methodology", true},
|
|
{"2.2.1 用户与权限", true}, // three-level numbering, Chinese title, no trailing dot
|
|
{"3.2.1 单机 Docker Compose", true},
|
|
{"IV. Results", true},
|
|
{"1.Introduction", false}, // requires whitespace
|
|
{"1.", false}, // requires title
|
|
{"1.1", false}, // requires title after numeral
|
|
{"1 NoDotSingleLevel", false}, // single-level numerals still need trailing dot
|
|
{"1.2.3.4.5 TooDeep", false}, // more than 3 sub-levels not accepted
|
|
{"plain text", false},
|
|
}
|
|
for _, c := range cases {
|
|
got := NumberedSectionPattern.MatchString(c.in)
|
|
if got != c.match {
|
|
t.Errorf("NumberedSectionPattern(%q): got %v want %v", c.in, got, c.match)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGermanChapterPattern(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
match bool
|
|
}{
|
|
{"Kapitel 1: Einführung", true},
|
|
{"Abschnitt 2.3 Methodik", true},
|
|
{"Abschnitt 3 Methodik", true},
|
|
{"Teil II Ergebnisse", true},
|
|
{"chapter 1", false},
|
|
}
|
|
for _, c := range cases {
|
|
got := GermanChapterPattern.MatchString(c.in)
|
|
if got != c.match {
|
|
t.Errorf("GermanChapterPattern(%q): got %v want %v", c.in, got, c.match)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEnglishChapterPattern(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
match bool
|
|
}{
|
|
{"Chapter 1: Intro", true},
|
|
{"Section 5 Methods", true},
|
|
{"Part IV Results", true},
|
|
{"Kapitel 1", false},
|
|
}
|
|
for _, c := range cases {
|
|
got := EnglishChapterPattern.MatchString(c.in)
|
|
if got != c.match {
|
|
t.Errorf("EnglishChapterPattern(%q): got %v want %v", c.in, got, c.match)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestChineseChapterPattern(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
match bool
|
|
}{
|
|
{"第一章 引言", true},
|
|
{"第3节 方法论", true},
|
|
{"第二部分 结果", true},
|
|
{"第 1 章 引言", true}, // space between 第 / numeral / unit
|
|
{"第 一 章 引言", true},
|
|
{"第1章 引言", true},
|
|
{"Chapter 1", false},
|
|
{"第 章 空数字", false}, // missing numeral
|
|
}
|
|
for _, c := range cases {
|
|
got := ChineseChapterPattern.MatchString(c.in)
|
|
if got != c.match {
|
|
t.Errorf("ChineseChapterPattern(%q): got %v want %v", c.in, got, c.match)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestVisualSeparatorPattern(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
match bool
|
|
}{
|
|
{"---", true},
|
|
{"========", true},
|
|
{"***", true},
|
|
{"____", true},
|
|
{"--", false}, // needs at least 3
|
|
{"-- text", false},
|
|
}
|
|
for _, c := range cases {
|
|
got := VisualSeparatorPattern.MatchString(c.in)
|
|
if got != c.match {
|
|
t.Errorf("VisualSeparatorPattern(%q): got %v want %v", c.in, got, c.match)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPageFooterPattern(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
match bool
|
|
}{
|
|
{"Seite 3 von 24", true},
|
|
{"Page 5 of 12", true},
|
|
{"page 7", true},
|
|
{"Seite 9", true},
|
|
{"页 3", true},
|
|
{"页码 3 / 12", true},
|
|
{"Some text", false},
|
|
}
|
|
for _, c := range cases {
|
|
got := PageFooterPattern.MatchString(c.in)
|
|
if got != c.match {
|
|
t.Errorf("PageFooterPattern(%q): got %v want %v", c.in, got, c.match)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAllCapsHeadingPattern(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
match bool
|
|
}{
|
|
{"INTRODUCTION", true},
|
|
{"METHODS AND MATERIALS", true},
|
|
{"Mixed Case Heading", false},
|
|
{"ABC", false}, // <4 chars
|
|
}
|
|
for _, c := range cases {
|
|
got := AllCapsHeadingPattern.MatchString(c.in)
|
|
if got != c.match {
|
|
t.Errorf("AllCapsHeadingPattern(%q): got %v want %v", c.in, got, c.match)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSentenceSeparators(t *testing.T) {
|
|
if got := SentenceSeparators(LangChinese); got[0] != "。" {
|
|
t.Errorf("Chinese should start with 。, got %v", got)
|
|
}
|
|
if got := SentenceSeparators(LangEnglish); got[0] != ". " {
|
|
t.Errorf("English should start with '. ', got %v", got)
|
|
}
|
|
if got := SentenceSeparators("xx"); len(got) < 5 {
|
|
t.Errorf("Unknown lang should return mixed (>=5 separators), got %v", got)
|
|
}
|
|
}
|
|
|
|
func TestChapterPatternsForLangs(t *testing.T) {
|
|
if got := ChapterPatternsForLangs(nil); len(got) != 3 {
|
|
t.Errorf("nil langs should return all 3, got %d", len(got))
|
|
}
|
|
if got := ChapterPatternsForLangs([]string{LangGerman}); len(got) != 1 {
|
|
t.Errorf("only DE requested should return 1, got %d", len(got))
|
|
}
|
|
if got := ChapterPatternsForLangs([]string{LangGerman, LangChinese}); len(got) != 2 {
|
|
t.Errorf("DE+ZH requested should return 2, got %d", len(got))
|
|
}
|
|
if got := ChapterPatternsForLangs([]string{"xx"}); len(got) != 3 {
|
|
t.Errorf("unknown lang should fall back to all 3, got %d", len(got))
|
|
}
|
|
}
|