package business import ( "context" "golang.org/x/crypto/bcrypt" "kra/internal/biz/system" "testing" ) type accountRepoFake struct { AccountRepo accounts map[uint]*Account created *Account updated *Account listCalls int scope UserScope } func (r *accountRepoFake) Find(_ context.Context, id uint) (*Account, error) { if a := r.accounts[id]; a != nil { copy := *a return ©, nil } return nil, ErrAccountNotFound } func (r *accountRepoFake) AuthorityForLevel(_ context.Context, level int) (uint, error) { return uint(900 + level), nil } func (r *accountRepoFake) Create(_ context.Context, a *Account) error { copy := *a r.created = © a.ID = 30 return nil } func (r *accountRepoFake) Update(_ context.Context, a *Account) error { r.updated = a; return nil } func (r *accountRepoFake) List(_ context.Context, scope UserScope, _ ...ListOption) ([]*Account, int64, error) { r.listCalls++ r.scope = scope return []*Account{}, 0, nil } func accountContext(id, aid uint) context.Context { return system.NewActorContext(context.Background(), system.Actor{UserID: id, AuthorityID: aid}) } func TestChildBusinessUsesServerHierarchyAndMappedRole(t *testing.T) { for _, level := range []int{0, 1, 2, 3} { r := &accountRepoFake{accounts: map[uint]*Account{12: {ID: 12, Level: level, IDPath: "0,5"}}} uc := NewAccountUsecase(r) aid := uint(901) if level == 0 { aid = 888 } a, err := uc.CreateChild(accountContext(12, aid), &Account{ID: 999, Level: 1, ParentID: 999, IDPath: "0,999", AuthorityID: 888, Username: "child", Password: "password", RealName: "姓名"}) if level == 3 { if err != ErrChildLevel || r.created != nil { t.Fatalf("三级不应创建子商务: %v", err) } continue } if err != nil { t.Fatal(err) } if r.created.Level != level+1 || r.created.AuthorityID != uint(901+level) || r.created.Status != 0 || r.created.NickName != "姓名" { t.Fatalf("层级或默认信息不正确: %+v", r.created) } if level == 0 { if r.created.ParentID != 0 || r.created.IDPath != "0" { t.Fatal("超管创建的一级挂错归属") } } else if r.created.ParentID != 12 || r.created.IDPath != "0,5,12" { t.Fatal("子商务祖先链不正确") } if a.Password != "" || bcrypt.CompareHashAndPassword([]byte(r.created.Password), []byte("password")) != nil { t.Fatal("密码未安全写入或泄露到返回值") } } } func TestBusinessScopeSeparatesDescendantsAndSiblings(t *testing.T) { r := &accountRepoFake{accounts: map[uint]*Account{ 12: {ID: 12, Level: 1, IDPath: "0"}, 13: {ID: 13, Level: 2, ParentID: 12, IDPath: "0,12"}, 14: {ID: 14, Level: 3, IDPath: "0,12,13"}, 99: {ID: 99, Level: 2, IDPath: "0,123"}, }} uc := NewAccountUsecase(r) ctx := accountContext(12, 901) for _, id := range []uint{12, 13, 14} { if _, err := uc.Find(ctx, id); err != nil { t.Fatalf("本人及下级应可查: %v", err) } } if _, err := uc.Find(ctx, 99); err != ErrAccountScope { t.Fatalf("ID 前缀相似的其他树不应可见: %v", err) } if _, _, err := uc.List(accountContext(14, 903), AccountFilter{}); err != nil || r.listCalls != 0 { t.Fatal("三级商务列表应直接返回空") } if _, _, err := uc.List(ctx, AccountFilter{}); err != nil || r.scope.IncludeSelf || r.scope.All || r.scope.DescendantPath != "0,12" { t.Fatalf("商务列表应仅下级: %+v %v", r.scope, err) } } func TestAccountPartialEditKeepsHierarchyAndReviewState(t *testing.T) { r := &accountRepoFake{accounts: map[uint]*Account{1: {ID: 1}, 2: {ID: 2, Level: 2, ParentID: 8, IDPath: "0,8", Status: 3, AuthorityID: 902, Username: "child"}}} name := "新姓名" if err := NewAccountUsecase(r).Update(accountContext(1, 888), &AccountUpdate{ID: 2, RealName: &name}); err != nil { t.Fatal(err) } if a := r.updated; a.Status != 3 || a.Level != 2 || a.ParentID != 8 || a.AuthorityID != 902 || a.Username != "child" || a.RealName != name { t.Fatalf("部分更新改变了其他字段: %+v", a) } }