189 lines
5.4 KiB
Go
189 lines
5.4 KiB
Go
package format
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseBlockHeader(t *testing.T) {
|
|
tests := []struct {
|
|
line string
|
|
wantNil bool
|
|
wantRole string
|
|
wantName string
|
|
wantID string
|
|
}{
|
|
// Valid headers
|
|
{"[[[user]]]", false, "user", "", ""},
|
|
{"[[[user#abc12345]]]", false, "user", "", "abc12345"},
|
|
{"[[[assistant]]]", false, "assistant", "", ""},
|
|
{"[[[assistant#def67890]]]", false, "assistant", "", "def67890"},
|
|
{"[[[assistant:resp_123#aabbccdd]]]", false, "assistant", "resp_123", "aabbccdd"},
|
|
{"[[[tool:shell]]]", false, "tool", "shell", ""},
|
|
{"[[[tool:shell#11223344]]]", false, "tool", "shell", "11223344"},
|
|
{"[[[call:file_read]]]", false, "call", "file_read", ""},
|
|
{"[[[call:file_read#99887766]]]", false, "call", "file_read", "99887766"},
|
|
{"[[[reasoning]]]", false, "reasoning", "", ""},
|
|
{"[[[reasoning#aabbcc00]]]", false, "reasoning", "", "aabbcc00"},
|
|
{"[[[retry]]]", false, "retry", "", ""},
|
|
{"[[[end]]]", false, "end", "", ""},
|
|
{"[[[error]]]", false, "error", "", ""},
|
|
{"[[[error:file_read#12345678]]]", false, "error", "file_read", "12345678"},
|
|
{"[[[info]]]", false, "info", "", ""},
|
|
{"[[[stalled]]]", false, "stalled", "", ""},
|
|
|
|
// Invalid headers
|
|
{"", true, "", "", ""},
|
|
{"[[[]]]", true, "", "", ""},
|
|
{"not a header", true, "", "", ""},
|
|
{"[[user]]", true, "", "", ""},
|
|
{"[[[user", true, "", "", ""},
|
|
{"user]]]", true, "", "", ""},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
bh := ParseBlockHeader(tt.line)
|
|
if tt.wantNil {
|
|
if bh != nil {
|
|
t.Errorf("ParseBlockHeader(%q): expected nil, got %+v", tt.line, bh)
|
|
}
|
|
continue
|
|
}
|
|
if bh == nil {
|
|
t.Errorf("ParseBlockHeader(%q): expected non-nil", tt.line)
|
|
continue
|
|
}
|
|
if bh.Role != tt.wantRole {
|
|
t.Errorf("ParseBlockHeader(%q): role = %q, want %q", tt.line, bh.Role, tt.wantRole)
|
|
}
|
|
if bh.Name != tt.wantName {
|
|
t.Errorf("ParseBlockHeader(%q): name = %q, want %q", tt.line, bh.Name, tt.wantName)
|
|
}
|
|
if bh.BlockID != tt.wantID {
|
|
t.Errorf("ParseBlockHeader(%q): blockID = %q, want %q", tt.line, bh.BlockID, tt.wantID)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBlockDelim(t *testing.T) {
|
|
tests := []struct {
|
|
role, name, blockID string
|
|
want string
|
|
}{
|
|
{"user", "", "abc12345", "[[[user#abc12345]]]\n"},
|
|
{"assistant", "", "def67890", "[[[assistant#def67890]]]\n"},
|
|
{"tool", "shell", "11223344", "[[[tool:shell#11223344]]]\n"},
|
|
{"call", "file_read", "99887766", "[[[call:file_read#99887766]]]\n"},
|
|
{"error", "test", "aabbccdd", "[[[error:test#aabbccdd]]]\n"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := BlockDelim(tt.role, tt.name, tt.blockID)
|
|
if got != tt.want {
|
|
t.Errorf("BlockDelim(%q, %q, %q) = %q, want %q", tt.role, tt.name, tt.blockID, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFormatEvent_WithBlockID(t *testing.T) {
|
|
tests := []struct {
|
|
role, name, content, outputFormat, blockID string
|
|
wantContains []string
|
|
}{
|
|
{
|
|
"user", "", "hello", "", "abc12345",
|
|
[]string{"[[[user#abc12345]]]", "hello", "[[[end]]]"},
|
|
},
|
|
{
|
|
"call", "shell", `{"cmd": "ls"}`, "", "def67890",
|
|
[]string{"[[[call:shell#def67890]]]", `{"cmd": "ls"}`, "[[[end]]]"},
|
|
},
|
|
{
|
|
"tool", "shell", "output", "text", "11223344",
|
|
[]string{"[[[tool:shell#11223344]]]", "output", "[[[end]]]"},
|
|
},
|
|
{
|
|
"error", "", "something failed", "", "aabbccdd",
|
|
[]string{"[[[error#aabbccdd]]]", "something failed", "[[[end]]]"},
|
|
},
|
|
{
|
|
"error", "test", "something failed", "", "eeff0011",
|
|
[]string{"[[[error:test#eeff0011]]]", "something failed", "[[[end]]]"},
|
|
},
|
|
{
|
|
"info", "", "status update", "", "22334455",
|
|
[]string{"[[[info#22334455]]]", "status update", "[[[end]]]"},
|
|
},
|
|
{
|
|
"stalled", "", "", "", "66778899",
|
|
[]string{"[[[stalled#66778899]]]", "[[[end]]]"},
|
|
},
|
|
{
|
|
"maxsteps", "", "limit reached", "", "aabbcc00",
|
|
[]string{"[[[maxsteps#aabbcc00]]]", "limit reached", "[[[end]]]"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := string(FormatEvent(tt.role, tt.name, tt.content, tt.outputFormat, tt.blockID))
|
|
for _, want := range tt.wantContains {
|
|
if !strings.Contains(got, want) {
|
|
t.Errorf("FormatEvent(%q, %q, ...): missing %q in:\n%s", tt.role, tt.name, want, got)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIsEndMarker(t *testing.T) {
|
|
tests := []struct {
|
|
line string
|
|
want bool
|
|
}{
|
|
{"[[[end]]]", true},
|
|
{"[[[end]]", false},
|
|
{"[[end]]]", false},
|
|
{"[[[END]]]", false},
|
|
{"[[[end]]] ", false},
|
|
{" [[[end]]]", false},
|
|
{"", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := IsEndMarker(tt.line)
|
|
if got != tt.want {
|
|
t.Errorf("IsEndMarker(%q) = %v, want %v", tt.line, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRoundtrip_ParseBlockHeader(t *testing.T) {
|
|
// Test that BlockDelim output can be parsed by ParseBlockHeader
|
|
tests := []struct {
|
|
role, name, blockID string
|
|
}{
|
|
{"user", "", "abc12345"},
|
|
{"tool", "shell", "def67890"},
|
|
{"call", "file_read", "11223344"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
delim := BlockDelim(tt.role, tt.name, tt.blockID)
|
|
// Trim the trailing newline for parsing
|
|
delim = strings.TrimSuffix(delim, "\n")
|
|
bh := ParseBlockHeader(delim)
|
|
if bh == nil {
|
|
t.Errorf("ParseBlockHeader(BlockDelim(%q, %q, %q)) returned nil", tt.role, tt.name, tt.blockID)
|
|
continue
|
|
}
|
|
if bh.Role != tt.role {
|
|
t.Errorf("roundtrip role: got %q, want %q", bh.Role, tt.role)
|
|
}
|
|
if bh.Name != tt.name {
|
|
t.Errorf("roundtrip name: got %q, want %q", bh.Name, tt.name)
|
|
}
|
|
if bh.BlockID != tt.blockID {
|
|
t.Errorf("roundtrip blockID: got %q, want %q", bh.BlockID, tt.blockID)
|
|
}
|
|
}
|
|
}
|