1
0
Files
nex/backend/internal/protocol/openai/adapter_test.go
lanyuanxiaoyao f18904af1e feat: 实现分层架构,包含 domain、service、repository 和 pkg 层
- 新增 domain 层:model、provider、route、stats 实体
- 新增 service 层:models、providers、routing、stats 业务逻辑
- 新增 repository 层:models、providers、stats 数据访问
- 新增 pkg 工具包:errors、logger、validator
- 新增中间件:CORS、logging、recovery、request ID
- 新增数据库迁移:初始 schema 和索引
- 新增单元测试和集成测试
- 新增规范文档:config-management、database-migration、error-handling、layered-architecture、middleware-system、request-validation、structured-logging、test-coverage
- 移除 config 子包和 model_router(已迁移至分层架构)
2026-04-16 00:47:20 +08:00

191 lines
4.5 KiB
Go

package openai
import (
"bytes"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAdapter_PrepareRequest(t *testing.T) {
adapter := NewAdapter()
req := &ChatCompletionRequest{
Model: "gpt-4",
Messages: []Message{
{Role: "user", Content: "Hello"},
},
}
httpReq, err := adapter.PrepareRequest(req, "test-api-key", "https://api.openai.com/v1")
require.NoError(t, err)
require.NotNil(t, httpReq)
assert.Equal(t, "POST", httpReq.Method)
assert.Equal(t, "https://api.openai.com/v1/chat/completions", httpReq.URL.String())
assert.Equal(t, "application/json", httpReq.Header.Get("Content-Type"))
assert.Equal(t, "Bearer test-api-key", httpReq.Header.Get("Authorization"))
// 验证请求体
var body ChatCompletionRequest
err = json.NewDecoder(httpReq.Body).Decode(&body)
require.NoError(t, err)
assert.Equal(t, "gpt-4", body.Model)
}
func TestAdapter_ParseResponse(t *testing.T) {
adapter := NewAdapter()
resp := &ChatCompletionResponse{
ID: "chatcmpl-123",
Object: "chat.completion",
Created: 1234567890,
Model: "gpt-4",
Choices: []Choice{
{
Index: 0,
Message: &Message{Role: "assistant", Content: "Hello!"},
},
},
Usage: Usage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15},
}
body, err := json.Marshal(resp)
require.NoError(t, err)
httpResp := &http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewReader(body)),
}
result, err := adapter.ParseResponse(httpResp)
require.NoError(t, err)
assert.Equal(t, "chatcmpl-123", result.ID)
assert.Equal(t, "gpt-4", result.Model)
require.Len(t, result.Choices, 1)
assert.Equal(t, "Hello!", result.Choices[0].Message.Content)
}
func TestAdapter_ParseErrorResponse(t *testing.T) {
adapter := NewAdapter()
errResp := &ErrorResponse{
Error: ErrorDetail{
Message: "Invalid API key",
Type: "invalid_request_error",
Code: "invalid_api_key",
},
}
body, err := json.Marshal(errResp)
require.NoError(t, err)
httpResp := &http.Response{
StatusCode: 401,
Body: io.NopCloser(bytes.NewReader(body)),
}
result, err := adapter.ParseErrorResponse(httpResp)
require.NoError(t, err)
assert.Equal(t, "Invalid API key", result.Error.Message)
assert.Equal(t, "invalid_request_error", result.Error.Type)
}
func TestAdapter_ParseStreamChunk(t *testing.T) {
adapter := NewAdapter()
chunk := &StreamChunk{
ID: "chatcmpl-123",
Object: "chat.completion.chunk",
Created: 1234567890,
Model: "gpt-4",
Choices: []StreamChoice{
{
Index: 0,
Delta: Delta{Content: "Hello"},
},
},
}
data, err := json.Marshal(chunk)
require.NoError(t, err)
result, err := adapter.ParseStreamChunk(data)
require.NoError(t, err)
assert.Equal(t, "chatcmpl-123", result.ID)
require.Len(t, result.Choices, 1)
assert.Equal(t, "Hello", result.Choices[0].Delta.Content)
}
func TestParseToolCallArguments(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
}{
{"有效JSON", `{"key": "value"}`, false},
{"无效JSON", `not json`, true},
{"空JSON", `{}`, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tc := &ToolCall{
Function: FunctionCall{Arguments: tt.input},
}
args, err := tc.ParseToolCallArguments()
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.NotNil(t, args)
}
})
}
}
func TestSerializeToolCallArguments(t *testing.T) {
args := map[string]interface{}{"key": "value"}
result, err := SerializeToolCallArguments(args)
require.NoError(t, err)
assert.JSONEq(t, `{"key": "value"}`, result)
}
func TestValidateRequest(t *testing.T) {
t.Run("有效请求", func(t *testing.T) {
req := &ChatCompletionRequest{
Model: "gpt-4",
Messages: []Message{{Role: "user", Content: "hello"}},
}
errs := ValidateRequest(req)
assert.Nil(t, errs)
})
t.Run("缺少模型", func(t *testing.T) {
req := &ChatCompletionRequest{
Messages: []Message{{Role: "user", Content: "hello"}},
}
errs := ValidateRequest(req)
assert.NotNil(t, errs)
assert.Contains(t, errs["model"], "不能为空")
})
t.Run("缺少消息", func(t *testing.T) {
req := &ChatCompletionRequest{
Model: "gpt-4",
}
errs := ValidateRequest(req)
assert.NotNil(t, errs)
assert.Contains(t, errs["messages"], "不能为空")
})
t.Run("空消息列表", func(t *testing.T) {
req := &ChatCompletionRequest{
Model: "gpt-4",
Messages: []Message{},
}
errs := ValidateRequest(req)
assert.NotNil(t, errs)
})
}