feat(ECR-015): RedemptionCode 兑换码并 Closed
批次生成/作废、C 端兑码延长会员;admin-h5 /codes。 Loop continuous。Next:ECR-016 UserIntelligence。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user