mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-04 08:25:26 +08:00
177 lines
5.0 KiB
Go
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
|
|
}
|