365 lines
10 KiB
Go
365 lines
10 KiB
Go
package taskcenter
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"chatapp3-golang/internal/common"
|
|
"chatapp3-golang/internal/config"
|
|
"chatapp3-golang/internal/integration"
|
|
"chatapp3-golang/internal/model"
|
|
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func newTestService(t *testing.T, java taskCenterJavaGateway) (*Service, *gorm.DB) {
|
|
t.Helper()
|
|
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatalf("open sqlite: %v", err)
|
|
}
|
|
if err := db.AutoMigrate(
|
|
&model.TaskCenterConfig{},
|
|
&model.TaskCenterTaskConfig{},
|
|
&model.TaskCenterConditionJumpConfig{},
|
|
&model.TaskCenterUserProgress{},
|
|
&model.TaskCenterEvent{},
|
|
&model.TaskCenterClaimRecord{},
|
|
); err != nil {
|
|
t.Fatalf("auto migrate: %v", err)
|
|
}
|
|
service := NewService(config.Config{
|
|
TaskCenter: config.TaskCenterConfig{
|
|
DefaultSysOrigin: "LIKEI",
|
|
Timezone: "Asia/Riyadh",
|
|
},
|
|
}, db, java)
|
|
return service, db
|
|
}
|
|
|
|
func TestDailyTaskConfigEventListAndClaim(t *testing.T) {
|
|
java := &taskCenterTestGateway{events: map[string]bool{}}
|
|
service, _ := newTestService(t, java)
|
|
ctx := context.Background()
|
|
|
|
configResp, err := service.SaveConfig(ctx, SaveConfigRequest{
|
|
SysOrigin: "likei",
|
|
Enabled: true,
|
|
Timezone: "Asia/Riyadh",
|
|
TaskCategory: TaskCategoryDaily,
|
|
Tasks: []SaveTaskConfigInput{
|
|
{
|
|
TaskCode: "daily_mic_duration",
|
|
Enabled: true,
|
|
TaskName: "使用麦克风 %d 分钟。",
|
|
TaskDesc: "进度 / 目标",
|
|
ConditionType: "mic_duration_seconds",
|
|
TargetValue: 60,
|
|
TargetUnit: "second",
|
|
RewardGold: 1100,
|
|
JumpType: "ROOM_RANDOM_WITH_MIC",
|
|
SortOrder: 10,
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("SaveConfig() error = %v", err)
|
|
}
|
|
if !configResp.Configured || !configResp.Enabled || len(configResp.Tasks) != 1 {
|
|
t.Fatalf("SaveConfig() resp = %+v", configResp)
|
|
}
|
|
|
|
user := common.AuthUser{UserID: 1001, SysOrigin: "LIKEI"}
|
|
tasks, err := service.ListTasks(ctx, user)
|
|
if err != nil {
|
|
t.Fatalf("ListTasks() error = %v", err)
|
|
}
|
|
if len(tasks) != 1 || tasks[0].TaskStatus != 0 || tasks[0].CompletedValue != 0 {
|
|
t.Fatalf("initial tasks = %+v", tasks)
|
|
}
|
|
|
|
eventResp, err := service.ReceiveEvent(ctx, EventRequest{
|
|
SysOrigin: "LIKEI",
|
|
EventID: "event-1",
|
|
EventType: "MIC_DURATION_SECONDS",
|
|
UserID: flexibleInt64(user.UserID),
|
|
DeltaValue: 60,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ReceiveEvent() error = %v", err)
|
|
}
|
|
if !eventResp.Processed || eventResp.Updated != 1 {
|
|
t.Fatalf("ReceiveEvent() resp = %+v", eventResp)
|
|
}
|
|
|
|
tasks, err = service.ListTasks(ctx, user)
|
|
if err != nil {
|
|
t.Fatalf("ListTasks() after event error = %v", err)
|
|
}
|
|
if len(tasks) != 1 || tasks[0].TaskStatus != 1 || tasks[0].CompletedValue != 60 {
|
|
t.Fatalf("completed tasks = %+v", tasks)
|
|
}
|
|
|
|
ok, err := service.Claim(ctx, user, tasks[0].TaskID)
|
|
if err != nil {
|
|
t.Fatalf("Claim() error = %v", err)
|
|
}
|
|
if !ok {
|
|
t.Fatalf("Claim() = false, want true")
|
|
}
|
|
if len(java.changedEvents) != 1 {
|
|
t.Fatalf("wallet changes = %d, want 1", len(java.changedEvents))
|
|
}
|
|
if java.changedCommands[0].Origin != "DAILY_TASK" {
|
|
t.Fatalf("wallet origin = %q, want DAILY_TASK", java.changedCommands[0].Origin)
|
|
}
|
|
if len(java.changedEvents[0]) > 50 {
|
|
t.Fatalf("wallet event id length = %d, want <= 50: %s", len(java.changedEvents[0]), java.changedEvents[0])
|
|
}
|
|
|
|
ok, err = service.Claim(ctx, user, tasks[0].TaskID)
|
|
if err != nil {
|
|
t.Fatalf("Claim() duplicate error = %v", err)
|
|
}
|
|
if !ok || len(java.changedEvents) != 1 {
|
|
t.Fatalf("duplicate claim ok=%v changes=%d, want true/1", ok, len(java.changedEvents))
|
|
}
|
|
}
|
|
|
|
func TestExclusiveTaskConfigUsesLifetimeCycleAndHidesAfterClaim(t *testing.T) {
|
|
java := &taskCenterTestGateway{events: map[string]bool{}}
|
|
service, _ := newTestService(t, java)
|
|
ctx := context.Background()
|
|
|
|
_, err := service.SaveConfig(ctx, SaveConfigRequest{
|
|
SysOrigin: "LIKEI",
|
|
Enabled: true,
|
|
Timezone: "Asia/Riyadh",
|
|
TaskCategory: TaskCategoryNewbie,
|
|
Tasks: []SaveTaskConfigInput{
|
|
{
|
|
TaskCode: "EXCLUSIVE_RECHARGE",
|
|
Enabled: true,
|
|
TaskName: "专属充值任务",
|
|
ConditionType: "RECHARGE_GOLD",
|
|
TargetValue: 10,
|
|
TargetUnit: "gold",
|
|
RewardGold: 100,
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("SaveConfig() error = %v", err)
|
|
}
|
|
|
|
user := common.AuthUser{UserID: 2001, SysOrigin: "LIKEI"}
|
|
tasks, err := service.ListTasks(ctx, user)
|
|
if err != nil {
|
|
t.Fatalf("ListTasks() error = %v", err)
|
|
}
|
|
if len(tasks) != 1 || tasks[0].TaskType != 1 {
|
|
t.Fatalf("exclusive tasks = %+v, want one taskType=1", tasks)
|
|
}
|
|
if tasks[0].JumpPage != defaultRechargeJumpPage {
|
|
t.Fatalf("exclusive recharge jumpPage = %q, want %q", tasks[0].JumpPage, defaultRechargeJumpPage)
|
|
}
|
|
|
|
eventResp, err := service.ReceiveEvent(ctx, EventRequest{
|
|
SysOrigin: "LIKEI",
|
|
EventID: "recharge-1",
|
|
EventType: "RECHARGE_GOLD",
|
|
UserID: flexibleInt64(user.UserID),
|
|
DeltaValue: 10,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ReceiveEvent() error = %v", err)
|
|
}
|
|
if !eventResp.Processed || eventResp.Updated != 1 {
|
|
t.Fatalf("ReceiveEvent() resp = %+v", eventResp)
|
|
}
|
|
|
|
ok, err := service.Claim(ctx, user, tasks[0].TaskID)
|
|
if err != nil {
|
|
t.Fatalf("Claim() error = %v", err)
|
|
}
|
|
if !ok {
|
|
t.Fatalf("Claim() = false, want true")
|
|
}
|
|
|
|
tasks, err = service.ListTasks(ctx, user)
|
|
if err != nil {
|
|
t.Fatalf("ListTasks() after claim error = %v", err)
|
|
}
|
|
if len(tasks) != 0 {
|
|
t.Fatalf("exclusive tasks after claim = %+v, want hidden", tasks)
|
|
}
|
|
}
|
|
|
|
func TestConditionJumpConfigAppliesToTasks(t *testing.T) {
|
|
service, _ := newTestService(t, nil)
|
|
ctx := context.Background()
|
|
|
|
resp, err := service.SaveConfig(ctx, SaveConfigRequest{
|
|
SysOrigin: "LIKEI",
|
|
Enabled: true,
|
|
Timezone: "Asia/Riyadh",
|
|
TaskCategory: TaskCategoryDaily,
|
|
ConditionJumps: []SaveConditionJumpConfigInput{
|
|
{
|
|
ConditionType: "MIC_DURATION_SECONDS",
|
|
JumpType: "APP_ROUTE",
|
|
JumpPage: "/task/mic-guide",
|
|
SortOrder: 10,
|
|
},
|
|
{
|
|
ConditionType: "GIFT_CONSUME_GOLD",
|
|
JumpType: "ROOM_RANDOM",
|
|
JumpPage: "/room?source=custom",
|
|
SortOrder: 20,
|
|
},
|
|
},
|
|
Tasks: []SaveTaskConfigInput{
|
|
{
|
|
TaskCode: "DAILY_MIC",
|
|
Enabled: true,
|
|
TaskName: "Mic task",
|
|
ConditionType: "MIC_DURATION_SECONDS",
|
|
TargetValue: 60,
|
|
TargetUnit: "second",
|
|
RewardGold: 100,
|
|
JumpType: "ROOM_RANDOM_WITH_MIC",
|
|
SortOrder: 10,
|
|
},
|
|
{
|
|
TaskCode: "DAILY_GIFT",
|
|
Enabled: true,
|
|
TaskName: "Gift task",
|
|
ConditionType: "GIFT_CONSUME_GOLD",
|
|
TargetValue: 1,
|
|
TargetUnit: "gold",
|
|
RewardGold: 100,
|
|
SortOrder: 20,
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("SaveConfig() error = %v", err)
|
|
}
|
|
if len(resp.ConditionJumps) != 2 {
|
|
t.Fatalf("condition jumps = %+v, want 2 rows", resp.ConditionJumps)
|
|
}
|
|
if resp.ConditionJumps[1].JumpPage != "/room?source=custom" {
|
|
t.Fatalf("room condition jump page = %q, want preserved", resp.ConditionJumps[1].JumpPage)
|
|
}
|
|
if resp.Tasks[0].JumpType != "APP_ROUTE" || resp.Tasks[0].JumpPage != "/task/mic-guide" {
|
|
t.Fatalf("mic task jump = %s %s, want APP_ROUTE /task/mic-guide", resp.Tasks[0].JumpType, resp.Tasks[0].JumpPage)
|
|
}
|
|
if resp.Tasks[1].JumpType != "ROOM_RANDOM" || resp.Tasks[1].JumpPage != "/room?source=custom" {
|
|
t.Fatalf("gift task jump = %s %s, want ROOM_RANDOM /room?source=custom", resp.Tasks[1].JumpType, resp.Tasks[1].JumpPage)
|
|
}
|
|
|
|
tasks, err := service.ListTasks(ctx, common.AuthUser{UserID: 3001, SysOrigin: "LIKEI"})
|
|
if err != nil {
|
|
t.Fatalf("ListTasks() error = %v", err)
|
|
}
|
|
if len(tasks) != 2 || tasks[0].JumpPage != "/task/mic-guide" || tasks[1].JumpPage != "/room?source=custom" {
|
|
t.Fatalf("app task jumps = %+v, want custom route and custom room param", tasks)
|
|
}
|
|
}
|
|
|
|
func TestResolveJumpPageUsesRechargeRouteForRechargeTask(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
task model.TaskCenterTaskConfig
|
|
want string
|
|
}{
|
|
{
|
|
name: "recharge task with old room jump type",
|
|
task: model.TaskCenterTaskConfig{
|
|
ConditionType: EventTypeRechargeGold,
|
|
JumpType: "ROOM_RANDOM_WITH_MIC",
|
|
},
|
|
want: defaultRechargeJumpPage,
|
|
},
|
|
{
|
|
name: "recharge jump type",
|
|
task: model.TaskCenterTaskConfig{
|
|
JumpType: JumpTypeRechargeInApp,
|
|
},
|
|
want: defaultRechargeJumpPage,
|
|
},
|
|
{
|
|
name: "explicit jump page wins",
|
|
task: model.TaskCenterTaskConfig{
|
|
ConditionType: EventTypeRechargeGold,
|
|
JumpPage: "/custom/recharge",
|
|
JumpType: "ROOM_RANDOM_WITH_MIC",
|
|
},
|
|
want: "/custom/recharge",
|
|
},
|
|
{
|
|
name: "room task keeps room jump",
|
|
task: model.TaskCenterTaskConfig{
|
|
ConditionType: EventTypeMicDurationSeconds,
|
|
JumpType: "ROOM_RANDOM_WITH_MIC",
|
|
},
|
|
want: "/room",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := resolveJumpPage(tt.task); got != tt.want {
|
|
t.Fatalf("resolveJumpPage() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTaskConfigRejectsRemovedConditionTypes(t *testing.T) {
|
|
service, _ := newTestService(t, nil)
|
|
|
|
for _, conditionType := range []string{"POST_MOOD_COUNT", "INTERACTIVE_GAME_COUNT", "CP_BIND_COUNT"} {
|
|
t.Run(conditionType, func(t *testing.T) {
|
|
_, err := service.SaveConfig(context.Background(), SaveConfigRequest{
|
|
SysOrigin: "LIKEI",
|
|
Enabled: true,
|
|
Timezone: "Asia/Riyadh",
|
|
TaskCategory: TaskCategoryDaily,
|
|
Tasks: []SaveTaskConfigInput{
|
|
{
|
|
TaskCode: "REMOVED_" + conditionType,
|
|
Enabled: true,
|
|
TaskName: "Removed condition",
|
|
ConditionType: conditionType,
|
|
TargetValue: 1,
|
|
TargetUnit: "count",
|
|
RewardGold: 100,
|
|
},
|
|
},
|
|
})
|
|
if err == nil {
|
|
t.Fatalf("SaveConfig() error = nil, want invalid condition")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type taskCenterTestGateway struct {
|
|
changedEvents []string
|
|
changedCommands []integration.GoldReceiptCommand
|
|
events map[string]bool
|
|
}
|
|
|
|
func (g *taskCenterTestGateway) ExistsGoldEvent(_ context.Context, eventID string) (bool, error) {
|
|
return g.events[eventID], nil
|
|
}
|
|
|
|
func (g *taskCenterTestGateway) ChangeGoldBalance(_ context.Context, cmd integration.GoldReceiptCommand) error {
|
|
g.changedEvents = append(g.changedEvents, cmd.EventID)
|
|
g.changedCommands = append(g.changedCommands, cmd)
|
|
g.events[cmd.EventID] = true
|
|
return nil
|
|
}
|