165 lines
3.9 KiB
Go
165 lines
3.9 KiB
Go
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)
|
|
}
|
|
}
|