Files
xiaoxia-saas/tests/unit/test_text_splitter.py
T
xiaoxia 76a0fdd00c
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Failing after 0s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 0s
CI/CD Pipeline / Build Staging Worker Image (push) Failing after 0s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
test(text_splitter): P3-1第十五波 长文本分段单测 32个 (#719)
2026-07-22 16:28:25 +08:00

316 lines
10 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
text_splitter 长文本分段工具单元测试
覆盖:
- 空文本 / 短文本
- 句子边界分段(。!?;\n . ! ? ;
- 超长句子硬切
- 过短段落合并
- max_chars 参数
- 中英文混合
"""
import pytest
from packages.application.tts_job.text_splitter import split_text
# ============================================================
# 基础场景
# ============================================================
class TestBasicCases:
"""基础场景"""
def test_empty_text_returns_empty_list(self):
assert split_text("") == []
def test_whitespace_only_returns_empty(self):
assert split_text(" \n\n ") == []
def test_short_text_single_segment(self):
text = "这是一段短文本。"
result = split_text(text, max_chars=500)
assert result == [text]
def test_exactly_max_chars_single_segment(self):
text = "a" * 500
result = split_text(text, max_chars=500)
assert len(result) == 1
assert len(result[0]) == 500
def test_text_stripped(self):
text = " 你好世界。 "
result = split_text(text, max_chars=500)
assert result == ["你好世界。"]
# ============================================================
# 句子边界分段
# ============================================================
class TestSentenceBoundarySplitting:
"""句子边界分段"""
def test_split_by_chinese_period(self):
text = "第一句。第二句。第三句。"
# 三句都很短,应该合并成一段
result = split_text(text, max_chars=500)
assert len(result) == 1
def test_split_by_chinese_period_long_text(self):
"""多段长句子,按句号分段"""
sentence1 = "我是第一句" + "啊" * 100 + "。"
sentence2 = "我是第二句" + "哦" * 100 + "。"
sentence3 = "我是第三句" + "嗯" * 100 + "。"
text = sentence1 + sentence2 + sentence3
result = split_text(text, max_chars=150)
# 每句106字符,超过150的阈值?不,106<150
# 但累计到一定程度会切
assert len(result) >= 2
# 每段都不超过 max_chars
for seg in result:
assert len(seg) <= 150
def test_split_by_question_mark(self):
text = "你是谁?你从哪里来?你要到哪里去?"
result = split_text(text, max_chars=500)
# 三句都很短,合并成一段
assert len(result) == 1
def test_split_by_exclamation_mark(self):
text = "太棒了!太厉害了!太牛了!"
result = split_text(text, max_chars=500)
assert len(result) == 1
def test_split_by_newline(self):
text = "第一段\n第二段\n第三段"
result = split_text(text, max_chars=500)
assert len(result) == 1
def test_split_by_semicolon(self):
text = "第一部分;第二部分;第三部分。"
result = split_text(text, max_chars=500)
assert len(result) == 1
def test_mixed_punctuation(self):
"""混合标点符号的句子边界"""
parts = []
for i in range(20):
parts.append(f"第{i}句的内容" + "字" * 30 + "。")
text = "".join(parts)
result = split_text(text, max_chars=200)
# 每句约35字符,200字符大约能放5-6句
assert len(result) >= 2
for seg in result:
assert len(seg) <= 200
def test_english_period_splitting(self):
text = "Hello. How are you. I am fine."
result = split_text(text, max_chars=500)
assert len(result) == 1
def test_english_question(self):
text = "What? Why? How?"
result = split_text(text, max_chars=500)
assert len(result) == 1
# ============================================================
# 超长硬切
# ============================================================
class TestLongSentenceHardCut:
"""超长句子硬切"""
def test_single_very_long_sentence_hard_cut(self):
"""单个超长句子,没有标点,硬切"""
text = "字" * 1000
result = split_text(text, max_chars=500)
assert len(result) == 2
assert len(result[0]) == 500
assert len(result[1]) == 500
def test_three_times_max_chars(self):
text = "字" * 1500
result = split_text(text, max_chars=500)
assert len(result) == 3
for seg in result:
assert len(seg) == 500
def test_not_exact_multiple(self):
text = "字" * 1250
result = split_text(text, max_chars=500)
assert len(result) == 3
assert len(result[0]) == 500
assert len(result[1]) == 500
assert len(result[2]) == 250
def test_all_segments_within_limit(self):
"""所有段都不超过 max_chars"""
import random
random.seed(42)
# 生成随机长度的文本
text = "".join(random.choices("字字字字。!?;\n", k=5000))
for max_chars in [100, 200, 500]:
result = split_text(text, max_chars=max_chars)
for i, seg in enumerate(result):
assert len(seg) <= max_chars, f"Segment {i} length {len(seg)} > {max_chars}"
# ============================================================
# 过短段落合并
# ============================================================
class TestShortSegmentMerging:
"""过短段落合并"""
def test_short_final_segment_merged(self):
"""最后一段过短,应该合并到前一段"""
# 构造:前一段接近上限,后一段很短
long_part = "字" * 480 + "。"
short_part = "好的。"
text = long_part + short_part
result = split_text(text, max_chars=500)
# 两段加起来 481+3=484 < 500,可能合并
# 但要看具体实现...
# 至少验证所有段不超长
for seg in result:
assert len(seg) <= 500
def test_multiple_short_segments(self):
"""多个短段落应该合并"""
sentences = ["你好。", "我好。", "大家好。", "今天天气不错。", "适合出去玩。"]
text = "".join(sentences)
result = split_text(text, max_chars=500)
# 5个短句子,应该合并成一段
assert len(result) == 1
# ============================================================
# max_chars 参数
# ============================================================
class TestMaxCharsParameter:
"""max_chars 参数"""
def test_small_max_chars(self):
text = "一二三四五六七八九十一二三四五六七八九十。"
result = split_text(text, max_chars=10)
# 应该被切成多段
assert len(result) >= 2
for seg in result:
assert len(seg) <= 10
def test_custom_max_chars_200(self):
text = "测试文本" * 100 # 400字符
result = split_text(text, max_chars=200)
assert len(result) == 2
assert len(result[0]) == 200
assert len(result[1]) == 200
def test_very_small_max_chars(self):
text = "abcdefghij"
result = split_text(text, max_chars=3)
assert len(result) >= 3
for seg in result:
assert len(seg) <= 3
# ============================================================
# 中英文混合
# ============================================================
class TestMixedContent:
"""中英文混合内容"""
def test_chinese_english_mixed(self):
text = "今天天气很好,Today is sunny. 我们去公园玩吧!Let's go to the park."
result = split_text(text, max_chars=500)
assert len(result) == 1
assert result[0] == text.strip()
def test_mixed_long_text(self):
parts = []
for i in range(50):
parts.append(f"第{i}段中文内容" + "字" * 20 + ". English part " + "word " * 10 + "。")
text = "".join(parts)
result = split_text(text, max_chars=300)
assert len(result) >= 2
for seg in result:
assert len(seg) <= 300
# ============================================================
# 输出完整性
# ============================================================
class TestOutputIntegrity:
"""输出完整性验证"""
def test_combined_length_equals_original(self):
"""所有段拼接起来(去掉空段)应该等于原文长度"""
text = "这是第一段。这是第二段。这是第三段。这是第四段。这是第五段。" * 20
result = split_text(text, max_chars=100)
combined = "".join(result)
# 由于 strip 可能去掉一些空格,原文也 strip 比较
assert len(combined) == len(text.strip())
def test_order_preserved(self):
"""分段后再拼接,文本顺序不变"""
text = "第一。第二。第三。第四。第五。" * 10
result = split_text(text, max_chars=50)
combined = "".join(result)
assert combined == text.strip()
def test_no_empty_strings_in_result(self):
"""结果中没有空字符串"""
text = "句子一。句子二。句子三。"
result = split_text(text, max_chars=10)
for seg in result:
assert seg != ""
assert len(seg) > 0
# ============================================================
# 边界情况
# ============================================================
class TestEdgeCases:
"""边界情况"""
def test_single_character(self):
assert split_text("一", max_chars=500) == ["一"]
def test_only_punctuation(self):
text = "。。。。。"
result = split_text(text, max_chars=500)
# 都是标点,也算文本
assert len(result) == 1
def test_only_newlines(self):
text = "\n\n\n"
result = split_text(text, max_chars=500)
assert result == []
def test_long_text_many_sentences(self):
"""大量句子的长文本"""
sentences = [f"第{i}句的完整内容。" for i in range(100)]
text = "".join(sentences)
result = split_text(text, max_chars=200)
assert len(result) >= 5
for seg in result:
assert len(seg) <= 200