Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
//go:build unit
|
||||
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zaptest/observer"
|
||||
)
|
||||
|
||||
func newObservedLogger(t *testing.T) (*zap.Logger, *observer.ObservedLogs) {
|
||||
t.Helper()
|
||||
core, logs := observer.New(zap.WarnLevel)
|
||||
return zap.New(core), logs
|
||||
}
|
||||
|
||||
func loggedFields(t *testing.T, logs *observer.ObservedLogs) map[string]any {
|
||||
t.Helper()
|
||||
entries := logs.All()
|
||||
require.Len(t, entries, 1)
|
||||
fields := map[string]any{}
|
||||
for _, f := range entries[0].Context {
|
||||
switch f.Key {
|
||||
case "body_len":
|
||||
fields[f.Key] = int(f.Integer)
|
||||
case "error":
|
||||
fields[f.Key] = f.Interface.(error).Error()
|
||||
default:
|
||||
fields[f.Key] = f.String
|
||||
}
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
func TestLogRequestBodyParseFailure_DerivesErrorWhenNil(t *testing.T) {
|
||||
log, logs := newObservedLogger(t)
|
||||
body := []byte(`{"model": bad}`)
|
||||
|
||||
logRequestBodyParseFailure(log, body, nil)
|
||||
|
||||
fields := loggedFields(t, logs)
|
||||
require.Equal(t, len(body), fields["body_len"])
|
||||
require.Contains(t, fields["error"], "invalid json")
|
||||
require.Contains(t, fields["error"], "offset=11")
|
||||
}
|
||||
|
||||
func TestLogRequestBodyParseFailure_ShortBodyHasNoTail(t *testing.T) {
|
||||
log, logs := newObservedLogger(t)
|
||||
body := []byte(`{"broken":`)
|
||||
|
||||
logRequestBodyParseFailure(log, body, nil)
|
||||
|
||||
fields := loggedFields(t, logs)
|
||||
require.Contains(t, fields, "body_head")
|
||||
require.NotContains(t, fields, "body_tail")
|
||||
require.Contains(t, fields["body_head"].(string), `{\"broken\":`)
|
||||
}
|
||||
|
||||
func TestLogRequestBodyParseFailure_LargeBodyBoundedSnippets(t *testing.T) {
|
||||
log, logs := newObservedLogger(t)
|
||||
// ~1MB body: head must show the structural prefix, tail the trailing bytes,
|
||||
// and neither snippet may exceed the configured bound (plus quoting overhead).
|
||||
body := []byte(`{"model":"claude-sonnet-4-6","big":"` + strings.Repeat("A", 1<<20) + `"`)
|
||||
|
||||
logRequestBodyParseFailure(log, body, nil)
|
||||
|
||||
fields := loggedFields(t, logs)
|
||||
require.Equal(t, len(body), fields["body_len"])
|
||||
head := fields["body_head"].(string)
|
||||
tail := fields["body_tail"].(string)
|
||||
require.Contains(t, head, "claude-sonnet-4-6")
|
||||
require.Contains(t, tail, "AAA")
|
||||
require.NotContains(t, tail, "claude-sonnet-4-6")
|
||||
// strconv.Quote adds surrounding quotes and escapes; 4x is a generous cap.
|
||||
require.LessOrEqual(t, len(head), parseFailureSnippetLen*4)
|
||||
require.LessOrEqual(t, len(tail), parseFailureSnippetLen*4)
|
||||
}
|
||||
|
||||
func TestLogRequestBodyParseFailure_EscapesControlCharacters(t *testing.T) {
|
||||
log, logs := newObservedLogger(t)
|
||||
body := []byte("{\"model\":\x01\n\"x\"}")
|
||||
|
||||
logRequestBodyParseFailure(log, body, nil)
|
||||
|
||||
fields := loggedFields(t, logs)
|
||||
head := fields["body_head"].(string)
|
||||
require.NotContains(t, head, "\n")
|
||||
require.NotContains(t, head, "\x01")
|
||||
require.Contains(t, head, `\n`)
|
||||
require.Contains(t, head, `\x01`)
|
||||
}
|
||||
|
||||
func TestLogRequestBodyParseFailure_NilLoggerNoPanic(t *testing.T) {
|
||||
require.NotPanics(t, func() {
|
||||
logRequestBodyParseFailure(nil, []byte(`{`), nil)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user