- waitOutgoing/sendWithError:重启通知、SendToSession(含图片/文件)、权限与 AskQuestion 内联按钮路径纳入出站令牌桶 - EventResult 分块与 channelClosed 收尾使用 sendWithError,首包失败即停止发送 - ProgressStyleProvider 且 progress_style=legacy 时不再单独推送 EventToolResult 聊天消息(compact/card 仍走 writer) - 工具结果 fallback 标签改为 MsgKey,补全日/西/繁中翻译 - config.example.toml:说明小数 max_per_second 时 burst 取 ceil - 修复 processInteractiveEvents 测试参数;新增 SuppressStandaloneToolResultEvent 单测 - fix(codex): 假 codex 脚本用 stdin 读 prompt,适配 --json -
118 lines
4.2 KiB
Go
118 lines
4.2 KiB
Go
package core
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
type suppressTestPlatform struct {
|
|
style string
|
|
}
|
|
|
|
func (s *suppressTestPlatform) Name() string { return "test" }
|
|
func (s *suppressTestPlatform) Start(MessageHandler) error { return nil }
|
|
func (s *suppressTestPlatform) Reply(context.Context, any, string) error { return nil }
|
|
func (s *suppressTestPlatform) Send(context.Context, any, string) error { return nil }
|
|
func (s *suppressTestPlatform) Stop() error { return nil }
|
|
func (s *suppressTestPlatform) ProgressStyle() string { return s.style }
|
|
|
|
func TestSuppressStandaloneToolResultEvent(t *testing.T) {
|
|
if SuppressStandaloneToolResultEvent(&stubPlatformNoProgress{}) {
|
|
t.Fatal("platform without ProgressStyleProvider should not suppress")
|
|
}
|
|
if !SuppressStandaloneToolResultEvent(&suppressTestPlatform{style: "legacy"}) {
|
|
t.Fatal("legacy ProgressStyleProvider should suppress standalone tool results")
|
|
}
|
|
if SuppressStandaloneToolResultEvent(&suppressTestPlatform{style: "compact"}) {
|
|
t.Fatal("compact should not suppress (writer absorbs tool results)")
|
|
}
|
|
if SuppressStandaloneToolResultEvent(&suppressTestPlatform{style: "card"}) {
|
|
t.Fatal("card should not suppress")
|
|
}
|
|
}
|
|
|
|
// stubPlatformNoProgress is a minimal Platform without ProgressStyleProvider.
|
|
type stubPlatformNoProgress struct{}
|
|
|
|
func (stubPlatformNoProgress) Name() string { return "plain" }
|
|
func (stubPlatformNoProgress) Start(MessageHandler) error { return nil }
|
|
func (stubPlatformNoProgress) Reply(context.Context, any, string) error { return nil }
|
|
func (stubPlatformNoProgress) Send(context.Context, any, string) error { return nil }
|
|
func (stubPlatformNoProgress) Stop() error { return nil }
|
|
|
|
func TestBuildAndParseProgressCardPayload(t *testing.T) {
|
|
payload := BuildProgressCardPayload([]string{" step1 ", "", "step2"}, true)
|
|
if payload == "" {
|
|
t.Fatal("BuildProgressCardPayload returned empty string")
|
|
}
|
|
if !strings.HasPrefix(payload, ProgressCardPayloadPrefix) {
|
|
t.Fatalf("payload = %q, want prefix %q", payload, ProgressCardPayloadPrefix)
|
|
}
|
|
|
|
parsed, ok := ParseProgressCardPayload(payload)
|
|
if !ok {
|
|
t.Fatalf("ParseProgressCardPayload should succeed, payload=%q", payload)
|
|
}
|
|
if len(parsed.Entries) != 2 {
|
|
t.Fatalf("entries = %d, want 2", len(parsed.Entries))
|
|
}
|
|
if parsed.Entries[0] != "step1" || parsed.Entries[1] != "step2" {
|
|
t.Fatalf("entries = %#v, want [step1 step2]", parsed.Entries)
|
|
}
|
|
if !parsed.Truncated {
|
|
t.Fatal("parsed.Truncated = false, want true")
|
|
}
|
|
if len(parsed.Items) != 2 {
|
|
t.Fatalf("items = %d, want 2", len(parsed.Items))
|
|
}
|
|
if parsed.Items[0].Kind != ProgressEntryInfo || parsed.Items[0].Text != "step1" {
|
|
t.Fatalf("items[0] = %#v, want info/step1", parsed.Items[0])
|
|
}
|
|
}
|
|
|
|
func TestBuildAndParseProgressCardPayloadV2(t *testing.T) {
|
|
payload := BuildProgressCardPayloadV2([]ProgressCardEntry{
|
|
{Kind: ProgressEntryThinking, Text: " plan "},
|
|
{Kind: ProgressEntryToolUse, Tool: "Bash", Text: "pwd"},
|
|
}, false, "Codex", LangChinese, ProgressCardStateRunning)
|
|
if payload == "" {
|
|
t.Fatal("BuildProgressCardPayloadV2 returned empty string")
|
|
}
|
|
|
|
parsed, ok := ParseProgressCardPayload(payload)
|
|
if !ok {
|
|
t.Fatalf("ParseProgressCardPayload should succeed, payload=%q", payload)
|
|
}
|
|
if parsed.Version != 2 {
|
|
t.Fatalf("version = %d, want 2", parsed.Version)
|
|
}
|
|
if parsed.Agent != "Codex" {
|
|
t.Fatalf("agent = %q, want Codex", parsed.Agent)
|
|
}
|
|
if parsed.Lang != string(LangChinese) {
|
|
t.Fatalf("lang = %q, want %q", parsed.Lang, LangChinese)
|
|
}
|
|
if parsed.State != ProgressCardStateRunning {
|
|
t.Fatalf("state = %q, want %q", parsed.State, ProgressCardStateRunning)
|
|
}
|
|
if len(parsed.Items) != 2 {
|
|
t.Fatalf("items = %d, want 2", len(parsed.Items))
|
|
}
|
|
if parsed.Items[1].Kind != ProgressEntryToolUse || parsed.Items[1].Tool != "Bash" {
|
|
t.Fatalf("items[1] = %#v, want tool_use/Bash", parsed.Items[1])
|
|
}
|
|
}
|
|
|
|
func TestParseProgressCardPayloadRejectsInvalid(t *testing.T) {
|
|
if _, ok := ParseProgressCardPayload("plain text"); ok {
|
|
t.Fatal("expected parse failure for plain text")
|
|
}
|
|
if _, ok := ParseProgressCardPayload(ProgressCardPayloadPrefix + "{not-json"); ok {
|
|
t.Fatal("expected parse failure for invalid json")
|
|
}
|
|
if _, ok := ParseProgressCardPayload(ProgressCardPayloadPrefix + `{"entries":[]}`); ok {
|
|
t.Fatal("expected parse failure for empty entries")
|
|
}
|
|
}
|