package buffer
import (
"fmt"
"io"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestProcessResponseAsRingBufferToEnd(t *testing.T) {
t.Run("normal lines", func(t *testing.T) {
body := "line1\nline2\nline3\n"
resp := &http.Response{
Body: io.NopCloser(strings.NewReader(body)),
}
result, totalLines, respOut, err := ProcessResponseAsRingBufferToEnd(resp, 10)
if respOut != nil && respOut.Body != nil {
defer respOut.Body.Close()
}
require.NoError(t, err)
assert.Equal(t, 3, totalLines)
assert.Equal(t, "line1\nline2\nline3", result)
})
t.Run("ring buffer keeps last N lines", func(t *testing.T) {
body := "line1\nline2\nline3\nline4\nline5\n"
resp := &http.Response{
Body: io.NopCloser(strings.NewReader(body)),
}
result, totalLines, respOut, err := ProcessResponseAsRingBufferToEnd(resp, 3)
if respOut != nil && respOut.Body != nil {
defer respOut.Body.Close()
}
require.NoError(t, err)
assert.Equal(t, 5, totalLines)
assert.Equal(t, "line3\nline4\nline5", result)
})
t.Run("handles very long line exceeding 10MB", func(t *testing.T) {
// Create a line that exceeds maxLineSize (10MB)
longLine := strings.Repeat("x", 11*1024*1024) // 11MB
body := "line1\n" + longLine + "\nline3\n"
resp := &http.Response{
Body: io.NopCloser(strings.NewReader(body)),
}
result, totalLines, respOut, err := ProcessResponseAsRingBufferToEnd(resp, 100)
if respOut != nil && respOut.Body != nil {
defer respOut.Body.Close()
}
require.NoError(t, err)
// Should have processed lines with truncation marker
assert.Greater(t, totalLines, 0)
assert.Contains(t, result, "TRUNCATED")
})
t.Run("handles line at exactly max size", func(t *testing.T) {
// Create a line just under maxLineSize
longLine := strings.Repeat("a", 1024*1024) // 1MB - should work fine
body := "start\n" + longLine + "\nend\n"
resp := &http.Response{
Body: io.NopCloser(strings.NewReader(body)),
}
result, totalLines, respOut, err := ProcessResponseAsRingBufferToEnd(resp, 100)
if respOut != nil && respOut.Body != nil {
defer respOut.Body.Close()
}
require.NoError(t, err)
assert.Equal(t, 3, totalLines)
assert.Contains(t, result, "start")
assert.Contains(t, result, "end")
})
t.Run("ring buffer with long line in middle of many lines", func(t *testing.T) {
// Create many lines with a long line in the middle
// Ring buffer size is 5, so we should only keep the last 5 lines
var sb strings.Builder
for i := 1; i <= 10; i++ {
sb.WriteString(fmt.Sprintf("line%d\n", i))
}
// Insert an 11MB line (exceeds maxLineSize of 10MB)
longLine := strings.Repeat("x", 11*1024*1024)
sb.WriteString(longLine)
sb.WriteString("\n")
for i := 11; i <= 20; i++ {
sb.WriteString(fmt.Sprintf("line%d\n", i))
}
resp := &http.Response{
Body: io.NopCloser(strings.NewReader(sb.String())),
}
result, totalLines, respOut, err := ProcessResponseAsRingBufferToEnd(resp, 5)
if respOut != nil && respOut.Body != nil {
defer respOut.Body.Close()
}
require.NoError(t, err)
// 10 lines before + 1 long line + 10 lines after = 21 total
assert.Equal(t, 21, totalLines)
// Should only have the last 5 lines (line16 through line20)
assert.Contains(t, result, "line16")
assert.Contains(t, result, "line17")
assert.Contains(t, result, "line18")
assert.Contains(t, result, "line19")
assert.Contains(t, result, "line20")
// Should NOT contain earlier lines
assert.NotContains(t, result, "line1\n")
assert.NotContains(t, result, "line10\n")
// The truncated line should not be in the last 5
assert.NotContains(t, result, "TRUNCATED")
})
t.Run("empty response body", func(t *testing.T) {
resp := &http.Response{
Body: io.NopCloser(strings.NewReader("")),
}
result, totalLines, respOut, err := ProcessResponseAsRingBufferToEnd(resp, 10)
if respOut != nil && respOut.Body != nil {
defer respOut.Body.Close()
}
require.NoError(t, err)
assert.Equal(t, 0, totalLines)
assert.Equal(t, "", result)
})
t.Run("line at exactly maxLineSize boundary", func(t *testing.T) {
// Create a line at exactly maxLineSize (10MB) - should be truncated
exactLine := strings.Repeat("z", 10*1024*1024)
body := "before\n" + exactLine + "\nafter\n"
resp := &http.Response{
Body: io.NopCloser(strings.NewReader(body)),
}
result, totalLines, respOut, err := ProcessResponseAsRingBufferToEnd(resp, 10)
if respOut != nil && respOut.Body != nil {
defer respOut.Body.Close()
}
require.NoError(t, err)
assert.Equal(t, 3, totalLines)
assert.Contains(t, result, "before")
assert.Contains(t, result, "TRUNCATED")
assert.Contains(t, result, "after")
})
t.Run("ring buffer keeps truncated line when in last N", func(t *testing.T) {
// Long line followed by only 2 more lines, with ring buffer size 5
longLine := strings.Repeat("y", 11*1024*1024)
body := "line1\nline2\nline3\n" + longLine + "\nlineA\nlineB\n"
resp := &http.Response{
Body: io.NopCloser(strings.NewReader(body)),
}
result, totalLines, respOut, err := ProcessResponseAsRingBufferToEnd(resp, 5)
if respOut != nil && respOut.Body != nil {
defer respOut.Body.Close()
}
require.NoError(t, err)
assert.Equal(t, 6, totalLines)
// Last 5: line2, line3, truncated, lineA, lineB
assert.Contains(t, result, "line2")
assert.Contains(t, result, "line3")
assert.Contains(t, result, "TRUNCATED")
assert.Contains(t, result, "lineA")
assert.Contains(t, result, "lineB")
// line1 should be rotated out
assert.NotContains(t, result, "line1")
})
}