160 lines
7.4 KiB
Go
160 lines
7.4 KiB
Go
package business
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"gorm.io/gorm"
|
|
"kra/internal/biz/business"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type activationTestSources map[string]*gorm.DB
|
|
|
|
func (s activationTestSources) Database(name string) (*gorm.DB, error) {
|
|
db := s[name]
|
|
if db == nil {
|
|
return nil, errors.New("missing source")
|
|
}
|
|
return db, nil
|
|
}
|
|
|
|
func TestActivationCrossPlatformMetricsAndIdentityDedup(t *testing.T) {
|
|
db, oa, repo := prepareActivations(t)
|
|
for _, sql := range []string{
|
|
"CREATE TABLE tab_user(id INTEGER,source_db TEXT,promote_id INTEGER,puid INTEGER,register_time TEXT,idcard TEXT)",
|
|
"INSERT INTO tab_user VALUES(1,'lx',100,0,'1000','same'),(2,'lx',101,0,'1500','same'),(1,'hw',100,0,'1500','same'),(3,'lx',102,0,'1600',''),(4,'lx',103,0,'1600','outside'),(5,'lx',100,1,'1600','child-account'),(6,'lx',100,0,'999','before'),(7,'lx',100,0,'2001','after')",
|
|
} {
|
|
if err := db.Exec(sql).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := oa.Model(&activationPO{}).Where("id=1").Update("related_channels", `[{"platformName":"lx","promoteId":100},{"platformName":"hw","promoteId":100},{"platformName":"lx","promoteId":101}]`).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
repo.(*activationRepo).databases = activationSDKFixtures(t, db, oa)
|
|
page, err := repo.List(context.Background(), business.ActivationScope{All: true}, business.ActivationFilter{Statuses: []string{"reached"}, IDCardDedup: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
row := page.Rows[0]
|
|
if row.RegisteredPlayers == nil || *row.RegisteredPlayers != 1 || row.AccumAmount != "1032.60" {
|
|
t.Fatalf("cross platform dedup/tree overlap: %+v", row)
|
|
}
|
|
page, err = repo.List(context.Background(), business.ActivationScope{All: true}, business.ActivationFilter{Statuses: []string{"reached"}})
|
|
if err != nil || *page.Rows[0].RegisteredPlayers != 4 {
|
|
t.Fatalf("account count: %+v %v", page, err)
|
|
}
|
|
if err := oa.Model(&activationPO{}).Where("id=1").Update("unbind_at", nil).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
page, err = repo.List(context.Background(), business.ActivationScope{All: true}, business.ActivationFilter{Statuses: []string{"reached"}})
|
|
if err != nil || page.Rows[0].AccumAmount != "1732.60" || *page.Rows[0].RegisteredPlayers != 5 {
|
|
t.Fatalf("reached should keep accumulating: %+v %v", page, err)
|
|
}
|
|
}
|
|
|
|
func TestActivationPoolFiltersAndRedactedProjection(t *testing.T) {
|
|
db, oa, _ := prepareActivations(t)
|
|
if err := oa.AutoMigrate(&activationPoolPO{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, sql := range []string{"ALTER TABLE tab_promote ADD COLUMN lock_status INTEGER", "ALTER TABLE tab_promote ADD COLUMN last_login_time INTEGER", "ALTER TABLE tab_promote ADD COLUMN create_time INTEGER", "UPDATE tab_promote SET status=1,lock_status=0,create_time=1000"} {
|
|
if err := db.Exec(sql).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
for _, sql := range []string{"ALTER TABLE sys_promote_active_pool_bind ADD COLUMN pool_record_id INTEGER", "ALTER TABLE sys_promote_active_pool_bind ADD COLUMN reject_reason TEXT", "ALTER TABLE sys_promote_active_pool_bind ADD COLUMN review_at DATETIME"} {
|
|
if err := oa.Exec(sql).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
now := time.Now()
|
|
rows := []activationPoolPO{{ID: 1, PlatformName: "lx", PromoteID: 100, PromoteAccount: "Open", RegisterType: 1, CreatedAt: now}, {ID: 2, PlatformName: "hw", PromoteID: 100, PromoteAccount: "Claimed", ClaimFlag: 1, CreatedAt: now}, {ID: 3, PlatformName: "lx", PromoteID: 103, PromoteAccount: "Removed", DeletedAt: &now, CreatedAt: now}}
|
|
if err := oa.Create(&rows).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := NewActivationPoolRepo(testData{db}, activationSDKFixtures(t, db, oa))
|
|
page, err := r.Pool(context.Background(), business.ActivationPoolFilter{Name: "Open", ChannelType: "公会", Status: "正常"}, business.ListLimit(10))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if page.Total != 1 || page.Rows[0].Name != "Open" || page.Rows[0].Status != "正常" {
|
|
t.Fatalf("pool filters: %+v", page)
|
|
}
|
|
if _, err := r.PoolChannel(context.Background(), 2); err != business.ErrActivationNotFound {
|
|
t.Fatal("claimed pool may not be applied")
|
|
}
|
|
}
|
|
|
|
func TestDashboardEventsUseTimeAndHistoricalOwner(t *testing.T) {
|
|
db := prepareAccounts(t)
|
|
sdk := openTestDB(t)
|
|
if err := sdk.AutoMigrate(&channelEventPO{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Exec("INSERT INTO sys_users(id,username,level,id_path) VALUES(7,'owner',3,'0'); INSERT INTO sys_user_bind_platform(user_id,source_db,platform_busier_id,status) VALUES(7,'lx',10,'0')").Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
begin := time.Date(2026, 11, 2, 0, 0, 0, 0, time.UTC)
|
|
w := business.DashboardWindow{Begin: begin, End: begin, PreviousBegin: begin.AddDate(0, 0, -1), PreviousEnd: begin.AddDate(0, 0, -1)}
|
|
events := []channelEventPO{{ID: 1, PromoteID: 1, PromoteLevel: 1, Kind: "locked", OldBusierID: 10, OccurredAt: begin}, {ID: 2, PromoteID: 1, PromoteLevel: 1, Kind: "locked", OldBusierID: 10, OccurredAt: begin.Add(time.Hour)}, {ID: 3, PromoteID: 2, PromoteLevel: 2, Kind: "terminated", OldBusierID: 10, OccurredAt: begin}, {ID: 4, PromoteID: 3, PromoteLevel: 1, Kind: "locked", OldBusierID: 20, OccurredAt: begin}, {ID: 5, PromoteID: 1, PromoteLevel: 1, Kind: "locked", OldBusierID: 10, OccurredAt: begin.Add(-time.Hour)}, {ID: 6, PromoteID: 1, PromoteLevel: 1, Kind: "terminated", OldBusierID: 10, OccurredAt: begin.AddDate(0, 0, 1)}}
|
|
if err := sdk.Create(&events).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := &dashboardRepo{data: testData{db}, databases: activationTestSources{"lx": sdk}}
|
|
out := &business.DashboardData{}
|
|
if err := r.events(context.Background(), business.AnalyticsScope{Viewer: business.UserScope{OnlySelf: true, SelfID: 7}, IncludeSub: true}, []string{"lx"}, w, out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !out.EventsAvailable || out.Current.LockedChannels != 1 || out.Previous.LockedChannels != 1 || out.Current.TerminatedChannels != 1 {
|
|
t.Fatalf("events: %+v", out)
|
|
}
|
|
if err := sdk.Create(&channelEventPO{Kind: "coverage", OccurredAt: begin}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
incomplete := &business.DashboardData{}
|
|
if err := r.events(context.Background(), business.AnalyticsScope{Viewer: business.UserScope{All: true}}, []string{"lx"}, w, incomplete); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if incomplete.EventsAvailable || !incomplete.EventsIncomplete {
|
|
t.Fatal("missing historical coverage was reported as real zero")
|
|
}
|
|
}
|
|
|
|
func TestActivationStopsAtExternalTransferBeforeOAScan(t *testing.T) {
|
|
db, oa, _ := prepareActivations(t)
|
|
sources := activationSDKFixtures(t, db, oa)
|
|
sdk := sources["lx"]
|
|
if err := sdk.AutoMigrate(&channelEventPO{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
end := time.Unix(1600, 0).UTC()
|
|
if err := oa.Model(&activationPO{}).Where("id=1").Updates(map[string]any{"reach_at": time.Unix(1200, 0).UTC(), "unbind_at": nil}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := sdk.Create(&channelEventPO{Kind: "transfer", PromoteID: 100, OldBusierAccount: "Own", OccurredAt: end}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
repo := NewActivationRepo(testData{db}, sources)
|
|
row, err := repo.Find(context.Background(), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if row.EndAt() == nil || !row.EndAt().Equal(end) {
|
|
t.Fatal("old owner can read past transfer")
|
|
}
|
|
items, err := repo.Spend(context.Background(), activationMetricQuery(row))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
values := []string{}
|
|
for _, item := range items {
|
|
values = append(values, item.Amount)
|
|
}
|
|
sum, err := sumActivationMoney(values)
|
|
if err != nil || sum != "13.40" {
|
|
t.Fatalf("post-transfer spend leaked: %s %v", sum, err)
|
|
}
|
|
}
|