批次生成/作废、C 端兑码延长会员;admin-h5 /codes。 Loop continuous。Next:ECR-016 UserIntelligence。 Co-authored-by: Cursor <cursoragent@cursor.com>
101 lines
3.0 KiB
Go
101 lines
3.0 KiB
Go
package integration_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestRedemptionCodes(t *testing.T) {
|
|
r, _ := setupAPIPool(t)
|
|
tok := adminLogin(t, r, "admin", "change-me")
|
|
|
|
_, code := doAdminJSON(t, r, http.MethodPost, "/api/v1/admin/redemption-batches",
|
|
map[string]any{"label": "t", "plan_code": "month", "quantity": 2}, "")
|
|
if code != http.StatusUnauthorized {
|
|
t.Fatalf("expected 401, got %d", code)
|
|
}
|
|
|
|
start := time.Now()
|
|
env, code := doAdminJSON(t, r, http.MethodPost, "/api/v1/admin/redemption-batches",
|
|
map[string]any{"label": "ops-test", "plan_code": "month", "quantity": 3}, tok)
|
|
if code != 200 {
|
|
t.Fatalf("create batch http=%d msg=%s", code, env.Message)
|
|
}
|
|
var created struct {
|
|
Batch struct {
|
|
ID string `json:"id"`
|
|
} `json:"batch"`
|
|
Codes []struct {
|
|
ID string `json:"id"`
|
|
Code string `json:"code"`
|
|
Status string `json:"status"`
|
|
} `json:"codes"`
|
|
}
|
|
_ = json.Unmarshal(env.Data, &created)
|
|
if len(created.Codes) != 3 {
|
|
t.Fatalf("want 3 codes, got %#v", created.Codes)
|
|
}
|
|
raw := created.Codes[0].Code
|
|
disableID := created.Codes[2].ID
|
|
|
|
_, code = doAdminJSON(t, r, http.MethodGet, "/api/v1/admin/redemption-batches", nil, tok)
|
|
if code != 200 || time.Since(start) > 500*time.Millisecond {
|
|
t.Fatalf("list batches http=%d dur=%v", code, time.Since(start))
|
|
}
|
|
|
|
_, code = doAdminJSON(t, r, http.MethodGet, "/api/v1/admin/redemption-batches/"+created.Batch.ID+"/codes", nil, tok)
|
|
if code != 200 {
|
|
t.Fatalf("list codes %d", code)
|
|
}
|
|
|
|
key := mustRegister(t, r)
|
|
_, _, httpCode := doJSONExpect(t, r, http.MethodPost, "/api/v1/membership/redeem",
|
|
map[string]string{"code": raw}, key, 0)
|
|
if httpCode != 200 {
|
|
t.Fatalf("redeem http=%d", httpCode)
|
|
}
|
|
|
|
_, _, httpCode = doJSONExpect(t, r, http.MethodPost, "/api/v1/membership/redeem",
|
|
map[string]string{"code": raw}, key, 40000)
|
|
if httpCode != http.StatusBadRequest {
|
|
t.Fatalf("expected HTTP 400 re-redeem, got %d", httpCode)
|
|
}
|
|
|
|
_, code = doAdminJSON(t, r, http.MethodPost, "/api/v1/admin/redemption-codes/"+disableID+"/disable", nil, tok)
|
|
if code != 200 {
|
|
t.Fatalf("disable %d", code)
|
|
}
|
|
_, _, httpCode = doJSONExpect(t, r, http.MethodPost, "/api/v1/membership/redeem",
|
|
map[string]string{"code": created.Codes[2].Code}, key, 40000)
|
|
if httpCode != http.StatusBadRequest {
|
|
t.Fatalf("expected HTTP 400 disabled, got %d", httpCode)
|
|
}
|
|
|
|
env, code = doAdminJSON(t, r, http.MethodGet, "/api/v1/admin/audit-logs", nil, tok)
|
|
if code != 200 {
|
|
t.Fatalf("audit %d", code)
|
|
}
|
|
var audit struct {
|
|
Items []struct {
|
|
Action string `json:"action"`
|
|
} `json:"items"`
|
|
}
|
|
_ = json.Unmarshal(env.Data, &audit)
|
|
found := false
|
|
for _, it := range audit.Items {
|
|
if it.Action == "redemption.batch.create" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("missing redemption.batch.create audit")
|
|
}
|
|
|
|
if code := deviceGET(t, r, "/api/v1/membership/me", "dev_orphan_"+time.Now().Format("150405"), ""); code != http.StatusUnauthorized {
|
|
t.Fatalf("expected 401 unregistered membership, got %d", code)
|
|
}
|
|
}
|