Files
kra-new/cmd/channel_portrait_http_test.go
T

196 lines
9.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"encoding/json"
"net/http"
"net/http/httptest"
"path/filepath"
"strconv"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
businessbiz "kra/internal/biz/business"
"kra/internal/biz/system"
businessdata "kra/internal/data/business"
systemdata "kra/internal/data/system"
"kra/internal/server/handler"
businessservice "kra/internal/service/business"
)
func TestChannelPortraitHTTPTotalTiersAndScope(t *testing.T) {
db, err := gorm.Open(sqlite.Open(filepath.Join(t.TempDir(), "channel-portrait.db")), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
pool, _ := db.DB()
t.Cleanup(func() { _ = pool.Close() })
for _, step := range append(systemdata.Migrations(), businessdata.Migrations()...) {
if err := step.Migrate(db); err != nil {
t.Fatal(err)
}
}
for _, sql := range []string{
"INSERT INTO sys_users(id,uuid,username,level,parent_id,id_path,enable) VALUES(1,'root','root',0,0,'0',1),(7,'owner','owner',1,0,'0',1),(9,'leaf','leaf',3,7,'0,7',1),(99,'outside','outside',1,0,'0',1)",
"INSERT INTO sys_user_bind_platform(user_id,source_db,platform_busier_id,platform_busier_account,status) VALUES(9,'lx',14,'sdk','0'),(99,'hw',20,'other','0')",
"CREATE TABLE tab_promote(id INTEGER,source_db TEXT,account TEXT,nickname TEXT,parent_name TEXT,real_name TEXT,promote_level INTEGER,register_type INTEGER,status INTEGER,lock_status INTEGER,parent_id INTEGER,top_promote_id INTEGER,busier_id INTEGER,create_time INTEGER,last_login_time INTEGER)",
"INSERT INTO tab_promote VALUES(100,'lx','root-account','主渠道','','负责人',1,1,-3,0,0,0,14,1000,2000),(101,'lx','child-account','子渠道','旧上级名称','',2,0,1,0,100,100,14,1000,0),(100,'hw','outside-channel','','','',1,0,1,0,0,0,20,1000,0)",
"CREATE TABLE tab_promote_apply(source_db TEXT,promote_id INTEGER,game_id INTEGER,status INTEGER)",
"INSERT INTO tab_promote_apply VALUES('lx',100,1,1),('lx',100,1,1),('lx',100,2,1)",
"CREATE TABLE tab_game(source_db TEXT,id INTEGER,game_name TEXT)",
"INSERT INTO tab_game VALUES('lx',1,'游戏甲'),('lx',2,'游戏乙')",
"CREATE TABLE tab_sw_report_day(source_db TEXT,promote_id INTEGER,game_id INTEGER,dedup_mode INTEGER,stat_date DATE,total_pay DECIMAL(20,2))",
"CREATE TABLE tab_sw_report_day_key(source_db TEXT,promote_id INTEGER,game_id INTEGER,dedup_mode INTEGER,stat_date DATE,metric INTEGER,identity_key TEXT)",
"CREATE TABLE tab_user(source_db TEXT,id INTEGER,promote_id INTEGER,puid INTEGER,idcard TEXT,register_time INTEGER)",
"CREATE TABLE tab_spend(source_db TEXT,promote_id INTEGER,user_id INTEGER,pay_status INTEGER,pay_time INTEGER,pay_amount DECIMAL(20,2))",
} {
if err := db.Exec(sql).Error; err != nil {
t.Fatal(err)
}
}
now := time.Now().In(time.FixedZone("CST", 8*3600))
today := now.Format("2006-01-02")
day20 := now.AddDate(0, 0, -20).Format("2006-01-02")
day30 := now.AddDate(0, 0, -30).Format("2006-01-02")
previous := now.AddDate(0, 0, -7).Format("2006-01-02")
start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix()
for _, row := range []struct {
promote, mode int
date, amount string
}{{100, 0, day30, "20.00"}, {100, 1, day20, "777.00"}, {100, 0, day20, "10.00"}, {100, 0, previous, "6.00"}, {100, 0, today, "12.00"}, {101, 0, today, "3.00"}} {
if err := db.Exec("INSERT INTO tab_sw_report_day VALUES('lx',?,1,?,?,?)", row.promote, row.mode, row.date, row.amount).Error; err != nil {
t.Fatal(err)
}
}
cards := []string{"A-X", " A-x ", "B", "C", "D"}
for i, card := range cards {
promote := 100
if i == 1 {
promote = 101
}
if err := db.Exec("INSERT INTO tab_user VALUES('lx',?,?,0,?,?)", i+1, promote, card, start+3600).Error; err != nil {
t.Fatal(err)
}
for _, mode := range []int{0, 1} {
key := strconv.Itoa(i + 1)
if mode == 1 {
key = card
if i < 2 {
key = "A-x"
}
}
for _, metric := range []int{1, 2} {
if err := db.Exec("INSERT INTO tab_sw_report_day_key VALUES('lx',?,1,?,?,?,?)", promote, mode, today, metric, key).Error; err != nil {
t.Fatal(err)
}
}
}
}
if err := db.Exec("INSERT INTO tab_sw_report_day_key VALUES('lx',100,1,1,?,3,'A-x')", today).Error; err != nil {
t.Fatal(err)
}
for _, row := range []struct {
promote, user int
timestamp int64
amount string
}{{100, 1, 1000, "9999.50"}, {101, 2, 2000000000, "0.50"}, {100, 3, start + 3600, "1000.00"}, {100, 4, start + 3600, "0.01"}, {100, 5, start + 3600, "0.00"}} {
if err := db.Exec("INSERT INTO tab_spend VALUES('lx',?,?,1,?,?)", row.promote, row.user, row.timestamp, row.amount).Error; err != nil {
t.Fatal(err)
}
}
provider := businessHTTPData{db}
channels := businessdata.NewChannelRepo(provider)
accounts := businessbiz.NewAccountUsecase(businessdata.NewAccountRepo(provider))
prefs := businessbiz.NewUserPreferenceUsecase(businessdata.NewUserPreferenceRepo(provider))
uc := businessbiz.NewChannelPortraitUsecase(businessdata.NewChannelPortraitRepo(provider), channels, businessdata.NewBusinessPortraitRepo(provider, channelHTTPDatabases{}), accounts, prefs)
h := handler.NewChannelPortrait(businessservice.NewChannelPortraitService(uc))
engine := gin.New()
engine.Use(func(c *gin.Context) {
id, _ := strconv.ParseUint(c.GetHeader("X-Test-User"), 10, 64)
aid := uint(901)
if id == 1 {
aid = 888
}
c.Request = c.Request.WithContext(system.NewActorContext(c.Request.Context(), system.Actor{UserID: uint(id), AuthorityID: aid}))
c.Next()
})
engine.GET("/sw/promote/portrait", h.Portrait)
server := httptest.NewServer(engine)
defer server.Close()
call := func(user int, query string, expected int) map[string]any {
t.Helper()
req, _ := http.NewRequest("GET", server.URL+"/sw/promote/portrait?"+query, nil)
req.Header.Set("X-Test-User", strconv.Itoa(user))
res, err := server.Client().Do(req)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
var body map[string]any
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
t.Fatal(err)
}
if body["code"] != float64(expected) {
t.Fatalf("画像 %s 用户%d响应错误: %+v", query, user, body)
}
return body["data"].(map[string]any)
}
data := call(7, "sourceDb=lx&promoteId=100&period=today", 0)
basic := data["basic"].(map[string]any)
metrics := data["metrics"].(map[string]any)
if basic["statusLabel"] != "商务新增" || basic["gameCount"] != float64(2) || basic["parentPromoteId"] != nil || basic["lastLoginTime"] == nil || basic["busierDisplayName"] != "sdk(leaf)" {
t.Fatalf("基本信息错误: %+v", basic)
}
if metrics["register"].(map[string]any)["value"] != "4" || metrics["active"].(map[string]any)["value"] != "4" || metrics["payRate"].(map[string]any)["value"] != "25.0" || metrics["payAmount"].(map[string]any)["value"] != "15.00" {
t.Fatalf("本期指标错误: %+v", metrics)
}
if _, ok := metrics["arpu"]; ok {
t.Fatal("渠道第四项是活跃人数,不是商务 ARPU")
}
tiers := data["payTiers"].([]any)
for _, raw := range tiers {
tier := raw.(map[string]any)
if tier["count"] != float64(1) || tier["ratio"] != "25.0" {
t.Fatalf("累计付费分层错误: %+v", tiers)
}
}
if data["gameDistribution"].([]any)[0].(map[string]any)["payAmount"] != "31.00" {
t.Fatal("游戏分布应始终累计,不随顶部今日切换")
}
data = call(7, "sourceDb=lx&promoteId=100&period=total", 0)
metrics = data["metrics"].(map[string]any)
if metrics["payAmount"].(map[string]any)["value"] != "31.00" || len(data["registerTrend"].([]any)) != 21 {
t.Fatal("累计模式没有使用当前去重口径最早日期")
}
for _, raw := range metrics {
metric := raw.(map[string]any)
if metric["compareValue"] != nil || metric["changeRate"] != nil || metric["changeDiff"] != nil || metric["compareLabel"] != nil {
t.Fatal("累计模式不应有环比字段值")
}
}
if err := db.Exec("INSERT INTO sys_user_preference(user_id,include_sub_promote,idcard_dedup) VALUES(7,1,0)").Error; err != nil {
t.Fatal(err)
}
data = call(7, "sourceDb=lx&promoteId=100&period=total", 0)
tiers = data["payTiers"].([]any)
if data["metrics"].(map[string]any)["payAmount"].(map[string]any)["value"] != "51.00" || tiers[0].(map[string]any)["count"] != float64(0) || tiers[1].(map[string]any)["count"] != float64(2) {
t.Fatal("模式0应按原始付款账号分类,且使用自己的累计起点")
}
data = call(7, "sourceDb=lx&promoteId=101&period=last7", 0)
basic = data["basic"].(map[string]any)
if basic["parentPromoteId"] != float64(100) || basic["parentPromoteName"] != "root-account" || basic["lastLoginTime"] != nil {
t.Fatal("上级应读同平台当前账号,不能沿用旧快照名")
}
call(99, "sourceDb=lx&promoteId=100", 7)
call(7, "sourceDb=hw&promoteId=100", 7)
if err := db.Exec("DROP TABLE tab_sw_report_day_key").Error; err != nil {
t.Fatal(err)
}
data = call(7, "sourceDb=lx&promoteId=100", 0)
if data["available"] != false || data["metrics"] != nil || data["basic"].(map[string]any)["promoteId"] != float64(100) {
t.Fatal("缺数据时应保留已授权基本资料并标明未知")
}
}