207 lines
7.2 KiB
Go
207 lines
7.2 KiB
Go
package application
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/administration"
|
|
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/postgres"
|
|
)
|
|
|
|
// bootstrapFakeStore is a minimal in-memory administration.Store for
|
|
// bootstrap tests. Only ListAccounts and CreateAccount carry behavior; the
|
|
// remaining methods exist to satisfy the interface.
|
|
type bootstrapFakeStore struct {
|
|
accounts map[string]administration.Account
|
|
}
|
|
|
|
func newBootstrapFakeStore() *bootstrapFakeStore {
|
|
return &bootstrapFakeStore{accounts: map[string]administration.Account{}}
|
|
}
|
|
|
|
func (s *bootstrapFakeStore) ListAccounts(_ context.Context, filters administration.AccountFilters) ([]administration.Account, error) {
|
|
out := []administration.Account{}
|
|
for _, account := range s.accounts {
|
|
if filters.OrganizationID != "" && account.OrganizationID != filters.OrganizationID {
|
|
continue
|
|
}
|
|
if filters.Role != "" && account.Role != filters.Role {
|
|
continue
|
|
}
|
|
if !filters.IncludeDisabled && account.Status != administration.StatusActive {
|
|
continue
|
|
}
|
|
out = append(out, account)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *bootstrapFakeStore) GetAccount(_ context.Context, id string) (administration.Account, bool, error) {
|
|
account, ok := s.accounts[id]
|
|
return account, ok, nil
|
|
}
|
|
|
|
func (s *bootstrapFakeStore) CreateAccount(_ context.Context, account administration.Account) (administration.Account, error) {
|
|
if _, exists := s.accounts[account.ID]; exists {
|
|
return administration.Account{}, errors.New("account already exists")
|
|
}
|
|
s.accounts[account.ID] = account
|
|
return account, nil
|
|
}
|
|
|
|
func (s *bootstrapFakeStore) UpdateAccount(_ context.Context, account administration.Account) (administration.Account, error) {
|
|
s.accounts[account.ID] = account
|
|
return account, nil
|
|
}
|
|
|
|
func (s *bootstrapFakeStore) DeleteAccount(_ context.Context, id, _ string) error {
|
|
delete(s.accounts, id)
|
|
return nil
|
|
}
|
|
|
|
func (s *bootstrapFakeStore) ListOrganizations(context.Context, bool) ([]administration.Organization, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (s *bootstrapFakeStore) GetOrganization(context.Context, string) (administration.Organization, bool, error) {
|
|
return administration.Organization{}, false, nil
|
|
}
|
|
|
|
func (s *bootstrapFakeStore) CreateOrganization(_ context.Context, organization administration.Organization) (administration.Organization, error) {
|
|
return organization, nil
|
|
}
|
|
|
|
func (s *bootstrapFakeStore) UpdateOrganization(_ context.Context, organization administration.Organization) (administration.Organization, error) {
|
|
return organization, nil
|
|
}
|
|
|
|
func (s *bootstrapFakeStore) DeleteOrganization(context.Context, string) error { return nil }
|
|
|
|
func (s *bootstrapFakeStore) CountOrganizationMembers(context.Context, string) (int, error) {
|
|
return 0, nil
|
|
}
|
|
|
|
func configuredBootstrap() BootstrapAdminConfig {
|
|
return BootstrapAdminConfig{Phone: "13800138000", Password: "change-me-now", DisplayName: "平台超级管理员"}
|
|
}
|
|
|
|
func TestParseBootstrapAdminConfig(t *testing.T) {
|
|
env := map[string]string{
|
|
"ZHINIAN_BOOTSTRAP_ADMIN_PHONE": " 13800138000 ",
|
|
"ZHINIAN_BOOTSTRAP_ADMIN_PASSWORD": "change-me-now",
|
|
"ZHINIAN_BOOTSTRAP_ADMIN_NAME": " 平台超级管理员 ",
|
|
}
|
|
config := ParseBootstrapAdminConfig(func(name string) string { return env[name] })
|
|
if config.Phone != "13800138000" || config.Password != "change-me-now" || config.DisplayName != "平台超级管理员" {
|
|
t.Fatalf("unexpected parsed config: %+v", config)
|
|
}
|
|
if !config.Configured() {
|
|
t.Fatalf("expected parsed config to be complete")
|
|
}
|
|
|
|
defaulted := ParseBootstrapAdminConfig(func(name string) string {
|
|
if name == "ZHINIAN_BOOTSTRAP_ADMIN_NAME" {
|
|
return ""
|
|
}
|
|
return env[name]
|
|
})
|
|
if defaulted.DisplayName != "平台超级管理员" {
|
|
t.Fatalf("expected default display name, got %q", defaulted.DisplayName)
|
|
}
|
|
|
|
if config := ParseBootstrapAdminConfig(nil); config.Configured() {
|
|
t.Fatalf("nil getenv must produce an incomplete config")
|
|
}
|
|
}
|
|
|
|
func TestBootstrapSuperAdminNoopWithoutConfig(t *testing.T) {
|
|
store := newBootstrapFakeStore()
|
|
service := administration.NewService(store)
|
|
created, err := BootstrapSuperAdmin(context.Background(), postgres.BackendPostgres, service, BootstrapAdminConfig{})
|
|
if err != nil || created {
|
|
t.Fatalf("expected no-op without config, got created=%v err=%v", created, err)
|
|
}
|
|
if len(store.accounts) != 0 {
|
|
t.Fatalf("expected no accounts, got %d", len(store.accounts))
|
|
}
|
|
}
|
|
|
|
func TestBootstrapSuperAdminNoopOnLocalBackend(t *testing.T) {
|
|
store := newBootstrapFakeStore()
|
|
service := administration.NewService(store)
|
|
created, err := BootstrapSuperAdmin(context.Background(), postgres.BackendLocal, service, configuredBootstrap())
|
|
if err != nil || created {
|
|
t.Fatalf("expected no-op on local backend, got created=%v err=%v", created, err)
|
|
}
|
|
if len(store.accounts) != 0 {
|
|
t.Fatalf("expected no accounts, got %d", len(store.accounts))
|
|
}
|
|
}
|
|
|
|
func TestBootstrapSuperAdminCreatesOnce(t *testing.T) {
|
|
store := newBootstrapFakeStore()
|
|
service := administration.NewService(store)
|
|
|
|
created, err := BootstrapSuperAdmin(context.Background(), postgres.BackendPostgres, service, configuredBootstrap())
|
|
if err != nil || !created {
|
|
t.Fatalf("expected first bootstrap to create, got created=%v err=%v", created, err)
|
|
}
|
|
if len(store.accounts) != 1 {
|
|
t.Fatalf("expected exactly one account, got %d", len(store.accounts))
|
|
}
|
|
for _, account := range store.accounts {
|
|
if account.Role != administration.RoleSuperAdmin || account.Status != administration.StatusActive {
|
|
t.Fatalf("unexpected bootstrapped account: %+v", account)
|
|
}
|
|
if account.PasswordHash == "change-me-now" || account.PasswordHash == "" {
|
|
t.Fatalf("password must be hashed, got %q", account.PasswordHash)
|
|
}
|
|
}
|
|
|
|
created, err = BootstrapSuperAdmin(context.Background(), postgres.BackendPostgres, service, configuredBootstrap())
|
|
if err != nil || created {
|
|
t.Fatalf("expected second bootstrap to skip, got created=%v err=%v", created, err)
|
|
}
|
|
if len(store.accounts) != 1 {
|
|
t.Fatalf("expected still exactly one account, got %d", len(store.accounts))
|
|
}
|
|
}
|
|
|
|
func TestBootstrapSuperAdminSkipsDisabledSuperAdmin(t *testing.T) {
|
|
store := newBootstrapFakeStore()
|
|
existing := administration.Account{
|
|
ID: "user-existing", Phone: "13900139000", DisplayName: "已有超管",
|
|
Role: administration.RoleSuperAdmin, Status: administration.StatusDisabled,
|
|
SessionVersion: 1,
|
|
}
|
|
store.accounts[existing.ID] = existing
|
|
service := administration.NewService(store)
|
|
|
|
created, err := BootstrapSuperAdmin(context.Background(), postgres.BackendPostgres, service, configuredBootstrap())
|
|
if err != nil || created {
|
|
t.Fatalf("expected skip with existing disabled super admin, got created=%v err=%v", created, err)
|
|
}
|
|
if len(store.accounts) != 1 {
|
|
t.Fatalf("expected no new account, got %d", len(store.accounts))
|
|
}
|
|
}
|
|
|
|
func TestBootstrapSuperAdminRejectsShortPassword(t *testing.T) {
|
|
store := newBootstrapFakeStore()
|
|
service := administration.NewService(store)
|
|
config := configuredBootstrap()
|
|
config.Password = "short"
|
|
|
|
created, err := BootstrapSuperAdmin(context.Background(), postgres.BackendPostgres, service, config)
|
|
if err == nil {
|
|
t.Fatalf("expected short password to fail")
|
|
}
|
|
if created {
|
|
t.Fatalf("expected no account on failed bootstrap")
|
|
}
|
|
if len(store.accounts) != 0 {
|
|
t.Fatalf("expected no accounts, got %d", len(store.accounts))
|
|
}
|
|
}
|