Files
NianAIGC/backend/internal/providers/minimax_diagnostics_test.go
T
2026-09-11 15:33:18 +08:00

99 lines
4.7 KiB
Go

package providers
import (
"bytes"
"context"
"encoding/json"
"errors"
"io"
"log"
"net/http"
"reflect"
"strings"
"testing"
"unicode/utf8"
)
func TestMinimaxPreservesRejectionReasonForPersistedTextVideoRequest(t *testing.T) {
var output bytes.Buffer
previousOutput := log.Writer()
log.SetOutput(&output)
t.Cleanup(func() { log.SetOutput(previousOutput) })
var request Request
if err := json.Unmarshal([]byte(`{"model":"MiniMax-H3","prompt":"一段盛开的烟花","settings":{"ratio":"1:1","duration":4,"resolution":"768P"},"capability":"video.generate"}`), &request); err != nil {
t.Fatal(err)
}
client := roundTripFunc(func(r *http.Request) (*http.Response, error) {
if r.Method != http.MethodPost || r.URL.Path != "/v2/video_generation" {
t.Fatalf("unexpected endpoint: %s %s", r.Method, r.URL.Path)
}
var payload map[string]any
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
t.Fatal(err)
}
want := map[string]any{
"model": "MiniMax-H3", "resolution": "768P", "duration": float64(4), "ratio": "1:1", "aigc_watermark": false,
"content": []any{map[string]any{"type": "text", "text": "一段盛开的烟花"}},
}
if !reflect.DeepEqual(payload, want) {
t.Fatalf("wire payload=%#v want=%#v", payload, want)
}
// A synthetic validation response: this does not assert that 4 seconds
// is unsupported by the live API, only that its explanation survives.
return &http.Response{StatusCode: 400, Header: http.Header{}, Body: io.NopCloser(strings.NewReader(`{"error":{"type":"bad_request_error","message":"invalid params, duration is not supported (2013)"},"request_id":"request-minimax-1"}`))}, nil
})
adapter := NewMinimax(Config{BaseURL: "https://api.minimax.cn", APIKey: "private-api-key"}, client)
_, err := adapter.Submit(context.Background(), request)
var failure *ProviderError
if !errors.As(err, &failure) || failure.Code != "2013" || failure.RequestID != "request-minimax-1" || failure.Detail != "invalid params, duration is not supported (2013)" {
t.Fatalf("failure=%#v err=%v", failure, err)
}
if strings.Contains(err.Error(), "invalid params") {
t.Fatalf("generic error exposed diagnostic: %v", err)
}
for _, expected := range []string{`detail="invalid params, duration is not supported (2013)"`, `requestId="request-minimax-1"`, `model="MiniMax-H3"`, `resolution="768P"`, "duration=4", `ratio="1:1"`, "promptChars=7", "imageItems=0", "endpointClass=official-cn", "basePathClass=root"} {
if !strings.Contains(output.String(), expected) {
t.Errorf("missing %q from %s", expected, output.String())
}
}
for _, private := range []string{"一段盛开的烟花", "private-api-key"} {
if strings.Contains(output.String(), private) {
t.Errorf("log leaked %q", private)
}
}
}
func TestMinimaxFailureDetailRedactsEchoedInputsCredentialsAndURLs(t *testing.T) {
const prompt = "private prompt\nsecond line"
const imageURL = "https://private.test/a.png?Signature=private-signature"
request, _ := json.Marshal(map[string]any{"content": []any{
map[string]any{"type": "text", "text": prompt},
map[string]any{"type": "image_url", "image_url": map[string]any{"url": imageURL}},
}})
encodedPrompt, _ := json.Marshal(prompt)
response, _ := json.Marshal(map[string]any{"error": map[string]any{"message": "invalid params, 'model' \"MiniMax-H3\" not supported; " + prompt + " " + string(encodedPrompt) + " " + imageURL + " Bearer private-api-key api_key=another-key Authorization: Bearer unrelated-token 'private-user-name' https://other-private.test/a.png?token=other-secret (2013)"}})
detail := minimaxSafeFailureDetail(response, request, "private-api-key")
for _, expected := range []string{"invalid params", "model", "MiniMax-H3", "not supported", "(2013)", "[redacted]"} {
if !strings.Contains(detail, expected) {
t.Fatalf("missing %q in %q", expected, detail)
}
}
for _, private := range []string{"private", "second line", "another-key", "unrelated-token", "other-secret", "Signature=", "https://", "Bearer", "\n"} {
if strings.Contains(detail, private) {
t.Fatalf("detail leaked %q: %s", private, detail)
}
}
}
func TestMinimaxFailureDetailIsBoundedAndIgnoresOtherResponseFields(t *testing.T) {
response, _ := json.Marshal(map[string]any{"error": map[string]any{"message": strings.Repeat("参数无效 ", 200), "request": "private request body"}, "secret": "private secret"})
detail := minimaxSafeFailureDetail(response, nil, "")
if utf8.RuneCountInString(detail) > 512+len("...[truncated]") || !strings.HasSuffix(detail, "...[truncated]") || strings.Contains(detail, "private") {
t.Fatalf("unsafe/unbounded detail: %s", detail)
}
if detail := minimaxSafeFailureDetail([]byte("not json"), nil, ""); detail != "" {
t.Fatalf("invalid JSON detail=%q", detail)
}
}