202 lines
9.5 KiB
Go
202 lines
9.5 KiB
Go
package vipconfig
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"hyapp-admin-server/internal/appctx"
|
|
"hyapp-admin-server/internal/integration/walletclient"
|
|
"hyapp-admin-server/internal/middleware"
|
|
walletv1 "hyapp.local/api/proto/wallet/v1"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func TestGetProgramReturnsCamelCaseCompleteConfigAndBenefits(t *testing.T) {
|
|
wallet := &fakeVIPConfigWallet{
|
|
programResponse: &walletv1.GetVipProgramConfigResponse{
|
|
Config: &walletv1.VipProgramConfig{
|
|
AppCode: "fami",
|
|
ProgramType: "tiered_privilege_v1",
|
|
LevelCount: 9,
|
|
SameLevelExpiryPolicy: "extend_remaining",
|
|
UpgradeExpiryPolicy: "replace_from_now",
|
|
DowngradePurchasePolicy: "reject",
|
|
BenefitInheritancePolicy: "target_only",
|
|
GrantMode: "trial_card",
|
|
TrialCardEnabled: true,
|
|
Status: "active",
|
|
ConfigVersion: 3,
|
|
UpdatedByAdminId: 77,
|
|
CreatedAtMs: 100,
|
|
UpdatedAtMs: 200,
|
|
},
|
|
Benefits: []*walletv1.VipBenefit{{
|
|
BenefitCode: "anti_kick",
|
|
Name: "防踢",
|
|
BenefitType: "function",
|
|
UnlockLevel: 9,
|
|
Status: "active",
|
|
TrialEnabled: true,
|
|
ExecutionScope: "room",
|
|
MetadataJson: "{}",
|
|
CreatedAtMs: 101,
|
|
UpdatedAtMs: 201,
|
|
}},
|
|
ServerTimeMs: 300,
|
|
},
|
|
}
|
|
recorder := serveVIPConfigRequest(t, wallet, http.MethodGet, "/admin/activity/vip-program", "")
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("get program status = %d, body=%s", recorder.Code, recorder.Body.String())
|
|
}
|
|
body := recorder.Body.String()
|
|
for _, fragment := range []string{
|
|
`"programType":"tiered_privilege_v1"`,
|
|
`"upgradeExpiryPolicy":"replace_from_now"`,
|
|
`"updatedByAdminId":77`,
|
|
`"benefitCode":"anti_kick"`,
|
|
`"executionScope":"room"`,
|
|
`"serverTimeMs":300`,
|
|
} {
|
|
if !strings.Contains(body, fragment) {
|
|
t.Fatalf("get program response missing %s: %s", fragment, body)
|
|
}
|
|
}
|
|
if strings.Contains(body, "program_type") || strings.Contains(body, "benefit_code") {
|
|
t.Fatalf("admin contract must stay camelCase: %s", body)
|
|
}
|
|
if wallet.getProgramRequest.GetAppCode() != "fami" || wallet.getProgramRequest.GetRequestId() != "vip-config-test" {
|
|
t.Fatalf("get program request mismatch: %+v", wallet.getProgramRequest)
|
|
}
|
|
}
|
|
|
|
func TestUpdateProgramUsesContextAppAndAuthenticatedOperator(t *testing.T) {
|
|
wallet := &fakeVIPConfigWallet{updateProgramResponse: &walletv1.UpdateAdminVipProgramConfigResponse{
|
|
Config: &walletv1.VipProgramConfig{AppCode: "fami", ProgramType: "tiered_privilege_v1", LevelCount: 9, Status: "active", ConfigVersion: 4},
|
|
ServerTimeMs: 400,
|
|
}}
|
|
body := `{"config":{"appCode":"lalu","programType":"tiered_privilege_v1","levelCount":9,"sameLevelExpiryPolicy":"extend_remaining","upgradeExpiryPolicy":"replace_from_now","downgradePurchasePolicy":"reject","benefitInheritancePolicy":"target_only","grantMode":"trial_card","trialCardEnabled":true,"status":"active","configVersion":999,"updatedByAdminId":999}}`
|
|
recorder := serveVIPConfigRequest(t, wallet, http.MethodPut, "/admin/activity/vip-program", body)
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("update program status = %d, body=%s", recorder.Code, recorder.Body.String())
|
|
}
|
|
req := wallet.updateProgramRequest
|
|
if req.GetAppCode() != "fami" || req.GetOperatorUserId() != 77 {
|
|
t.Fatalf("program scope/operator mismatch: %+v", req)
|
|
}
|
|
if req.GetConfig().GetAppCode() != "fami" || req.GetConfig().GetConfigVersion() != 0 || req.GetConfig().GetUpdatedByAdminId() != 0 {
|
|
t.Fatalf("client-controlled program audit fields leaked through: %+v", req.GetConfig())
|
|
}
|
|
if !strings.Contains(recorder.Body.String(), `"configVersion":4`) || !strings.Contains(recorder.Body.String(), `"benefits":[]`) {
|
|
t.Fatalf("update program response mismatch: %s", recorder.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestUpdateLevelsForwardsExplicitBenefitsAndReturnsProgram(t *testing.T) {
|
|
wallet := &fakeVIPConfigWallet{updateLevelsResponse: &walletv1.UpdateAdminVipLevelsResponse{
|
|
Levels: []*walletv1.VipLevel{{
|
|
Level: 2, Name: "VIP2", Status: "active", DurationMs: 2_592_000_000,
|
|
Benefits: []*walletv1.VipBenefit{{BenefitCode: "room_entry_notice", Name: "进房通知", Status: "active", ExecutionScope: "room", MetadataJson: "{}"}},
|
|
}},
|
|
ProgramConfig: &walletv1.VipProgramConfig{AppCode: "fami", ProgramType: "tiered_privilege_v1", LevelCount: 9},
|
|
ServerTimeMs: 500,
|
|
}}
|
|
body := `{"levels":[{"level":2,"name":"VIP2","status":"active","priceCoin":2000,"durationMs":2592000000,"rewardResourceGroupId":22,"sortOrder":20,"requiredRechargeCoinAmount":123,"benefits":[{"benefitCode":"room_entry_notice","name":"进房通知","benefitType":"function","unlockLevel":2,"status":"active","trialEnabled":true,"resourceId":0,"resourceType":"","executionScope":"room","autoEquip":false,"sortOrder":10,"metadataJson":"{}"}]}]}`
|
|
recorder := serveVIPConfigRequest(t, wallet, http.MethodPut, "/admin/activity/vip-levels", body)
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("update levels status = %d, body=%s", recorder.Code, recorder.Body.String())
|
|
}
|
|
req := wallet.updateLevelsRequest
|
|
if req.GetAppCode() != "fami" || req.GetOperatorUserId() != 77 || len(req.GetLevels()) != 1 {
|
|
t.Fatalf("update levels request mismatch: %+v", req)
|
|
}
|
|
level := req.GetLevels()[0]
|
|
if level.GetRequiredRechargeCoinAmount() != 0 || len(level.GetBenefits()) != 1 || level.GetBenefits()[0].GetBenefitCode() != "room_entry_notice" {
|
|
t.Fatalf("explicit level benefits mismatch: %+v", level)
|
|
}
|
|
if !strings.Contains(recorder.Body.String(), `"programConfig":{"appCode":"fami","programType":"tiered_privilege_v1","levelCount":9`) {
|
|
t.Fatalf("level response must include program config: %s", recorder.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestGrantTrialCardLeavesOptionalResourceUnsetAndUsesAuthenticatedOperator(t *testing.T) {
|
|
wallet := &fakeVIPConfigWallet{grantTrialResponse: &walletv1.GrantVipTrialCardResponse{
|
|
TrialCard: &walletv1.VipTrialCard{TrialCardId: "trial-1", EntitlementId: "ent-1", UserId: 318705991371722752, Level: 5, DurationMs: 1_728_000_000, ExpiresAtMs: 2_000},
|
|
State: &walletv1.VipState{EffectiveSource: "none", EffectiveBenefits: []*walletv1.VipBenefit{}},
|
|
ServerTimeMs: 1_000,
|
|
}}
|
|
body := `{"targetUserId":"318705991371722752","level":5,"durationMs":1728000000,"reason":"manual"}`
|
|
recorder := serveVIPConfigRequest(t, wallet, http.MethodPost, "/admin/activity/vip-trial-card-grants", body)
|
|
if recorder.Code != http.StatusCreated {
|
|
t.Fatalf("grant trial card status = %d, body=%s", recorder.Code, recorder.Body.String())
|
|
}
|
|
req := wallet.grantTrialRequest
|
|
if req.GetCommandId() != "admin_vip_trial_card_grant:vip-config-test" || req.GetAppCode() != "fami" || req.GetOperatorUserId() != 77 {
|
|
t.Fatalf("trial grant command context mismatch: %+v", req)
|
|
}
|
|
if req.ResourceId != nil || req.GetTargetUserId() != 318705991371722752 || req.GetDurationMs() != 1_728_000_000 {
|
|
t.Fatalf("trial grant payload mismatch: %+v", req)
|
|
}
|
|
if !strings.Contains(recorder.Body.String(), `"trialCardId":"trial-1"`) || !strings.Contains(recorder.Body.String(), `"userId":"318705991371722752"`) {
|
|
t.Fatalf("trial grant response mismatch: %s", recorder.Body.String())
|
|
}
|
|
}
|
|
|
|
func serveVIPConfigRequest(t *testing.T, wallet walletclient.Client, method string, path string, body string) *httptest.ResponseRecorder {
|
|
t.Helper()
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Request = c.Request.WithContext(appctx.WithContext(c.Request.Context(), "fami"))
|
|
c.Set(middleware.ContextRequestID, "vip-config-test")
|
|
c.Set(middleware.ContextUserID, uint(77))
|
|
c.Set(middleware.ContextUsername, "vip-operator")
|
|
c.Set(middleware.ContextPermissions, []string{"vip-config:view", "vip-config:update", "vip-config:grant"})
|
|
c.Next()
|
|
})
|
|
RegisterRoutes(router.Group(""), New(wallet, nil))
|
|
recorder := httptest.NewRecorder()
|
|
request := httptest.NewRequest(method, path, strings.NewReader(body))
|
|
if body != "" {
|
|
request.Header.Set("Content-Type", "application/json")
|
|
}
|
|
router.ServeHTTP(recorder, request)
|
|
return recorder
|
|
}
|
|
|
|
type fakeVIPConfigWallet struct {
|
|
walletclient.Client
|
|
programResponse *walletv1.GetVipProgramConfigResponse
|
|
updateProgramResponse *walletv1.UpdateAdminVipProgramConfigResponse
|
|
updateLevelsResponse *walletv1.UpdateAdminVipLevelsResponse
|
|
grantTrialResponse *walletv1.GrantVipTrialCardResponse
|
|
getProgramRequest *walletv1.GetVipProgramConfigRequest
|
|
updateProgramRequest *walletv1.UpdateAdminVipProgramConfigRequest
|
|
updateLevelsRequest *walletv1.UpdateAdminVipLevelsRequest
|
|
grantTrialRequest *walletv1.GrantVipTrialCardRequest
|
|
}
|
|
|
|
func (f *fakeVIPConfigWallet) GetVipProgramConfig(_ context.Context, req *walletv1.GetVipProgramConfigRequest) (*walletv1.GetVipProgramConfigResponse, error) {
|
|
f.getProgramRequest = req
|
|
return f.programResponse, nil
|
|
}
|
|
|
|
func (f *fakeVIPConfigWallet) UpdateAdminVipProgramConfig(_ context.Context, req *walletv1.UpdateAdminVipProgramConfigRequest) (*walletv1.UpdateAdminVipProgramConfigResponse, error) {
|
|
f.updateProgramRequest = req
|
|
return f.updateProgramResponse, nil
|
|
}
|
|
|
|
func (f *fakeVIPConfigWallet) UpdateAdminVipLevels(_ context.Context, req *walletv1.UpdateAdminVipLevelsRequest) (*walletv1.UpdateAdminVipLevelsResponse, error) {
|
|
f.updateLevelsRequest = req
|
|
return f.updateLevelsResponse, nil
|
|
}
|
|
|
|
func (f *fakeVIPConfigWallet) GrantVipTrialCard(_ context.Context, req *walletv1.GrantVipTrialCardRequest) (*walletv1.GrantVipTrialCardResponse, error) {
|
|
f.grantTrialRequest = req
|
|
return f.grantTrialResponse, nil
|
|
}
|