|
|
@@ -0,0 +1,194 @@
|
|
|
+package decoders
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "io"
|
|
|
+ "strings"
|
|
|
+ "testing"
|
|
|
+)
|
|
|
+
|
|
|
+func TestUnboundedDecoder_SmallEvent(t *testing.T) {
|
|
|
+ data := "event: test\ndata: hello world\n\n"
|
|
|
+ rc := io.NopCloser(strings.NewReader(data))
|
|
|
+ decoder := NewUnboundedDecoder(rc)
|
|
|
+
|
|
|
+ if !decoder.Next() {
|
|
|
+ t.Fatal("Expected Next() to return true")
|
|
|
+ }
|
|
|
+
|
|
|
+ evt := decoder.Event()
|
|
|
+ if evt.Type != "test" {
|
|
|
+ t.Errorf("Expected event type 'test', got '%s'", evt.Type)
|
|
|
+ }
|
|
|
+ if string(evt.Data) != "hello world\n" {
|
|
|
+ t.Errorf("Expected data 'hello world\\n', got '%s'", string(evt.Data))
|
|
|
+ }
|
|
|
+
|
|
|
+ if decoder.Next() {
|
|
|
+ t.Error("Expected Next() to return false at end of stream")
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := decoder.Err(); err != nil {
|
|
|
+ t.Errorf("Expected no error, got %v", err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestUnboundedDecoder_LargeEvent(t *testing.T) {
|
|
|
+ // Create a large event (50MB)
|
|
|
+ size := 50 * 1024 * 1024
|
|
|
+ largeData := strings.Repeat("x", size)
|
|
|
+
|
|
|
+ var buf bytes.Buffer
|
|
|
+ buf.WriteString("event: large\n")
|
|
|
+ buf.WriteString("data: ")
|
|
|
+ buf.WriteString(largeData)
|
|
|
+ buf.WriteString("\n\n")
|
|
|
+
|
|
|
+ rc := io.NopCloser(&buf)
|
|
|
+ decoder := NewUnboundedDecoder(rc)
|
|
|
+
|
|
|
+ if !decoder.Next() {
|
|
|
+ t.Fatal("Expected Next() to return true")
|
|
|
+ }
|
|
|
+
|
|
|
+ evt := decoder.Event()
|
|
|
+ if evt.Type != "large" {
|
|
|
+ t.Errorf("Expected event type 'large', got '%s'", evt.Type)
|
|
|
+ }
|
|
|
+
|
|
|
+ expectedData := largeData + "\n"
|
|
|
+ if string(evt.Data) != expectedData {
|
|
|
+ t.Errorf("Data size mismatch: expected %d, got %d", len(expectedData), len(evt.Data))
|
|
|
+ }
|
|
|
+
|
|
|
+ if decoder.Next() {
|
|
|
+ t.Error("Expected Next() to return false at end of stream")
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := decoder.Err(); err != nil {
|
|
|
+ t.Errorf("Expected no error, got %v", err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestUnboundedDecoder_MultipleEvents(t *testing.T) {
|
|
|
+ data := "event: first\ndata: data1\n\nevent: second\ndata: data2\n\n"
|
|
|
+ rc := io.NopCloser(strings.NewReader(data))
|
|
|
+ decoder := NewUnboundedDecoder(rc)
|
|
|
+
|
|
|
+ // First event
|
|
|
+ if !decoder.Next() {
|
|
|
+ t.Fatal("Expected Next() to return true for first event")
|
|
|
+ }
|
|
|
+ evt := decoder.Event()
|
|
|
+ if evt.Type != "first" {
|
|
|
+ t.Errorf("Expected event type 'first', got '%s'", evt.Type)
|
|
|
+ }
|
|
|
+ if string(evt.Data) != "data1\n" {
|
|
|
+ t.Errorf("Expected data 'data1\\n', got '%s'", string(evt.Data))
|
|
|
+ }
|
|
|
+
|
|
|
+ // Second event
|
|
|
+ if !decoder.Next() {
|
|
|
+ t.Fatal("Expected Next() to return true for second event")
|
|
|
+ }
|
|
|
+ evt = decoder.Event()
|
|
|
+ if evt.Type != "second" {
|
|
|
+ t.Errorf("Expected event type 'second', got '%s'", evt.Type)
|
|
|
+ }
|
|
|
+ if string(evt.Data) != "data2\n" {
|
|
|
+ t.Errorf("Expected data 'data2\\n', got '%s'", string(evt.Data))
|
|
|
+ }
|
|
|
+
|
|
|
+ // No more events
|
|
|
+ if decoder.Next() {
|
|
|
+ t.Error("Expected Next() to return false at end of stream")
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := decoder.Err(); err != nil {
|
|
|
+ t.Errorf("Expected no error, got %v", err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestUnboundedDecoder_MultilineData(t *testing.T) {
|
|
|
+ data := "event: multiline\ndata: line1\ndata: line2\ndata: line3\n\n"
|
|
|
+ rc := io.NopCloser(strings.NewReader(data))
|
|
|
+ decoder := NewUnboundedDecoder(rc)
|
|
|
+
|
|
|
+ if !decoder.Next() {
|
|
|
+ t.Fatal("Expected Next() to return true")
|
|
|
+ }
|
|
|
+
|
|
|
+ evt := decoder.Event()
|
|
|
+ if evt.Type != "multiline" {
|
|
|
+ t.Errorf("Expected event type 'multiline', got '%s'", evt.Type)
|
|
|
+ }
|
|
|
+
|
|
|
+ expectedData := "line1\nline2\nline3\n"
|
|
|
+ if string(evt.Data) != expectedData {
|
|
|
+ t.Errorf("Expected data '%s', got '%s'", expectedData, string(evt.Data))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestUnboundedDecoder_Comments(t *testing.T) {
|
|
|
+ data := ": this is a comment\nevent: test\n: another comment\ndata: hello\n\n"
|
|
|
+ rc := io.NopCloser(strings.NewReader(data))
|
|
|
+ decoder := NewUnboundedDecoder(rc)
|
|
|
+
|
|
|
+ if !decoder.Next() {
|
|
|
+ t.Fatal("Expected Next() to return true")
|
|
|
+ }
|
|
|
+
|
|
|
+ evt := decoder.Event()
|
|
|
+ if evt.Type != "test" {
|
|
|
+ t.Errorf("Expected event type 'test', got '%s'", evt.Type)
|
|
|
+ }
|
|
|
+ if string(evt.Data) != "hello\n" {
|
|
|
+ t.Errorf("Expected data 'hello\\n', got '%s'", string(evt.Data))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestUnboundedDecoder_NoEventType(t *testing.T) {
|
|
|
+ data := "data: hello\n\n"
|
|
|
+ rc := io.NopCloser(strings.NewReader(data))
|
|
|
+ decoder := NewUnboundedDecoder(rc)
|
|
|
+
|
|
|
+ if !decoder.Next() {
|
|
|
+ t.Fatal("Expected Next() to return true")
|
|
|
+ }
|
|
|
+
|
|
|
+ evt := decoder.Event()
|
|
|
+ if evt.Type != "" {
|
|
|
+ t.Errorf("Expected empty event type, got '%s'", evt.Type)
|
|
|
+ }
|
|
|
+ if string(evt.Data) != "hello\n" {
|
|
|
+ t.Errorf("Expected data 'hello\\n', got '%s'", string(evt.Data))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func BenchmarkUnboundedDecoder_LargeEvent(b *testing.B) {
|
|
|
+ // Create a 10MB event for benchmarking
|
|
|
+ size := 10 * 1024 * 1024
|
|
|
+ largeData := strings.Repeat("x", size)
|
|
|
+
|
|
|
+ var buf bytes.Buffer
|
|
|
+ buf.WriteString("event: bench\n")
|
|
|
+ buf.WriteString("data: ")
|
|
|
+ buf.WriteString(largeData)
|
|
|
+ buf.WriteString("\n\n")
|
|
|
+
|
|
|
+ data := buf.Bytes()
|
|
|
+
|
|
|
+ b.ResetTimer()
|
|
|
+ b.SetBytes(int64(len(data)))
|
|
|
+
|
|
|
+ for i := 0; i < b.N; i++ {
|
|
|
+ rc := io.NopCloser(bytes.NewReader(data))
|
|
|
+ decoder := NewUnboundedDecoder(rc)
|
|
|
+
|
|
|
+ if !decoder.Next() {
|
|
|
+ b.Fatal("Expected Next() to return true")
|
|
|
+ }
|
|
|
+
|
|
|
+ _ = decoder.Event()
|
|
|
+ }
|
|
|
+}
|