Files
Agent-Native/runtime/session/microcompact_test.go
T
weichen 6ddd3da8df Enable canonical versioned Go module consumption
Why: Allow consumers to download an immutable module revision without nested source checkouts or local path replacements.

Evidence: SELF package tests and the prior canonical-path rejection are recorded in the Dev dependency-workspace task card; remote consumption is replayed against the committed candidate.

Impact: Change the module identity and its Go imports while preserving API behavior. Existing historical submodule revisions remain buildable.

Refs: Versioned dependencies U1/U3; feature/dependency-workspace
2026-10-13 15:36:10 +00:00

137 lines
4.1 KiB
Go

package session
import (
"os"
"path/filepath"
"strings"
"testing"
"git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/llm"
)
func TestClearOldToolResultsKeepsLastFive(t *testing.T) {
var msgs []llm.Message
for i := 0; i < 8; i++ {
id := string(rune('a' + i))
msgs = append(msgs,
llm.Message{Role: llm.RoleAssistant, ToolCalls: []llm.ToolCall{{ID: id, Name: "read_file"}}},
llm.Message{Role: llm.RoleTool, Name: "read_file", ToolCallID: id, Content: "正文-" + id},
)
}
out := ClearOldToolResults(msgs, 5)
if len(out) != len(msgs) {
t.Fatalf("len %d, want %d", len(out), len(msgs))
}
cleared := 0
for i, m := range out {
if m.Role != llm.RoleTool {
if m.Role != msgs[i].Role || len(m.ToolCalls) != len(msgs[i].ToolCalls) {
t.Fatalf("pairing changed at %d", i)
}
continue
}
if m.ToolCallID != msgs[i].ToolCallID {
t.Fatalf("tool id changed at %d", i)
}
if strings.Contains(m.Content, "旧工具结果已清理") {
cleared++
if !strings.Contains(m.Content, m.ToolCallID) {
t.Fatalf("placeholder missing call id: %s", m.Content)
}
} else if m.Content != msgs[i].Content {
t.Fatalf("recent result changed: %s", m.Content)
}
}
if cleared != 3 {
t.Fatalf("cleared %d, want 3", cleared)
}
for _, m := range out[len(out)-10:] {
if m.Role == llm.RoleTool && strings.Contains(m.Content, "旧工具结果已清理") {
t.Fatalf("last five were cleared: %s", m.ToolCallID)
}
}
}
func TestNeedsClearUsesConservativeWindow(t *testing.T) {
if NeedsClear(10, 0, 0, 0) {
t.Fatal("small estimate must fit the default window")
}
if !NeedsClear(DefaultContextWindow, 0, DefaultOutputReserve, DefaultContextBuffer) {
t.Fatal("estimate at the default window must clear")
}
}
// ---- N2 WP1:旧工具结果落盘(路径占位)----
func spillFixture() []llm.Message {
msgs := []llm.Message{{Role: llm.RoleUser, Content: "u"}}
for i := 0; i < 7; i++ {
id := "call-" + string(rune('a'+i))
msgs = append(msgs,
llm.Message{Role: llm.RoleAssistant, ToolCalls: []llm.ToolCall{{ID: id, Name: "read", Arguments: "{}"}}},
llm.Message{Role: llm.RoleTool, ToolCallID: id, Name: "read", Content: "完整结果内容-" + id})
}
return msgs
}
func TestClearOldToolResultsSpillsToDisk(t *testing.T) {
dir := t.TempDir()
out := ClearOldToolResultsTo(spillFixture(), 5, dir)
if len(out) != len(spillFixture()) {
t.Fatal("消息条数不得变")
}
// 最旧两条被落盘,占位带路径。
for i, id := range []string{"call-a", "call-b"} {
path := filepath.Join(dir, id+".txt")
b, err := os.ReadFile(path)
if err != nil {
t.Fatalf("落盘文件缺失:%v", err)
}
if string(b) != "完整结果内容-"+id {
t.Fatalf("落盘内容不符:%q", b)
}
placeholder := out[2+i*2].Content
if !strings.Contains(placeholder, path) {
t.Fatalf("占位未带路径:%q", placeholder)
}
}
// 保留的最近 5 条正文不动。
if out[len(out)-1].Content != "完整结果内容-call-g" {
t.Fatalf("最近结果被误改:%q", out[len(out)-1].Content)
}
}
func TestClearOldToolResultsEmptyDirKeepsLegacyPlaceholder(t *testing.T) {
out := ClearOldToolResultsTo(spillFixture(), 5, "")
legacy := ClearOldToolResults(spillFixture(), 5)
for i := range out {
if out[i].Content != legacy[i].Content {
t.Fatalf("空 dir 应与旧行为逐字节一致:msg %d", i)
}
}
}
func TestClearOldToolResultsSpillFailureFallsBack(t *testing.T) {
// 不可写目录:占位回落为转录指针,不阻断。
blocked := filepath.Join(t.TempDir(), "file")
if err := os.WriteFile(blocked, []byte("x"), 0o644); err != nil {
t.Fatal(err)
}
out := ClearOldToolResultsTo(spillFixture(), 5, filepath.Join(blocked, "sub"))
if !strings.Contains(out[2].Content, "会话记录中的调用 call-a") {
t.Fatalf("写盘失败应回落转录指针:%q", out[2].Content)
}
}
func TestSpillResultSanitizesCallID(t *testing.T) {
dir := t.TempDir()
path, ok := spillResult(dir, "call/../evil\x00id", "x")
if !ok {
t.Fatal("spill 失败")
}
base := filepath.Base(path)
if strings.Contains(base, "/") || strings.ContainsRune(base, 0) || filepath.Dir(path) != dir {
t.Fatalf("文件名未消毒或越出目录:%q", path)
}
}