Files
NianAIGC/backend/internal/application/bootstrap_admin_test.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))
}
}