143 lines
6.8 KiB
Go
143 lines
6.8 KiB
Go
package business
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type channelRepoFake struct {
|
|
ChannelRepo
|
|
roots, descendants []*Channel
|
|
metrics *ChannelMetricData
|
|
scope UserScope
|
|
bindings []*Binding
|
|
gameScope ChannelGameScope
|
|
}
|
|
|
|
func (r *channelRepoFake) List(_ context.Context, scope UserScope, _ ChannelFilter, _ ...ListOption) (*ChannelPage, error) {
|
|
r.scope = scope
|
|
rows := []*Channel{}
|
|
for _, root := range r.roots {
|
|
copy := *root
|
|
rows = append(rows, ©)
|
|
}
|
|
return &ChannelPage{Rows: rows, Available: true}, nil
|
|
}
|
|
func (r *channelRepoFake) StatChannels(context.Context, []*Channel) ([]*Channel, error) {
|
|
return r.descendants, nil
|
|
}
|
|
func (r *channelRepoFake) Metrics(context.Context, []ChannelKey, []ChannelKey, bool) (*ChannelMetricData, error) {
|
|
return r.metrics, nil
|
|
}
|
|
func (r *channelRepoFake) Bindings(_ context.Context, scope UserScope, _ string) ([]*Binding, error) {
|
|
r.scope = scope
|
|
return r.bindings, nil
|
|
}
|
|
func (r *channelRepoFake) Find(context.Context, UserScope, ChannelKey) (*Channel, error) {
|
|
return r.roots[0], nil
|
|
}
|
|
func (r *channelRepoFake) Games(_ context.Context, scope ChannelGameScope) ([]*ChannelGame, error) {
|
|
r.gameScope = scope
|
|
return []*ChannelGame{{ID: 2, PayAmount: "10.004"}, {ID: 1, PayAmount: "10.003"}}, nil
|
|
}
|
|
|
|
type channelWriteFake struct{ input *ChannelWrite }
|
|
|
|
func (w *channelWriteFake) Create(_ context.Context, input *ChannelWrite) (uint64, error) {
|
|
w.input = input
|
|
return 123, nil
|
|
}
|
|
|
|
type channelEncoderFake struct{ received string }
|
|
|
|
func (e *channelEncoderFake) Encode(raw string) (string, error) {
|
|
e.received = raw
|
|
return "encoded", nil
|
|
}
|
|
|
|
type channelPreferenceFake struct{ pref UserPreference }
|
|
|
|
func (p *channelPreferenceFake) Find(context.Context, uint) (*UserPreference, error) {
|
|
copy := p.pref
|
|
return ©, nil
|
|
}
|
|
func (p *channelPreferenceFake) Save(context.Context, uint, UserPreference) error { return nil }
|
|
|
|
func channelFixture() (*ChannelUsecase, *channelRepoFake, *channelPreferenceFake, *channelWriteFake, *channelEncoderFake) {
|
|
key := func(id uint64) ChannelKey { return ChannelKey{SourceDB: "lx", ID: id} }
|
|
r := &channelRepoFake{roots: []*Channel{{ChannelKey: key(100), Level: intPointer(1)}}, descendants: []*Channel{{ChannelKey: key(101), Level: intPointer(2), ParentID: 100, TopPromoteID: 100}, {ChannelKey: key(102), Level: intPointer(3), ParentID: 101, TopPromoteID: 100}, {ChannelKey: key(103), Level: intPointer(3), ParentID: 101}, {ChannelKey: ChannelKey{SourceDB: "hw", ID: 100}, Level: intPointer(1)}}, bindings: []*Binding{{UserID: 9, PlatformKey: PlatformKey{SourceDB: "lx", BusierID: 14}, Status: "0"}}}
|
|
r.metrics = &ChannelMetricData{GamesAvailable: true, PlayersAvailable: true, PayAvailable: true, Games: map[ChannelKey]int64{key(100): 2, key(101): 1}, Players: map[ChannelKey]int64{key(100): 1, key(101): 1, key(102): 1, key(103): 1}, Pay: map[ChannelKey]string{key(100): "10.10", key(101): "20.20", key(102): "30.30", key(103): "40.40"}, PlayerCards: []ChannelPlayer{{ChannelKey: key(100), UserID: 1, IDCard: " abcX "}, {ChannelKey: key(101), UserID: 2, IDCard: "abcx"}, {ChannelKey: key(102), UserID: 3, IDCard: ""}, {ChannelKey: key(103), UserID: 4, IDCard: "third"}}}
|
|
p := &channelPreferenceFake{UserPreference{IncludeSubPromote: true, IDCardDedup: true}}
|
|
w, e := &channelWriteFake{}, &channelEncoderFake{}
|
|
a := &accountRepoFake{accounts: map[uint]*Account{1: {ID: 1}, 7: {ID: 7, Level: 1, IDPath: "0"}, 8: {ID: 8, Level: 2, IDPath: "0,7"}, 9: {ID: 9, Level: 3, IDPath: "0,7,8"}}}
|
|
uc := NewChannelUsecase(r, w, NewAccountUsecase(a), NewUserPreferenceUsecase(p), e, nil)
|
|
uc.now = func() time.Time { return time.Date(2026, 11, 3, 12, 0, 0, 0, time.UTC) }
|
|
return uc, r, p, w, e
|
|
}
|
|
func TestChannelMetricsHonorBothPreferencesWithoutChangingGameCount(t *testing.T) {
|
|
uc, _, pref, _, _ := channelFixture()
|
|
for _, tc := range []struct {
|
|
include, dedup bool
|
|
count int64
|
|
pay string
|
|
}{{true, true, 1, "60.60"}, {true, false, 3, "60.60"}, {false, true, 1, "10.10"}, {false, false, 1, "10.10"}} {
|
|
pref.pref = UserPreference{IncludeSubPromote: tc.include, IDCardDedup: tc.dedup}
|
|
page, err := uc.List(accountContext(8, 902), ChannelFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
row := page.Rows[0]
|
|
if *row.GameCount != 2 || *row.PlayerCount != tc.count || *row.TotalPayAmount != tc.pay {
|
|
t.Fatalf("偏好统计错误 include=%v dedup=%v row=%+v", tc.include, tc.dedup, row)
|
|
}
|
|
}
|
|
}
|
|
func TestChannelLevelTwoScopeAndUnavailableMetrics(t *testing.T) {
|
|
uc, r, _, _, _ := channelFixture()
|
|
r.roots = []*Channel{r.descendants[0]}
|
|
page, err := uc.List(accountContext(9, 903), ChannelFilter{})
|
|
if err != nil || *page.Rows[0].PlayerCount != 2 || *page.Rows[0].TotalPayAmount != "90.90" || !r.scope.OnlySelf {
|
|
t.Fatal("二级渠道仅聚合直属三级或商务范围错误")
|
|
}
|
|
r.metrics.PlayersAvailable = false
|
|
r.metrics.PayAvailable = false
|
|
page, err = uc.List(accountContext(9, 903), ChannelFilter{})
|
|
if err != nil || page.Rows[0].PlayerCount != nil || page.Rows[0].TotalPayAmount != nil || page.Rows[0].GameCount == nil {
|
|
t.Fatal("未接入指标不能伪造零,已接入指标应保留")
|
|
}
|
|
}
|
|
func TestChannelCreateOwnsHashAndSettlementDefaults(t *testing.T) {
|
|
uc, r, _, writer, encoder := channelFixture()
|
|
input := &ChannelCreate{SourceDB: " LX ", Account: " 中文渠道 ", Password: " pass123 ", BusierID: 14, RegisterType: 1}
|
|
result, err := uc.Create(accountContext(8, 902), input)
|
|
if err != nil || result.ID != 123 || result.SourceDB != "lx" {
|
|
t.Fatalf("创建失败: %+v %v", result, err)
|
|
}
|
|
if writer.input.Account != "中文渠道" || writer.input.Password != "encoded" || encoder.received != " pass123 " || writer.input.NextCountUnix-writer.input.CreatedUnix != 604800 || writer.input.Status != -3 || writer.input.Level != 1 || writer.input.SettlementDays != 7 || !r.scope.IncludeSelf {
|
|
t.Fatalf("创建参数或结算周期错误: %+v", writer.input)
|
|
}
|
|
input.BusierID = 999
|
|
if _, err := uc.Create(accountContext(8, 902), input); err != ErrChannelBinding {
|
|
t.Fatal("选择了范围外的绑定")
|
|
}
|
|
input.BusierID = 14
|
|
input.Account = "bad_underscore"
|
|
if _, err := uc.Create(accountContext(8, 902), input); err != ErrChannelAccount {
|
|
t.Fatal("渠道账号字符限制丢失")
|
|
}
|
|
input.Account = "valid123"
|
|
input.Password = "12345"
|
|
if _, err := uc.Create(accountContext(8, 902), input); err != ErrChannelPassword {
|
|
t.Fatal("密码长度未校验")
|
|
}
|
|
}
|
|
func TestChannelGamesUseShanghaiDateAndStableMoneyOrder(t *testing.T) {
|
|
uc, r, _, _, _ := channelFixture()
|
|
uc.now = func() time.Time { return time.Date(2026, 11, 3, 17, 0, 0, 0, time.UTC) }
|
|
games, err := uc.Games(accountContext(9, 903), ChannelKey{SourceDB: "lx", ID: 100})
|
|
if err != nil || r.gameScope.EndDate != "2026-11-04" || !r.gameScope.IDCardDedup || games[0].ID != 1 {
|
|
t.Fatal("累计日期、口径或并列排序错误")
|
|
}
|
|
}
|