Files
sub2api/backend/internal/service/openai_response_terminal_usage_compat_test.go
T

116 lines
4.3 KiB
Go

//go:build unit
package service
import (
"strings"
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/pkg/apicompat"
"github.com/stretchr/testify/require"
"github.com/tidwall/gjson"
)
func TestEffectiveOpenAISSEEventTypePrefersPayload(t *testing.T) {
t.Parallel()
require.Equal(t, "response.failed", effectiveOpenAISSEEventType([]byte(`{"type":"response.failed"}`), "error"))
require.Equal(t, "error", effectiveOpenAISSEEventType([]byte(`{"error":{"message":"failed"}}`), " error "))
require.JSONEq(t, `{"type":"error","error":{"message":"failed"}}`, openAICompatPayloadWithEventType(`{"type":"","error":{"message":"failed"}}`, "error"))
}
func TestExtractOpenAISSETerminalEventUsesEventField(t *testing.T) {
t.Parallel()
body := "event: error\n" +
"data: {\"error\":{\"message\":\"provider failed\"}}\n\n" +
"data: [DONE]\n\n"
eventType, payload, ok := extractOpenAISSETerminalEvent(body)
require.True(t, ok)
require.Equal(t, "error", eventType)
require.Equal(t, "provider failed", extractOpenAISSEErrorMessage(payload))
}
func TestExtractOpenAISSETerminalEventUsesFinalAuthoritativeTerminal(t *testing.T) {
t.Parallel()
body := "data: {\"type\":\"error\",\"error\":{\"message\":\"recovering\"}}\n\n" +
"data: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_1\",\"status\":\"completed\"}}\n\n"
eventType, payload, ok := extractOpenAISSETerminalEvent(body)
require.True(t, ok)
require.Equal(t, "response.completed", eventType)
require.Equal(t, "resp_1", gjson.GetBytes(payload, "response.id").String())
}
func TestParseSSEUsageEffectiveTerminalRules(t *testing.T) {
t.Parallel()
svc := &OpenAIGatewayService{}
usage := &OpenAIUsage{}
svc.parseSSEUsageBytesWithType([]byte(`{"usage":{"input_tokens":17,"output_tokens":5,"input_tokens_details":{"cached_tokens":3}}}`), "response.in_progress", usage)
svc.parseSSEUsageBytesWithType([]byte(`{"response":{"id":"resp_1"}}`), "response.completed", usage)
require.Equal(t, OpenAIUsage{InputTokens: 17, OutputTokens: 5, CacheReadInputTokens: 3}, *usage)
svc.parseSSEUsageBytesWithType([]byte(`{"response":{"usage":{"input_tokens":0,"output_tokens":0,"input_tokens_details":{"cached_tokens":0}}}}`), "response.completed", usage)
require.Equal(t, OpenAIUsage{InputTokens: 17, OutputTokens: 5, CacheReadInputTokens: 3}, *usage)
svc.parseSSEUsageBytesWithType([]byte(`{"response":{"usage":{"input_tokens":2,"output_tokens":0,"input_tokens_details":{"cached_tokens":0}}}}`), "response.completed", usage)
require.Equal(t, OpenAIUsage{InputTokens: 2}, *usage)
}
func BenchmarkParseSSEUsageNoUsageDelta(b *testing.B) {
svc := &OpenAIGatewayService{}
usage := &OpenAIUsage{}
payload := []byte(`{"type":"response.output_text.delta","delta":"hello"}`)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
svc.parseSSEUsageBytesWithType(payload, "response.output_text.delta", usage)
}
}
func TestOpenAICompatTerminalResponseSynthesizesBareError(t *testing.T) {
t.Parallel()
payload := []byte(`{"type":"error","code":"upstream_error","error":{"message":"provider failed"}}`)
event := &apicompat.ResponsesStreamEvent{Type: "error", Code: "upstream_error"}
response := openAICompatTerminalResponse(event, payload)
require.NotNil(t, response)
require.Equal(t, "failed", response.Status)
require.Equal(t, "upstream_error", response.Error.Code)
require.Equal(t, "provider failed", response.Error.Message)
}
func TestForEachOpenAISSEFrameDataTypeOverridesEventField(t *testing.T) {
t.Parallel()
var types []string
forEachOpenAISSEFrame(strings.Join([]string{
"event: response.in_progress",
`data: {"type":"response.completed","response":{"id":"resp_1"}}`,
"",
}, "\n"), func(eventType string, _ []byte) {
types = append(types, eventType)
})
require.Equal(t, []string{"response.completed"}, types)
}
func TestSampleOpenAIMissingUsageLogRateLimitsAndCounts(t *testing.T) {
var sampler openAIMissingUsageLogSampler
now := time.Unix(100, 0)
logNow, total, suppressed := sampler.sample(now)
require.True(t, logNow)
require.Equal(t, uint64(1), total)
require.Zero(t, suppressed)
logNow, total, _ = sampler.sample(now.Add(time.Second))
require.False(t, logNow)
require.Equal(t, uint64(2), total)
logNow, total, suppressed = sampler.sample(now.Add(openAIMissingUsageLogInterval))
require.True(t, logNow)
require.Equal(t, uint64(3), total)
require.Equal(t, uint64(1), suppressed)
}