Skip to content

Commit 76ae485

Browse files
committed
test: enhance error accumulator and form builder tests, add marshaller tests
1 parent 4d2e7ab commit 76ae485

File tree

5 files changed

+223
-27
lines changed

5 files changed

+223
-27
lines changed

internal/error_accumulator_test.go

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,28 @@
11
package openai_test
22

33
import (
4-
"bytes"
5-
"errors"
64
"testing"
75

8-
utils "github.com/sashabaranov/go-openai/internal"
9-
"github.com/sashabaranov/go-openai/internal/test"
6+
openai "github.com/sashabaranov/go-openai/internal"
7+
"github.com/sashabaranov/go-openai/internal/test/checks"
108
)
119

12-
func TestErrorAccumulatorBytes(t *testing.T) {
13-
accumulator := &utils.DefaultErrorAccumulator{
14-
Buffer: &bytes.Buffer{},
15-
}
16-
17-
errBytes := accumulator.Bytes()
18-
if len(errBytes) != 0 {
19-
t.Fatalf("Did not return nil with empty bytes: %s", string(errBytes))
20-
}
10+
func TestDefaultErrorAccumulator_WriteMultiple(t *testing.T) {
11+
ea := openai.NewErrorAccumulator().(*openai.DefaultErrorAccumulator)
2112

22-
err := accumulator.Write([]byte("{}"))
23-
if err != nil {
24-
t.Fatalf("%+v", err)
25-
}
13+
checks.NoError(t, ea.Write([]byte("{\"error\": \"test1\"}")))
14+
checks.NoError(t, ea.Write([]byte("{\"error\": \"test2\"}")))
2615

27-
errBytes = accumulator.Bytes()
28-
if len(errBytes) == 0 {
29-
t.Fatalf("Did not return error bytes when has error: %s", string(errBytes))
16+
expected := "{\"error\": \"test1\"}{\"error\": \"test2\"}"
17+
if string(ea.Bytes()) != expected {
18+
t.Fatalf("Expected %q, got %q", expected, ea.Bytes())
3019
}
3120
}
3221

33-
func TestErrorByteWriteErrors(t *testing.T) {
34-
accumulator := &utils.DefaultErrorAccumulator{
35-
Buffer: &test.FailingErrorBuffer{},
36-
}
37-
err := accumulator.Write([]byte("{"))
38-
if !errors.Is(err, test.ErrTestErrorAccumulatorWriteFailed) {
39-
t.Fatalf("Did not return error when write failed: %v", err)
22+
func TestDefaultErrorAccumulator_EmptyBuffer(t *testing.T) {
23+
ea := openai.NewErrorAccumulator().(*openai.DefaultErrorAccumulator)
24+
25+
if len(ea.Bytes()) != 0 {
26+
t.Fatal("Buffer should be empty initially")
4027
}
4128
}

internal/form_builder.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ func (fb *DefaultFormBuilder) createFormFile(fieldname string, r io.Reader, file
5353
}
5454

5555
func (fb *DefaultFormBuilder) WriteField(fieldname, value string) error {
56+
if fieldname == "" {
57+
return fmt.Errorf("fieldname cannot be empty")
58+
}
5659
return fb.writer.WriteField(fieldname, value)
5760
}
5861

internal/form_builder_test.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package openai //nolint:testpackage // testing private field
22

33
import (
4+
"strings"
5+
46
"github.com/sashabaranov/go-openai/internal/test/checks"
57

68
"bytes"
@@ -9,6 +11,47 @@ import (
911
"testing"
1012
)
1113

14+
type mockFormBuilder struct {
15+
mockCreateFormFile func(string, *os.File) error
16+
mockWriteField func(string, string) error
17+
mockClose func() error
18+
}
19+
20+
func (m *mockFormBuilder) CreateFormFile(fieldname string, file *os.File) error {
21+
return m.mockCreateFormFile(fieldname, file)
22+
}
23+
24+
func (m *mockFormBuilder) WriteField(fieldname, value string) error {
25+
return m.mockWriteField(fieldname, value)
26+
}
27+
28+
func (m *mockFormBuilder) Close() error {
29+
return m.mockClose()
30+
}
31+
32+
func (m *mockFormBuilder) FormDataContentType() string {
33+
return ""
34+
}
35+
36+
func TestCloseMethod(t *testing.T) {
37+
t.Run("NormalClose", func(t *testing.T) {
38+
body := &bytes.Buffer{}
39+
builder := NewFormBuilder(body)
40+
checks.NoError(t, builder.Close(), "正常关闭应成功")
41+
})
42+
43+
t.Run("ErrorPropagation", func(t *testing.T) {
44+
errorMock := errors.New("mock close error")
45+
mockBuilder := &mockFormBuilder{
46+
mockClose: func() error {
47+
return errorMock
48+
},
49+
}
50+
err := mockBuilder.Close()
51+
checks.ErrorIs(t, err, errorMock, "应传递关闭错误")
52+
})
53+
}
54+
1255
type failingWriter struct {
1356
}
1457

@@ -43,3 +86,95 @@ func TestFormBuilderWithClosedFile(t *testing.T) {
4386
checks.HasError(t, err, "formbuilder should return error if file is closed")
4487
checks.ErrorIs(t, err, os.ErrClosed, "formbuilder should return error if file is closed")
4588
}
89+
90+
func TestMultiPartFormUploads(t *testing.T) {
91+
body := &bytes.Buffer{}
92+
builder := NewFormBuilder(body)
93+
94+
t.Run("MultipleFiles", func(t *testing.T) {
95+
file1, _ := os.CreateTemp(t.TempDir(), "*.png")
96+
file2, _ := os.CreateTemp(t.TempDir(), "*.jpg")
97+
defer file1.Close()
98+
defer file2.Close()
99+
100+
checks.NoError(t, builder.CreateFormFile("image1", file1), "PNG file upload failed")
101+
checks.NoError(t, builder.CreateFormFile("image2", file2), "JPG file upload failed")
102+
checks.NoError(t, builder.WriteField("description", "test images"), "Field write failed")
103+
})
104+
105+
t.Run("LargeFileConcurrent", func(t *testing.T) {
106+
bigFile, _ := os.CreateTemp(t.TempDir(), "*.bin")
107+
defer bigFile.Close()
108+
bigFile.Write(make([]byte, 1024*1024*5)) // 5MB test file
109+
110+
checks.NoError(t, builder.CreateFormFile("bigfile", bigFile), "Large file upload failed")
111+
checks.NoError(t, builder.WriteField("note", "large file test"), "Field write failed")
112+
})
113+
114+
t.Run("MixedContentTypes", func(t *testing.T) {
115+
csvFile, _ := os.CreateTemp(t.TempDir(), "*.csv")
116+
textFile, _ := os.CreateTemp(t.TempDir(), "*.txt")
117+
defer csvFile.Close()
118+
defer textFile.Close()
119+
120+
checks.NoError(t, builder.CreateFormFile("data", csvFile), "CSV file upload failed")
121+
checks.NoError(t, builder.CreateFormFile("text", textFile), "Text file upload failed")
122+
checks.NoError(t, builder.WriteField("format", "mixed"), "Field write failed")
123+
})
124+
}
125+
126+
func TestFormDataContentType(t *testing.T) {
127+
body := &bytes.Buffer{}
128+
builder := NewFormBuilder(body)
129+
contentType := builder.FormDataContentType()
130+
if !strings.HasPrefix(contentType, "multipart/form-data") {
131+
t.Fatalf("Content-Type格式错误,期望multipart/form-data开头,实际得到:%s", contentType)
132+
}
133+
}
134+
135+
func TestCreateFormFileReader(t *testing.T) {
136+
body := &bytes.Buffer{}
137+
builder := NewFormBuilder(body)
138+
139+
t.Run("SpecialCharacters", func(t *testing.T) {
140+
checks.NoError(t, builder.CreateFormFileReader("field", strings.NewReader("content"), "测 试@file.txt"), "特殊字符文件名应处理成功")
141+
})
142+
143+
t.Run("InvalidReader", func(t *testing.T) {
144+
err := builder.CreateFormFileReader("field", &failingReader{}, "valid.txt")
145+
checks.HasError(t, err, "无效reader应返回错误")
146+
})
147+
}
148+
149+
type failingReader struct{}
150+
151+
func (r *failingReader) Read(p []byte) (int, error) {
152+
return 0, errors.New("mock read error")
153+
}
154+
155+
func TestWriteFieldEdgeCases(t *testing.T) {
156+
mockErr := errors.New("mock write error")
157+
t.Run("EmptyFieldName", func(t *testing.T) {
158+
body := &bytes.Buffer{}
159+
builder := NewFormBuilder(body)
160+
err := builder.WriteField("", "valid-value")
161+
checks.HasError(t, err, "should return error for empty field name")
162+
})
163+
164+
t.Run("EmptyValue", func(t *testing.T) {
165+
body := &bytes.Buffer{}
166+
builder := NewFormBuilder(body)
167+
err := builder.WriteField("valid-field", "")
168+
checks.NoError(t, err, "should allow empty value")
169+
})
170+
171+
t.Run("MockWriterFailure", func(t *testing.T) {
172+
mockBuilder := &mockFormBuilder{
173+
mockWriteField: func(_, _ string) error {
174+
return mockErr
175+
},
176+
}
177+
err := mockBuilder.WriteField("field", "value")
178+
checks.ErrorIs(t, err, mockErr, "should propagate write error")
179+
})
180+
}

internal/marshaller_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package openai_test
2+
3+
import (
4+
"testing"
5+
6+
openai "github.com/sashabaranov/go-openai/internal"
7+
"github.com/sashabaranov/go-openai/internal/test/checks"
8+
)
9+
10+
func TestJSONMarshaller_Normal(t *testing.T) {
11+
jm := &openai.JSONMarshaller{}
12+
data := map[string]string{"key": "value"}
13+
14+
b, err := jm.Marshal(data)
15+
checks.NoError(t, err)
16+
if len(b) == 0 {
17+
t.Fatal("should return non-empty bytes")
18+
}
19+
}
20+
21+
func TestJSONMarshaller_InvalidInput(t *testing.T) {
22+
jm := &openai.JSONMarshaller{}
23+
_, err := jm.Marshal(make(chan int))
24+
checks.HasError(t, err, "should return error for unsupported type")
25+
}
26+
27+
func TestJSONMarshaller_EmptyValue(t *testing.T) {
28+
jm := &openai.JSONMarshaller{}
29+
b, err := jm.Marshal(nil)
30+
checks.NoError(t, err)
31+
if string(b) != "null" {
32+
t.Fatalf("unexpected marshaled value: %s", string(b))
33+
}
34+
}

internal/unmarshaler_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package openai_test
2+
3+
import (
4+
"testing"
5+
6+
openai "github.com/sashabaranov/go-openai/internal"
7+
"github.com/sashabaranov/go-openai/internal/test/checks"
8+
)
9+
10+
func TestJSONUnmarshaler_Normal(t *testing.T) {
11+
jm := &openai.JSONUnmarshaler{}
12+
data := []byte(`{"key":"value"}`)
13+
var v map[string]string
14+
15+
err := jm.Unmarshal(data, &v)
16+
checks.NoError(t, err)
17+
if v["key"] != "value" {
18+
t.Fatal("unmarshal result mismatch")
19+
}
20+
}
21+
22+
func TestJSONUnmarshaler_InvalidJSON(t *testing.T) {
23+
jm := &openai.JSONUnmarshaler{}
24+
data := []byte(`{invalid}`)
25+
var v map[string]interface{}
26+
27+
err := jm.Unmarshal(data, &v)
28+
checks.HasError(t, err, "should return error for invalid JSON")
29+
}
30+
31+
func TestJSONUnmarshaler_EmptyInput(t *testing.T) {
32+
jm := &openai.JSONUnmarshaler{}
33+
var v interface{}
34+
35+
err := jm.Unmarshal(nil, &v)
36+
checks.HasError(t, err, "should return error for nil input")
37+
}

0 commit comments

Comments
 (0)