Files
kra-new/internal/biz/business/report_build_test.go
T

131 lines
5.4 KiB
Go

package business
import (
"context"
"errors"
"kra/pkg/task"
"strings"
"testing"
"time"
)
func reportPtr(n int64) *int64 { return &n }
func TestReportBuildIdentityRulesAndAnonymousLogins(t *testing.T) {
input := &ReportInput{Users: map[ReportUserKey]ReportUser{}, Games: map[AnalyticsKey]string{{SourceDB: "lx", ID: 1}: "游戏"}}
for _, u := range []ReportUser{{ReportUserKey: ReportUserKey{"lx", 1}, ParentID: reportPtr(0), PromoteID: reportPtr(10), IDCard: " aaX "}, {ReportUserKey: ReportUserKey{"hw", 1}, ParentID: reportPtr(0), PromoteID: reportPtr(20), IDCard: "b"}, {ReportUserKey: ReportUserKey{"lx", 2}, ParentID: reportPtr(0), PromoteID: reportPtr(10), IDCard: "aax"}, {ReportUserKey: ReportUserKey{"lx", 3}, ParentID: reportPtr(1), PromoteID: reportPtr(10), IDCard: "sub"}, {ReportUserKey: ReportUserKey{"lx", 4}, PromoteID: reportPtr(10)}, {ReportUserKey: ReportUserKey{"lx", 5}, ParentID: reportPtr(0), PromoteID: reportPtr(10)}} {
input.Users[u.ReportUserKey] = u
}
event := func(id int64) ReportEvent {
return ReportEvent{SourceDB: "lx", UserID: reportPtr(id), PromoteID: reportPtr(10), GameID: reportPtr(1)}
}
input.Registers = []ReportEvent{event(1), event(2), event(5)}
missing := event(2)
missing.SourceDB = ""
missing.PromoteID = nil
ambiguous := event(1)
ambiguous.SourceDB = ""
input.Actives = []ReportEvent{event(1), missing, ambiguous, event(3), event(4)}
for _, id := range []int64{1, 3, 99} {
p := event(id)
p.Amount = "10"
input.Payments = append(input.Payments, p)
}
noUser := event(1)
noUser.UserID = nil
noUser.Amount = "1"
input.Payments = append(input.Payments, noUser)
rows, err := compileReport(DailyReport, input)
if err != nil || len(rows) != 2 {
t.Fatalf("汇总分组错误: %+v %v", rows, err)
}
for _, row := range rows {
if row.Pay != "31.00" || row.GameName != "游戏" || row.Counts.NewPay != 1 {
t.Fatalf("金额、名称或新增付费错误: %+v", row)
}
if row.Mode == 0 && (row.Counts.Register != 3 || row.Counts.Active != 2 || row.Counts.Pay != 3) {
t.Fatalf("账号口径未保留主账号登录/原始付费账号: %+v", row)
}
if row.Mode == 1 && (row.Counts.Register != 1 || row.Counts.Active != 1 || row.Counts.Pay != 2) {
t.Fatalf("身份证口径、空证件或末尾 X 归一错误: %+v", row)
}
}
input.Registers = []ReportEvent{event(2)}
input.Payments = []ReportEvent{event(1)}
input.Payments[0].Amount = "10"
rows, err = compileReport(DailyReport, input)
if err != nil {
t.Fatal(err)
}
for _, row := range rows {
if row.Counts.NewPay != 0 {
t.Fatal("新增付费必须先取原始账号交集,不能把相同证件的其他付费账号算入")
}
}
server := event(3)
server.ServerNum = " 001 "
input.Registers = []ReportEvent{server}
input.Actives = []ReportEvent{server}
input.Payments = []ReportEvent{server}
input.Payments[0].Amount = "20"
rows, err = compileReport(ServerReport, input)
if err != nil || len(rows) != 2 {
t.Fatal("区服汇总失败")
}
for _, row := range rows {
if row.ServerNum != "001" || row.Counts.Register != 1 || row.Counts.Active != 1 || row.Counts.Pay != 1 || len(row.Keys[4]) != 0 {
t.Fatal("区服创角/活跃不能套用主账号限制,也不能生成新增付费指标")
}
}
}
type reportBuildFake struct{ days []string }
type failingReportCommit struct{}
func (failingReportCommit) Rebuild(_ context.Context, _ ReportBuildKind, _ time.Time, compile func(*ReportInput) ([]*ReportBuiltRow, error)) error {
_, err := compile(&ReportInput{Registers: []ReportEvent{{SourceDB: "lx", UserID: reportPtr(1), PromoteID: reportPtr(1), GameID: reportPtr(1)}}})
if err != nil {
return err
}
return errors.New("commit failed")
}
func TestReportBuildDetailsDoNotClaimRolledBackRows(t *testing.T) {
ctx, output := task.WithOutput(context.Background())
uc := NewReportBuildUsecase(failingReportCommit{})
if err := uc.Run(ctx, DailyReport, "backfill", "2026-11-03", "2026-11-03"); err == nil {
t.Fatal("commit error was lost")
}
text := output.String()
if !strings.Contains(text, "2026-11-03 未提交") || !strings.Contains(text, "已提交日期=0;汇总行=0;身份键=0") {
t.Fatalf("rollback was reported as success: %s", text)
}
}
func (r *reportBuildFake) Rebuild(_ context.Context, _ ReportBuildKind, day time.Time, _ func(*ReportInput) ([]*ReportBuiltRow, error)) error {
r.days = append(r.days, day.Format("2006-01-02"))
return nil
}
func TestReportBuildBackfillRangeAndCancellation(t *testing.T) {
r := &reportBuildFake{}
uc := NewReportBuildUsecase(r)
uc.now = func() time.Time { return time.Date(2026, 11, 4, 9, 0, 0, 0, portraitLocation) }
pauses := 0
uc.pause = func(context.Context) error { pauses++; return nil }
if err := uc.Run(context.Background(), DailyReport, "backfill", "2026-10-31", "2026-11-02"); err != nil || len(r.days) != 3 || pauses != 2 || r.days[2] != "2026-11-02" {
t.Fatal("回跑日期应闭区间且仅日间暂停")
}
if err := uc.Run(context.Background(), DailyReport, "backfill", "bad", ""); err != ErrAnalyticsDate {
t.Fatal("非法日期未拒绝")
}
if err := uc.Run(context.Background(), DailyReport, "backfill", "2026-11-05", "2026-11-04"); err != ErrAnalyticsDateOrder {
t.Fatal("倒置区间未拒绝")
}
ctx, cancel := context.WithCancel(context.Background())
cancel()
count := len(r.days)
if err := uc.Run(ctx, ServerReport, "today", "", ""); err != context.Canceled || len(r.days) != count {
t.Fatal("取消后仍重建报表")
}
}