ollie/format/format_test.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)
}
}
}