Files
Sakurasan d5dac5a6fc feat: AutoZero 通用 Agent 框架(从 AutoGo 五阶段拆分完成)
llm(三协议 Provider)/ session(JSONL 持久化)/ tools(Registry、围栏、
builtin、computer)/ cdp(browser)/ skills / mcp / agent(循环、两级压缩、
编排、审批门控)+ 底座 jsonl/textx/execx/diag/scratch/spill/imagecache。

产品装配代码(config 桥接、schedule 工具、memory/plan/retro/prompt)
留在 AutoGo,宿主接入方式见 MIGRATION.md。
2026-10-26 00:54:46 +08:00

83 lines
2.8 KiB
Go
Raw Permalink 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.
package textx
import (
"strings"
"testing"
"unicode/utf8"
)
// 中文每字 3 字节、emoji 4 字节:按字节下标切下去,正好切开的位置都要覆盖到。
func TestClipBytesNeverSplitsACharacter(t *testing.T) {
s := "删除全部缓存文件🗑️继续" // 混合:中文 3 字节、带变体选择符的 emoji、再中文
cases := []struct {
name string
s string
n int
}{
{"切在中文中间", s, 4},
{"切在中文中间2", s, 5},
{"切在 emoji 中间", s, 8},
{"预算为 0", s, 0},
{"预算为负", s, -3},
{"正好边界", "你好", 3},
{"一个半字", "你好", 4},
{"短于预算", "hi", 10},
{"4 字节字符", "😀😀", 5},
}
for _, c := range cases {
got := ClipBytes(c.s, c.n)
if !utf8.ValidString(got) {
t.Errorf("%s:结果非法 UTF-8(%q ← %q 取 %d 字节)", c.name, got, c.s, c.n)
}
if len(got) > c.n && c.n > 0 {
t.Errorf("%s:超出预算 %d 字节,得到 %d", c.name, c.n, len(got))
}
if c.n >= len(c.s) && got != c.s {
t.Errorf("%s:没超预算却改动了原文 %q", c.name, got)
}
}
}
func TestClipRunesCountsCharacters(t *testing.T) {
if got := ClipRunes("删除全部缓存", 3); got != "删除全" {
t.Errorf("按字截断 = %q,want 删除全", got)
}
// 按码点算:😀 是 1 个,而 🗑️ 是「U+1F5D1 + 变体选择符 U+FE0F」两个码点。
// 这不是切坏字符(每个码点都完整),只是"字数"的口径本来就按码点——
// 要按终端显示宽度算,用带宽度版本的截断(TUI 侧是 cutDisplay)。
if got := ClipRunes("😀🗑️x", 2); got != "😀🗑" {
t.Errorf("按码点计数不对:%q", got)
}
if got := ClipRunes("😀x", 1); got != "😀" {
t.Errorf("4 字节字符应整体保留:%q", got)
}
if got := ClipRunes("abc", 0); got != "" {
t.Errorf("0 应得空串,得到 %q", got)
}
if got := ClipRunes("abc", 9); got != "abc" {
t.Errorf("未超字数却改动了原文:%q", got)
}
// 长串必须仍然是合法 UTF-8(这是这类 bug 的表现形式)。
long := strings.Repeat("中文", 500)
if !utf8.ValidString(ClipRunes(long, 3)) {
t.Error("按字截断产生了非法字符")
}
}
// TestClipBothValidOnEveryOffset 是回归护栏:对同一串中文,任意预算下两种截断
// 都必须落在字符边界上。原来的 `s[:n]` 写法在这里会成片失败。
func TestClipBothValidOnEveryOffset(t *testing.T) {
s := "改一下 a.txt:把中文标题与 emoji 😀 都留下"
for n := 0; n <= len(s)+2; n++ {
if b := ClipBytes(s, n); !utf8.ValidString(b) {
t.Fatalf("ClipBytes(%d) 非法:%q", n, b)
}
}
for n := 0; n <= utf8.RuneCountInString(s)+2; n++ {
r := ClipRunes(s, n)
if !utf8.ValidString(r) {
t.Fatalf("ClipRunes(%d) 非法:%q", n, r)
}
}
}