opencode-cursor-agent/internal/parser/stream_test.go

165 lines
3.9 KiB
Go
Raw Normal View History

2026-03-30 14:09:15 +00:00
package parser
import (
"encoding/json"
"testing"
)
func makeAssistantLine(text string) string {
obj := map[string]interface{}{
"type": "assistant",
"message": map[string]interface{}{
"content": []map[string]interface{}{
{"type": "text", "text": text},
},
},
}
b, _ := json.Marshal(obj)
return string(b)
}
func makeResultLine() string {
b, _ := json.Marshal(map[string]string{"type": "result", "subtype": "success"})
return string(b)
}
func TestStreamParserIncrementalDeltas(t *testing.T) {
var texts []string
doneCount := 0
parse := CreateStreamParser(
func(text string) { texts = append(texts, text) },
func() { doneCount++ },
)
parse(makeAssistantLine("Hello"))
if len(texts) != 1 || texts[0] != "Hello" {
t.Fatalf("expected [Hello], got %v", texts)
}
parse(makeAssistantLine("Hello world"))
if len(texts) != 2 || texts[1] != " world" {
t.Fatalf("expected second call with ' world', got %v", texts)
}
}
func TestStreamParserDeduplicatesFinalMessage(t *testing.T) {
var texts []string
parse := CreateStreamParser(
func(text string) { texts = append(texts, text) },
func() {},
)
parse(makeAssistantLine("Hi"))
parse(makeAssistantLine("Hi there"))
if len(texts) != 2 {
t.Fatalf("expected 2 calls, got %d: %v", len(texts), texts)
}
if texts[0] != "Hi" || texts[1] != " there" {
t.Fatalf("unexpected texts: %v", texts)
}
// Final duplicate: full accumulated text again
parse(makeAssistantLine("Hi there"))
if len(texts) != 2 {
t.Fatalf("expected no new call after duplicate, got %d: %v", len(texts), texts)
}
}
func TestStreamParserCallsOnDone(t *testing.T) {
var texts []string
doneCount := 0
parse := CreateStreamParser(
func(text string) { texts = append(texts, text) },
func() { doneCount++ },
)
parse(makeResultLine())
if doneCount != 1 {
t.Fatalf("expected onDone called once, got %d", doneCount)
}
if len(texts) != 0 {
t.Fatalf("expected no text, got %v", texts)
}
}
func TestStreamParserIgnoresLinesAfterDone(t *testing.T) {
var texts []string
doneCount := 0
parse := CreateStreamParser(
func(text string) { texts = append(texts, text) },
func() { doneCount++ },
)
parse(makeResultLine())
parse(makeAssistantLine("late"))
if len(texts) != 0 {
t.Fatalf("expected no text after done, got %v", texts)
}
if doneCount != 1 {
t.Fatalf("expected onDone called once, got %d", doneCount)
}
}
func TestStreamParserIgnoresNonAssistantLines(t *testing.T) {
var texts []string
parse := CreateStreamParser(
func(text string) { texts = append(texts, text) },
func() {},
)
b1, _ := json.Marshal(map[string]interface{}{"type": "user", "message": map[string]interface{}{}})
parse(string(b1))
b2, _ := json.Marshal(map[string]interface{}{
"type": "assistant",
"message": map[string]interface{}{"content": []interface{}{}},
})
parse(string(b2))
parse(`{"type":"assistant","message":{"content":[{"type":"code","text":"x"}]}}`)
if len(texts) != 0 {
t.Fatalf("expected no texts, got %v", texts)
}
}
func TestStreamParserIgnoresParseErrors(t *testing.T) {
var texts []string
doneCount := 0
parse := CreateStreamParser(
func(text string) { texts = append(texts, text) },
func() { doneCount++ },
)
parse("not json")
parse("{")
parse("")
if len(texts) != 0 || doneCount != 0 {
t.Fatalf("expected nothing, got texts=%v done=%d", texts, doneCount)
}
}
func TestStreamParserJoinsMultipleTextParts(t *testing.T) {
var texts []string
parse := CreateStreamParser(
func(text string) { texts = append(texts, text) },
func() {},
)
obj := map[string]interface{}{
"type": "assistant",
"message": map[string]interface{}{
"content": []map[string]interface{}{
{"type": "text", "text": "Hello"},
{"type": "text", "text": " "},
{"type": "text", "text": "world"},
},
},
}
b, _ := json.Marshal(obj)
parse(string(b))
if len(texts) != 1 || texts[0] != "Hello world" {
t.Fatalf("expected ['Hello world'], got %v", texts)
}
}