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。
106 lines
3.4 KiB
Go
106 lines
3.4 KiB
Go
package session
|
||
|
||
import (
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
|
||
"git.gopher.ink/mirrors2/AutoZero/llm"
|
||
)
|
||
|
||
// TestNewNameSkipsExisting:时间戳撞上已有文件时依次加 -2/-3,绝不指向别人的会话。
|
||
// 不依赖时钟(直接喂固定 ts),所以不会因跨过一秒而变哑。
|
||
func TestNewNameSkipsExisting(t *testing.T) {
|
||
dir := t.TempDir()
|
||
ts := "20260101-000000"
|
||
|
||
if got := newName(dir, ts); got != filepath.Join(dir, ts+".jsonl") {
|
||
t.Fatalf("干净目录里应直接用基础名,得到 %q", got)
|
||
}
|
||
|
||
writeFile(t, filepath.Join(dir, ts+".jsonl"))
|
||
if got := newName(dir, ts); got != filepath.Join(dir, ts+"-2.jsonl") {
|
||
t.Fatalf("基础名被占时应给 -2,得到 %q", got)
|
||
}
|
||
|
||
writeFile(t, filepath.Join(dir, ts+"-2.jsonl"))
|
||
if got := newName(dir, ts); got != filepath.Join(dir, ts+"-3.jsonl") {
|
||
t.Fatalf("前两个都被占时应给 -3,得到 %q", got)
|
||
}
|
||
}
|
||
|
||
// TestNewDoesNotRecoverOldSession:连续两次新建必须拿到不同名字,第二份还是空的。
|
||
// 撞名的真实后果:/session new 后立刻发消息(文件写下内容),同一秒内再新建,
|
||
// 前端拿 LoadOrCreate 打开同名文件,"新建"就变成了把旧会话整份恢复回来。
|
||
func TestNewDoesNotRecoverOldSession(t *testing.T) {
|
||
t.Setenv("HOME", t.TempDir())
|
||
|
||
first, err := New()
|
||
if err != nil {
|
||
t.Fatalf("New: %v", err)
|
||
}
|
||
if err := first.Append(llm.Message{Role: llm.RoleUser, Content: "history"}); err != nil {
|
||
t.Fatalf("append: %v", err)
|
||
}
|
||
|
||
second, err := New()
|
||
if err != nil {
|
||
t.Fatalf("New: %v", err)
|
||
}
|
||
if second.Path == first.Path {
|
||
t.Fatalf("两次新建拿到同一个名字 %s", second.Path)
|
||
}
|
||
if len(second.Messages) != 0 {
|
||
t.Fatalf("新会话应为空,得到 %d 条", len(second.Messages))
|
||
}
|
||
if _, err := os.Stat(second.Path); !os.IsNotExist(err) {
|
||
t.Fatalf("新会话文件不该在这一步出现(err=%v)", err)
|
||
}
|
||
// 经渠道/前端实际会走的路径再打开一次,仍然是空的
|
||
byName := strings.TrimSuffix(filepath.Base(second.Path), ".jsonl")
|
||
again, err := LoadOrCreate(byName)
|
||
if err != nil {
|
||
t.Fatalf("LoadOrCreate: %v", err)
|
||
}
|
||
if len(again.Messages) != 0 {
|
||
t.Fatalf("按名字打开新会话却拿到 %d 条消息", len(again.Messages))
|
||
}
|
||
|
||
// 第一份的历史不受影响
|
||
reloaded, err := Load(first.Path)
|
||
if err != nil {
|
||
t.Fatalf("load: %v", err)
|
||
}
|
||
if len(reloaded.Messages) != 1 || reloaded.Messages[0].Content != "history" {
|
||
t.Fatalf("旧会话被改动: %+v", reloaded.Messages)
|
||
}
|
||
}
|
||
|
||
// TestNewNameSkipsUnwrittenIssued:两份新会话都还没落盘时也不能同名。
|
||
// 会话文件要到第一条消息才写出来,所以"盘上没有"不等于"没人用"。
|
||
func TestNewNameSkipsUnwrittenIssued(t *testing.T) {
|
||
dir := t.TempDir()
|
||
ts := "20260202-120000"
|
||
|
||
first := newName(dir, ts)
|
||
second := newName(dir, ts)
|
||
if first == second {
|
||
t.Fatalf("两次取名拿到同一个名字 %s(第一份还没落盘,Stat 看不见)", first)
|
||
}
|
||
if strings.TrimSuffix(filepath.Base(second), ".jsonl") != ts+"-2" {
|
||
t.Fatalf("第二个名字 = %q,want %s-2.jsonl", second, ts)
|
||
}
|
||
// 已发出的名字不许被复用
|
||
if got := newName(dir, ts); got == first || got == second {
|
||
t.Fatalf("复用了已发出的名字: %q", got)
|
||
}
|
||
}
|
||
|
||
func writeFile(t *testing.T, path string) {
|
||
t.Helper()
|
||
if err := os.WriteFile(path, []byte("{}\n"), 0o644); err != nil {
|
||
t.Fatalf("write %s: %v", path, err)
|
||
}
|
||
}
|