Files
wx-win-agent/control-plane/user_store_test.go
T
rogee 8760fa49f0
Build web service image / build (push) Successful in 2m7s
feat: add control-plane identity and management console
2026-09-27 20:27:31 +08:00

91 lines
2.9 KiB
Go

package controlplane
import (
"context"
"errors"
"path/filepath"
"testing"
)
func testUserStore(t *testing.T) *UserStore {
t.Helper()
db, err := openSQLite(filepath.Join(t.TempDir(), "users.sqlite"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = db.Close() })
store, err := NewUserStore(db)
if err != nil {
t.Fatal(err)
}
return store
}
func TestUserStoreBootstrapAndAuthentication(t *testing.T) {
store := testUserStore(t)
ctx := context.Background()
if err := store.BootstrapAdmins(ctx, map[string]string{"root": "bootstrap-password"}); err != nil {
t.Fatal(err)
}
user, ok, err := store.Authenticate(ctx, "ROOT", "bootstrap-password")
if err != nil || !ok {
t.Fatalf("expected case-insensitive bootstrap login, got ok=%v err=%v", ok, err)
}
if user.Role != RoleAdmin || !user.Active {
t.Fatalf("unexpected bootstrap user: %+v", user)
}
if _, ok, err := store.Authenticate(ctx, "root", "wrong-password"); err != nil || ok {
t.Fatalf("expected invalid password, got ok=%v err=%v", ok, err)
}
if err := store.BootstrapAdmins(ctx, map[string]string{"root": "changed-password"}); err != nil {
t.Fatal(err)
}
if _, ok, err := store.Authenticate(ctx, "root", "bootstrap-password"); err != nil || !ok {
t.Fatalf("bootstrap must not overwrite persisted credentials: ok=%v err=%v", ok, err)
}
}
func TestUserStoreAssignmentsAndLastAdministrator(t *testing.T) {
store := testUserStore(t)
ctx := context.Background()
if err := store.BootstrapAdmins(ctx, map[string]string{"root": "bootstrap-password"}); err != nil {
t.Fatal(err)
}
member, err := store.Create(ctx, WebUser{
Username: "operator-1", DisplayName: "Operator One", Role: RoleMember,
NodeIDs: []string{"node-a", "node-a"},
}, "member-password-1")
if err != nil {
t.Fatal(err)
}
if len(member.NodeIDs) != 1 || member.NodeIDs[0] != "node-a" {
t.Fatalf("expected de-duplicated Node assignment, got %v", member.NodeIDs)
}
if _, err := store.Create(ctx, WebUser{Username: "OPERATOR-1", DisplayName: "Duplicate", Role: RoleMember}, "member-password-2"); !errors.Is(err, ErrUserAlreadyExists) {
t.Fatalf("expected case-insensitive duplicate rejection, got %v", err)
}
if _, err := store.Update(ctx, member.ID, UserUpdate{
DisplayName: "Operator Updated", Role: RoleMember, Active: false, NodeIDs: []string{"node-b"},
}); err != nil {
t.Fatal(err)
}
updated, err := store.Get(ctx, member.ID)
if err != nil {
t.Fatal(err)
}
if updated.Active || len(updated.NodeIDs) != 1 || updated.NodeIDs[0] != "node-b" {
t.Fatalf("user update did not persist: %+v", updated)
}
admins, err := store.List(ctx)
if err != nil {
t.Fatal(err)
}
for _, admin := range admins {
if admin.Role == RoleAdmin {
if _, err := store.Update(ctx, admin.ID, UserUpdate{DisplayName: admin.DisplayName, Role: RoleMember, Active: true}); !errors.Is(err, ErrLastAdministrator) {
t.Fatalf("expected last administrator protection, got %v", err)
}
}
}
}