- Added support for reading files from workspace URIs in the delivery process, allowing for more flexible attachment management. - Introduced a new `convertWorkspaceAttachment` function to handle workspace-based file retrieval and integration into messenger attachments. - Updated the `AgentCaller` to include execution mode in the context, improving task execution tracking. - Enhanced the `RunDelivery` method to utilize workspace manifests for delivery input, reducing token usage and improving efficiency. - Implemented locale handling in various request structures to support multi-language capabilities in user interfaces.
234 lines
5.7 KiB
Go
234 lines
5.7 KiB
Go
package shared
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestReadJSONLine_ShortLine(t *testing.T) {
|
|
input := `{"type":"system","text":"hello"}` + "\n"
|
|
r := bufio.NewReaderSize(strings.NewReader(input), 64*1024)
|
|
|
|
line, skipped, err := ReadJSONLine(r)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if skipped {
|
|
t.Fatal("expected skipped=false")
|
|
}
|
|
if string(line) != `{"type":"system","text":"hello"}` {
|
|
t.Fatalf("unexpected line: %q", line)
|
|
}
|
|
}
|
|
|
|
func TestReadJSONLine_EmptyLine(t *testing.T) {
|
|
input := "\n" + `{"type":"ok"}` + "\n"
|
|
r := bufio.NewReaderSize(strings.NewReader(input), 64*1024)
|
|
|
|
line, skipped, err := ReadJSONLine(r)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if skipped {
|
|
t.Fatal("expected skipped=false")
|
|
}
|
|
if len(line) != 0 {
|
|
t.Fatalf("expected empty line, got %d bytes", len(line))
|
|
}
|
|
|
|
line, skipped, err = ReadJSONLine(r)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if skipped {
|
|
t.Fatal("expected skipped=false for second line")
|
|
}
|
|
if string(line) != `{"type":"ok"}` {
|
|
t.Fatalf("unexpected second line: %q", line)
|
|
}
|
|
}
|
|
|
|
func TestReadJSONLine_MediumLine(t *testing.T) {
|
|
payload := strings.Repeat("x", 200*1024) // 200KB — exceeds 64KB buffer, requires multiple ReadLine chunks
|
|
input := payload + "\n"
|
|
r := bufio.NewReaderSize(strings.NewReader(input), 64*1024)
|
|
|
|
line, skipped, err := ReadJSONLine(r)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if skipped {
|
|
t.Fatal("expected skipped=false for 200KB line")
|
|
}
|
|
if len(line) != 200*1024 {
|
|
t.Fatalf("expected 200KB, got %d bytes", len(line))
|
|
}
|
|
}
|
|
|
|
func TestReadJSONLine_OversizedLine(t *testing.T) {
|
|
saved := MaxLineSize
|
|
defer func() {
|
|
// MaxLineSize is a const; we test by embedding a smaller threshold.
|
|
// Since we can't reassign a const, we build a line that exceeds 50MB.
|
|
// Instead, we use a custom helper to keep the test fast.
|
|
_ = saved
|
|
}()
|
|
|
|
// Build a line just over MaxLineSize. To keep allocations reasonable
|
|
// in tests, we use a custom reader that streams repeated bytes.
|
|
totalSize := MaxLineSize + 1024 // slightly over 50MB
|
|
src := &repeatingReader{char: 'A', remaining: totalSize}
|
|
// Append a newline + a short "next" line so we can verify recovery.
|
|
combined := io.MultiReader(src, strings.NewReader("\n{\"type\":\"ok\"}\n"))
|
|
r := bufio.NewReaderSize(combined, 64*1024)
|
|
|
|
line, skipped, err := ReadJSONLine(r)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if !skipped {
|
|
t.Fatal("expected skipped=true for oversized line")
|
|
}
|
|
if line != nil {
|
|
t.Fatalf("expected nil line when skipped, got %d bytes", len(line))
|
|
}
|
|
|
|
// Next line should be readable normally
|
|
line, skipped, err = ReadJSONLine(r)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error reading next line: %v", err)
|
|
}
|
|
if skipped {
|
|
t.Fatal("expected skipped=false for recovery line")
|
|
}
|
|
if string(line) != `{"type":"ok"}` {
|
|
t.Fatalf("unexpected recovery line: %q", line)
|
|
}
|
|
}
|
|
|
|
func TestReadJSONLine_EOF(t *testing.T) {
|
|
r := bufio.NewReaderSize(strings.NewReader(""), 64*1024)
|
|
|
|
_, _, err := ReadJSONLine(r)
|
|
if err != io.EOF {
|
|
t.Fatalf("expected io.EOF, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestReadJSONLine_EOFDuringDrain(t *testing.T) {
|
|
// Oversized line without trailing newline — EOF during drain
|
|
totalSize := MaxLineSize + 1024
|
|
src := &repeatingReader{char: 'B', remaining: totalSize}
|
|
r := bufio.NewReaderSize(src, 64*1024)
|
|
|
|
_, skipped, err := ReadJSONLine(r)
|
|
// During drain, ReadLine will eventually hit EOF
|
|
if err == nil && !skipped {
|
|
t.Fatal("expected either error or skip")
|
|
}
|
|
if err != nil && err != io.EOF {
|
|
t.Fatalf("expected io.EOF during drain, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestReadJSONLine_MultiLineMixed(t *testing.T) {
|
|
// Line 1: normal short
|
|
// Line 2: oversized
|
|
// Line 3: normal short (verify recovery)
|
|
var buf bytes.Buffer
|
|
buf.WriteString(`{"type":"start"}`)
|
|
buf.WriteByte('\n')
|
|
|
|
oversized := strings.Repeat("Z", MaxLineSize+100)
|
|
buf.WriteString(oversized)
|
|
buf.WriteByte('\n')
|
|
|
|
buf.WriteString(`{"type":"end"}`)
|
|
buf.WriteByte('\n')
|
|
|
|
r := bufio.NewReaderSize(&buf, 64*1024)
|
|
|
|
// Line 1
|
|
line, skipped, err := ReadJSONLine(r)
|
|
if err != nil {
|
|
t.Fatalf("line 1 error: %v", err)
|
|
}
|
|
if skipped {
|
|
t.Fatal("line 1 should not be skipped")
|
|
}
|
|
if string(line) != `{"type":"start"}` {
|
|
t.Fatalf("line 1 unexpected: %q", string(line))
|
|
}
|
|
|
|
// Line 2 (oversized)
|
|
line, skipped, err = ReadJSONLine(r)
|
|
if err != nil {
|
|
t.Fatalf("line 2 error: %v", err)
|
|
}
|
|
if !skipped {
|
|
t.Fatal("line 2 should be skipped")
|
|
}
|
|
if line != nil {
|
|
t.Fatal("line 2 should return nil")
|
|
}
|
|
|
|
// Line 3 (recovery)
|
|
line, skipped, err = ReadJSONLine(r)
|
|
if err != nil {
|
|
t.Fatalf("line 3 error: %v", err)
|
|
}
|
|
if skipped {
|
|
t.Fatal("line 3 should not be skipped")
|
|
}
|
|
if string(line) != `{"type":"end"}` {
|
|
t.Fatalf("line 3 unexpected: %q", string(line))
|
|
}
|
|
}
|
|
|
|
func TestReadJSONLine_IOError(t *testing.T) {
|
|
expectedErr := fmt.Errorf("simulated IO failure")
|
|
r := bufio.NewReaderSize(&errorReader{err: expectedErr}, 64*1024)
|
|
|
|
_, _, err := ReadJSONLine(r)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
if err.Error() != expectedErr.Error() {
|
|
t.Fatalf("expected %q, got %q", expectedErr, err)
|
|
}
|
|
}
|
|
|
|
// repeatingReader streams a single character `remaining` times without a newline,
|
|
// then returns io.EOF. Used to test oversized lines without allocating huge buffers.
|
|
type repeatingReader struct {
|
|
char byte
|
|
remaining int
|
|
}
|
|
|
|
func (r *repeatingReader) Read(p []byte) (int, error) {
|
|
if r.remaining <= 0 {
|
|
return 0, io.EOF
|
|
}
|
|
n := len(p)
|
|
if n > r.remaining {
|
|
n = r.remaining
|
|
}
|
|
for i := 0; i < n; i++ {
|
|
p[i] = r.char
|
|
}
|
|
r.remaining -= n
|
|
return n, nil
|
|
}
|
|
|
|
// errorReader always returns the configured error.
|
|
type errorReader struct {
|
|
err error
|
|
}
|
|
|
|
func (r *errorReader) Read(p []byte) (int, error) {
|
|
return 0, r.err
|
|
}
|