bindbox-game/internal/api/admin/threshold_activities_admin_test.go

209 lines
7.0 KiB
Go

package admin
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"bindbox-game/internal/pkg/core"
"bindbox-game/internal/pkg/logger"
"bindbox-game/internal/proposal"
"bindbox-game/internal/repository/mysql"
)
func setupThresholdAdminTestRouter(t *testing.T) (mysql.Repo, http.Handler) {
t.Helper()
repo, err := mysql.NewSQLiteRepoForTest()
if err != nil {
t.Fatal(err)
}
ddls := []string{
`CREATE TABLE threshold_activities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
type TEXT NOT NULL,
qualification_mode TEXT NOT NULL,
spend_threshold_amount INTEGER NOT NULL DEFAULT 0,
invite_threshold_count INTEGER NOT NULL DEFAULT 0,
invite_effective_amount INTEGER NOT NULL DEFAULT 0,
min_participants INTEGER NOT NULL DEFAULT 1,
start_time DATETIME NOT NULL,
end_time DATETIME NOT NULL,
draw_time DATETIME NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
description TEXT,
cover_image TEXT NOT NULL DEFAULT '',
draw_batch TEXT NOT NULL DEFAULT '',
abort_reason TEXT NOT NULL DEFAULT '',
aborted_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
deleted_at DATETIME
)`,
`CREATE TABLE threshold_activity_prizes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
activity_id INTEGER NOT NULL,
reward_type TEXT NOT NULL,
reward_ref_id INTEGER NOT NULL,
reward_name_snapshot TEXT NOT NULL DEFAULT '',
reward_image_snapshot TEXT NOT NULL DEFAULT '',
reward_value_snapshot_cents INTEGER NOT NULL DEFAULT 0,
cost_snapshot_cents INTEGER NOT NULL DEFAULT 0,
quantity INTEGER NOT NULL DEFAULT 0,
remaining_quantity INTEGER NOT NULL DEFAULT 0,
sort INTEGER NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`,
`CREATE TABLE threshold_activity_participants (
id INTEGER PRIMARY KEY AUTOINCREMENT,
activity_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
period_key TEXT NOT NULL,
qualification_source TEXT NOT NULL DEFAULT '',
paid_amount_snapshot INTEGER NOT NULL DEFAULT 0,
effective_invite_count_snapshot INTEGER NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`,
`CREATE TABLE threshold_activity_winners (
id INTEGER PRIMARY KEY AUTOINCREMENT,
activity_id INTEGER NOT NULL,
prize_id INTEGER NOT NULL,
reward_type TEXT NOT NULL,
reward_ref_id INTEGER NOT NULL,
prize_name_snapshot TEXT NOT NULL DEFAULT '',
prize_image_snapshot TEXT NOT NULL DEFAULT '',
prize_value_snapshot_cents INTEGER NOT NULL DEFAULT 0,
user_id INTEGER NOT NULL,
grant_record_type TEXT NOT NULL DEFAULT '',
grant_record_id INTEGER NOT NULL DEFAULT 0,
cost_cents INTEGER NOT NULL DEFAULT 0,
draw_batch TEXT NOT NULL DEFAULT '',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`,
`CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nickname TEXT NOT NULL,
avatar TEXT,
invite_code TEXT NOT NULL,
inviter_id INTEGER,
status INTEGER NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
deleted_at DATETIME
)`,
}
for _, ddl := range ddls {
if err := repo.GetDbW().Exec(ddl).Error; err != nil {
t.Fatal(err)
}
}
lg, err := logger.NewCustomLogger(logger.WithOutputInConsole())
if err != nil {
t.Fatal(err)
}
mux, err := core.New(lg)
if err != nil {
t.Fatal(err)
}
dummyAuth := func(ctx core.Context) (proposal.SessionUserInfo, core.BusinessError) {
return proposal.SessionUserInfo{Id: 1, IsSuper: 1}, nil
}
g := mux.Group("/api/admin", core.WrapAuthHandler(dummyAuth))
h := New(lg, repo, nil)
g.GET("/threshold-activities", h.ListThresholdActivities())
g.POST("/threshold-activities", h.CreateThresholdActivity())
g.GET("/threshold-activities/:id", h.GetThresholdActivity())
g.PUT("/threshold-activities/:id", h.UpdateThresholdActivity())
return repo, mux
}
func TestThresholdAdminCreateListAndGet(t *testing.T) {
_, mux := setupThresholdAdminTestRouter(t)
start := time.Now().Add(-time.Hour).Format("2006-01-02 15:04:05")
end := time.Now().Add(24 * time.Hour).Format("2006-01-02 15:04:05")
draw := time.Now().Add(25 * time.Hour).Format("2006-01-02 15:04:05")
body := map[string]any{
"title": "后台测试活动",
"type": "daily",
"qualification_mode": "invite_only",
"spend_threshold_amount": 0,
"invite_threshold_count": 2,
"invite_effective_amount": 1000,
"min_participants": 3,
"start_time": start,
"end_time": end,
"draw_time": draw,
"status": "active",
"description": "admin create",
"prizes": []any{},
}
payload, _ := json.Marshal(body)
rr := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/api/admin/threshold-activities", bytes.NewReader(payload))
req.Header.Set("Content-Type", "application/json")
mux.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("create code=%d body=%s", rr.Code, rr.Body.String())
}
var created map[string]any
if err := json.Unmarshal(rr.Body.Bytes(), &created); err != nil {
t.Fatal(err)
}
id := int(created["id"].(float64))
if id <= 0 {
t.Fatalf("expected positive activity id, got %v", created["id"])
}
rr = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodGet, "/api/admin/threshold-activities?page=1&page_size=20&status=active", bytes.NewReader([]byte{}))
mux.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("list code=%d body=%s", rr.Code, rr.Body.String())
}
var listed struct {
Total int64 `json:"total"`
List []map[string]any `json:"list"`
}
if err := json.Unmarshal(rr.Body.Bytes(), &listed); err != nil {
t.Fatal(err)
}
if listed.Total != 1 || len(listed.List) != 1 {
t.Fatalf("expected 1 listed activity, got total=%d len=%d", listed.Total, len(listed.List))
}
rr = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodGet, fmt.Sprintf("/api/admin/threshold-activities/%d", id), bytes.NewReader([]byte{}))
mux.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("get code=%d body=%s", rr.Code, rr.Body.String())
}
var detail map[string]any
if err := json.Unmarshal(rr.Body.Bytes(), &detail); err != nil {
t.Fatal(err)
}
if detail["title"] != "后台测试活动" {
t.Fatalf("unexpected title: %v", detail["title"])
}
if detail["qualification_mode"] != "invite_only" {
t.Fatalf("unexpected qualification mode: %v", detail["qualification_mode"])
}
}
func TestThresholdAdminCreateRejectsInvalidPayload(t *testing.T) {
_, mux := setupThresholdAdminTestRouter(t)
body := []byte(`{"title":"bad","type":"daily","start_time":"2026-01-01 00:00:00","end_time":"2026-01-02 00:00:00","draw_time":"2026-01-03 00:00:00"}`)
rr := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/api/admin/threshold-activities", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
mux.ServeHTTP(rr, req)
if rr.Code != http.StatusBadRequest {
t.Fatalf("expected 400 for invalid payload, got %d body=%s", rr.Code, rr.Body.String())
}
}