feat: support verified bulk load-test setup

This commit is contained in:
richjohnrambo
2026-09-27 01:34:39 +08:00
parent 021169555a
commit b3b5dd55a6
8 changed files with 272 additions and 47 deletions
+41 -3
View File
@@ -18,6 +18,7 @@ import (
type apiClient struct {
baseURL string
apiKey string
tenant string
client *http.Client
seq atomic.Uint64
}
@@ -47,8 +48,8 @@ func newAPIClient(cfg *Config, apiKey string) *apiClient {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} // #nosec G402: explicit load-test option.
}
return &apiClient{
baseURL: strings.TrimRight(cfg.HTTPBase, "/"), apiKey: apiKey,
client: &http.Client{Timeout: 30 * time.Second, Transport: transport},
baseURL: strings.TrimRight(cfg.HTTPBase, "/"), apiKey: apiKey, tenant: cfg.Tenant,
client: &http.Client{Timeout: 2 * time.Minute, Transport: transport},
}
}
@@ -66,6 +67,7 @@ func (a *apiClient) do(ctx context.Context, method, path, token, idempotencyKey
return err
}
req.Header.Set("X-MeetingIM-APIKey", a.apiKey)
req.Header.Set("X-Tenant-Code", a.tenant)
req.Header.Set("X-IM-Schema", "1")
req.Header.Set("X-Request-ID", fmt.Sprintf("loadtest-%d-%d", time.Now().UnixNano(), a.seq.Add(1)))
if token != "" {
@@ -107,6 +109,40 @@ type privacyResponse struct {
Version int `json:"version"`
}
type registrationCredential struct {
ChallengeID string
VerificationToken string
}
func (a *apiClient) createRegistrationCredential(ctx context.Context, account AccountRecord, code string) (*registrationCredential, error) {
if account.Phone == "" {
return nil, nil
}
var challenge struct {
ChallengeID string `json:"challenge_id"`
}
key := fmt.Sprintf("loadtest-verification-%s-%d", account.Username, time.Now().UnixNano())
body := map[string]any{"scene": "register", "registration_type": 0, "tenant_code": a.tenant, "phone": account.Phone}
if err := a.do(ctx, http.MethodPost, "/v1/im/auth/verification-codes", "", key, body, &challenge); err != nil {
return nil, fmt.Errorf("create verification for %s: %w", account.Username, err)
}
if challenge.ChallengeID == "" {
return nil, fmt.Errorf("create verification for %s: missing challenge_id", account.Username)
}
var verified struct {
VerificationToken string `json:"verification_token"`
}
key = fmt.Sprintf("loadtest-verify-%s-%d", account.Username, time.Now().UnixNano())
path := "/v1/im/auth/verification-codes/" + url.PathEscape(challenge.ChallengeID) + "/verify"
if err := a.do(ctx, http.MethodPost, path, "", key, map[string]any{"code": code}, &verified); err != nil {
return nil, fmt.Errorf("verify code for %s: %w", account.Username, err)
}
if verified.VerificationToken == "" {
return nil, fmt.Errorf("verify code for %s: missing verification_token", account.Username)
}
return &registrationCredential{ChallengeID: challenge.ChallengeID, VerificationToken: verified.VerificationToken}, nil
}
func (a *apiClient) disableGroupInviteVerification(ctx context.Context, account AccountRecord) error {
var privacy privacyResponse
if err := a.do(ctx, http.MethodGet, "/v1/im/me/privacy", account.Token, "", nil, &privacy); err != nil {
@@ -211,8 +247,10 @@ type groupInvitationResponse struct {
func (a *apiClient) inviteGroup(ctx context.Context, owner AccountRecord, topic string, batch int, userIDs []string) (*groupInvitationResponse, error) {
var result groupInvitationResponse
membersHash := topicHash(strings.Join(userIDs, ","))
err := a.do(ctx, http.MethodPost, "/v1/im/groups/"+url.PathEscape(topic)+"/invitations", owner.Token,
fmt.Sprintf("loadtest-group-%s-invite-%03d", topicHash(topic), batch), map[string]any{"user_ids": userIDs, "message": "loadtest"}, &result)
fmt.Sprintf("loadtest-group-%s-invite-%03d-%s", topicHash(topic), batch, membersHash),
map[string]any{"user_ids": userIDs, "message": "loadtest"}, &result)
return &result, err
}