Files
ds2api/internal/testsuite/edge_cases_error_contract.go
2026-04-26 00:10:59 +08:00

177 lines
5.0 KiB
Go

package testsuite
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
)
func (r *Runner) caseInvalidModel(ctx context.Context, cc *caseContext) error {
resp, err := cc.requestOnce(ctx, requestSpec{
Method: http.MethodPost,
Path: "/v1/chat/completions",
Headers: map[string]string{
"Authorization": "Bearer " + r.apiKey,
},
Body: map[string]any{
"model": "deepseek-not-exists",
"messages": []map[string]any{
{"role": "user", "content": "hi"},
},
"stream": false,
},
Retryable: false,
}, 1)
if err != nil {
return err
}
cc.assert("status_503", resp.StatusCode == http.StatusServiceUnavailable, fmt.Sprintf("status=%d", resp.StatusCode))
var m map[string]any
_ = json.Unmarshal(resp.Body, &m)
e, _ := m["error"].(map[string]any)
cc.assert("error_type_service_unavailable", asString(e["type"]) == "service_unavailable_error", fmt.Sprintf("body=%s", string(resp.Body)))
return nil
}
func (r *Runner) caseMissingMessages(ctx context.Context, cc *caseContext) error {
resp, err := cc.request(ctx, requestSpec{
Method: http.MethodPost,
Path: "/v1/chat/completions",
Headers: map[string]string{
"Authorization": "Bearer " + r.apiKey,
},
Body: map[string]any{
"model": "deepseek-v4-flash",
"stream": false,
},
Retryable: true,
})
if err != nil {
return err
}
cc.assert("status_400", resp.StatusCode == http.StatusBadRequest, fmt.Sprintf("status=%d", resp.StatusCode))
var m map[string]any
_ = json.Unmarshal(resp.Body, &m)
e, _ := m["error"].(map[string]any)
cc.assert("error_type_invalid_request", asString(e["type"]) == "invalid_request_error", fmt.Sprintf("body=%s", string(resp.Body)))
return nil
}
func (r *Runner) caseAdminUnauthorized(ctx context.Context, cc *caseContext) error {
resp, err := cc.request(ctx, requestSpec{
Method: http.MethodGet,
Path: "/admin/config",
Retryable: true,
})
if err != nil {
return err
}
cc.assert("status_401", resp.StatusCode == http.StatusUnauthorized, fmt.Sprintf("status=%d", resp.StatusCode))
return nil
}
func (r *Runner) caseTokenRefreshManagedAccount(ctx context.Context, cc *caseContext) error {
if len(r.configRaw.Accounts) == 0 {
cc.assert("account_present", false, "no account in config")
return nil
}
acc := r.configRaw.Accounts[0]
id := strings.TrimSpace(acc.Email)
if id == "" {
id = strings.TrimSpace(acc.Mobile)
}
if id == "" {
cc.assert("account_identifier", false, "first account has no identifier")
return nil
}
if strings.TrimSpace(acc.Password) == "" {
r.warnings = append(r.warnings, "token refresh edge case skipped strict check: first account password empty")
cc.assert("account_password_present", true, "skipped strict refresh check due empty password")
return nil
}
invalidToken := "invalid-testsuite-refresh-token-" + sanitizeID(r.runID)
update := map[string]any{
"keys": r.configRaw.Keys,
"accounts": []map[string]any{
{
"email": acc.Email,
"mobile": acc.Mobile,
"password": acc.Password,
"token": invalidToken,
},
},
}
updResp, err := cc.request(ctx, requestSpec{
Method: http.MethodPost,
Path: "/admin/config",
Headers: map[string]string{
"Authorization": "Bearer " + r.adminJWT,
},
Body: update,
Retryable: true,
})
if err != nil {
return err
}
cc.assert("update_config_status_200", updResp.StatusCode == http.StatusOK, fmt.Sprintf("status=%d", updResp.StatusCode))
chatResp, err := cc.request(ctx, requestSpec{
Method: http.MethodPost,
Path: "/v1/chat/completions",
Headers: map[string]string{
"Authorization": "Bearer " + r.apiKey,
"X-Ds2-Target-Account": id,
},
Body: map[string]any{
"model": "deepseek-v4-flash",
"messages": []map[string]any{
{"role": "user", "content": "token refresh test"},
},
"stream": false,
},
Retryable: true,
})
if err != nil {
return err
}
cc.assert("chat_status_200", chatResp.StatusCode == http.StatusOK, fmt.Sprintf("status=%d body=%s", chatResp.StatusCode, string(chatResp.Body)))
cfgResp, err := cc.request(ctx, requestSpec{
Method: http.MethodGet,
Path: "/admin/config",
Headers: map[string]string{
"Authorization": "Bearer " + r.adminJWT,
},
Retryable: true,
})
if err != nil {
return err
}
var cfg map[string]any
_ = json.Unmarshal(cfgResp.Body, &cfg)
accounts, _ := cfg["accounts"].([]any)
preview := ""
hasToken := false
for _, item := range accounts {
m, _ := item.(map[string]any)
e := asString(m["email"])
mo := asString(m["mobile"])
if e == acc.Email && mo == acc.Mobile {
preview = asString(m["token_preview"])
hasToken, _ = m["has_token"].(bool)
break
}
}
cc.assert("has_token_after_refresh", hasToken, fmt.Sprintf("config=%s", string(cfgResp.Body)))
maskedInvalid := invalidToken
if len(maskedInvalid) <= 4 {
maskedInvalid = strings.Repeat("*", len(maskedInvalid))
} else {
maskedInvalid = maskedInvalid[:2] + "****" + maskedInvalid[len(maskedInvalid)-2:]
}
cc.assert("token_preview_changed_from_invalid", preview != maskedInvalid, fmt.Sprintf("preview=%s invalid_mask=%s", preview, maskedInvalid))
return nil
}