Improve unit test coverage (#984)

* add tests for config

* add audio tests

* lint

* lint

* lint
This commit is contained in:
Alex Baranov
2025-05-03 22:25:14 +01:00
committed by GitHub
parent d65f0cb54e
commit 5ea214a188
2 changed files with 153 additions and 0 deletions

View File

@@ -2,12 +2,16 @@ package openai //nolint:testpackage // testing private field
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"testing"
utils "github.com/sashabaranov/go-openai/internal"
"github.com/sashabaranov/go-openai/internal/test"
"github.com/sashabaranov/go-openai/internal/test/checks"
)
@@ -107,3 +111,131 @@ func TestCreateFileField(t *testing.T) {
checks.HasError(t, err, "createFileField using file should return error when open file fails")
})
}
// failingFormBuilder always returns an error when creating form files.
type failingFormBuilder struct{ err error }
func (f *failingFormBuilder) CreateFormFile(_ string, _ *os.File) error {
return f.err
}
func (f *failingFormBuilder) CreateFormFileReader(_ string, _ io.Reader, _ string) error {
return f.err
}
func (f *failingFormBuilder) WriteField(_, _ string) error {
return nil
}
func (f *failingFormBuilder) Close() error {
return nil
}
func (f *failingFormBuilder) FormDataContentType() string {
return "multipart/form-data"
}
// failingAudioRequestBuilder simulates an error during HTTP request construction.
type failingAudioRequestBuilder struct{ err error }
func (f *failingAudioRequestBuilder) Build(
_ context.Context,
_, _ string,
_ any,
_ http.Header,
) (*http.Request, error) {
return nil, f.err
}
// errorHTTPClient always returns an error when making HTTP calls.
type errorHTTPClient struct{ err error }
func (e *errorHTTPClient) Do(_ *http.Request) (*http.Response, error) {
return nil, e.err
}
func TestCallAudioAPIMultipartFormError(t *testing.T) {
client := NewClient("test-token")
errForm := errors.New("mock create form file failure")
// Override form builder to force an error during multipart form creation.
client.createFormBuilder = func(_ io.Writer) utils.FormBuilder {
return &failingFormBuilder{err: errForm}
}
// Provide a reader so createFileField uses the reader path (no file open).
req := AudioRequest{FilePath: "fake.mp3", Reader: bytes.NewBuffer([]byte("dummy")), Model: Whisper1}
_, err := client.callAudioAPI(context.Background(), req, "transcriptions")
if err == nil {
t.Fatal("expected error but got none")
}
if !errors.Is(err, errForm) {
t.Errorf("expected error %v, got %v", errForm, err)
}
}
func TestCallAudioAPINewRequestError(t *testing.T) {
client := NewClient("test-token")
// Create a real temp file so multipart form succeeds.
tmp := t.TempDir()
path := filepath.Join(tmp, "file.mp3")
if err := os.WriteFile(path, []byte("content"), 0644); err != nil {
t.Fatalf("failed to write temp file: %v", err)
}
errBuild := errors.New("mock build failure")
client.requestBuilder = &failingAudioRequestBuilder{err: errBuild}
req := AudioRequest{FilePath: path, Model: Whisper1}
_, err := client.callAudioAPI(context.Background(), req, "translations")
if err == nil {
t.Fatal("expected error but got none")
}
if !errors.Is(err, errBuild) {
t.Errorf("expected error %v, got %v", errBuild, err)
}
}
func TestCallAudioAPISendRequestErrorJSON(t *testing.T) {
client := NewClient("test-token")
// Create a real temp file so multipart form succeeds.
tmp := t.TempDir()
path := filepath.Join(tmp, "file.mp3")
if err := os.WriteFile(path, []byte("content"), 0644); err != nil {
t.Fatalf("failed to write temp file: %v", err)
}
errHTTP := errors.New("mock HTTPClient failure")
// Override HTTP client to simulate a network error.
client.config.HTTPClient = &errorHTTPClient{err: errHTTP}
req := AudioRequest{FilePath: path, Model: Whisper1}
_, err := client.callAudioAPI(context.Background(), req, "transcriptions")
if err == nil {
t.Fatal("expected error but got none")
}
if !errors.Is(err, errHTTP) {
t.Errorf("expected error %v, got %v", errHTTP, err)
}
}
func TestCallAudioAPISendRequestErrorText(t *testing.T) {
client := NewClient("test-token")
tmp := t.TempDir()
path := filepath.Join(tmp, "file.mp3")
if err := os.WriteFile(path, []byte("content"), 0644); err != nil {
t.Fatalf("failed to write temp file: %v", err)
}
errHTTP := errors.New("mock HTTPClient failure")
client.config.HTTPClient = &errorHTTPClient{err: errHTTP}
// Use a non-JSON response format to exercise the text path.
req := AudioRequest{FilePath: path, Model: Whisper1, Format: AudioResponseFormatText}
_, err := client.callAudioAPI(context.Background(), req, "translations")
if err == nil {
t.Fatal("expected error but got none")
}
if !errors.Is(err, errHTTP) {
t.Errorf("expected error %v, got %v", errHTTP, err)
}
}

View File

@@ -100,3 +100,24 @@ func TestDefaultAnthropicConfigWithEmptyValues(t *testing.T) {
t.Errorf("Expected BaseURL to be %v, got %v", expectedBaseURL, config.BaseURL)
}
}
func TestClientConfigString(t *testing.T) {
// String() should always return the constant value
conf := openai.DefaultConfig("dummy-token")
expected := "<OpenAI API ClientConfig>"
got := conf.String()
if got != expected {
t.Errorf("ClientConfig.String() = %q; want %q", got, expected)
}
}
func TestGetAzureDeploymentByModel_NoMapper(t *testing.T) {
// On a zero-value or DefaultConfig, AzureModelMapperFunc is nil,
// so GetAzureDeploymentByModel should just return the input model.
conf := openai.DefaultConfig("dummy-token")
model := "some-model"
got := conf.GetAzureDeploymentByModel(model)
if got != model {
t.Errorf("GetAzureDeploymentByModel(%q) = %q; want %q", model, got, model)
}
}