package repository import ( "errors" "regexp" "testing" "time" "github.com/DATA-DOG/go-sqlmock" "github.com/wmms/wmms/internal/model" "github.com/wmms/wmms/pkg/database" ) func TestListUsersIncludesAllAssignedRoles(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("open sql mock: %v", err) } t.Cleanup(func() { database.DB = nil; db.Close() }) database.DB = db now := time.Date(2026, 9, 16, 9, 0, 0, 0, time.Local) mock.ExpectQuery(regexp.QuoteMeta(`SELECT COUNT(*) FROM users`)).WillReturnRows( sqlmock.NewRows([]string{"count"}).AddRow(1), ) mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, username, password, description, created_at FROM users ORDER BY id LIMIT ? OFFSET ?`)). WithArgs(20, 0). WillReturnRows(sqlmock.NewRows([]string{"id", "username", "password", "description", "created_at"}). AddRow(7, "zhangsan", "hash", "测试用户", now)) mock.ExpectQuery(regexp.QuoteMeta(`SELECT ur.user_id, r.id, r.code, r.name, r.enabled, r.created_at, r.updated_at FROM user_roles ur JOIN roles r ON r.id = ur.role_id WHERE ur.user_id IN (?) ORDER BY ur.user_id, r.id`)). WithArgs(int64(7)). WillReturnRows(sqlmock.NewRows([]string{"user_id", "id", "code", "name", "enabled", "created_at", "updated_at"}). AddRow(7, 2, "warehouse_admin", "仓库管理员", true, now, now). AddRow(7, 3, "purchaser", "采购人员", true, now, now)) users, total, err := NewUserRepository().List("", 20, 0) if err != nil { t.Fatalf("list users: %v", err) } if total != 1 || len(users) != 1 || len(users[0].Roles) != 2 { t.Fatalf("unexpected users: total=%d users=%+v", total, users) } if users[0].Roles[1].Name != "采购人员" { t.Fatalf("role names not loaded: %+v", users[0].Roles) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestDeleteRejectsLastSuperAdmin(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("open sql mock: %v", err) } t.Cleanup(func() { database.DB = nil; db.Close() }) database.DB = db mock.ExpectBegin() mock.ExpectQuery(regexp.QuoteMeta(`SELECT id FROM roles WHERE code = 'super_admin' FOR UPDATE`)). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) mock.ExpectQuery(regexp.QuoteMeta(`SELECT COUNT(*) FROM user_roles WHERE user_id = ? AND role_id = ?`)). WithArgs(int64(7), int64(1)). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1)) mock.ExpectQuery(regexp.QuoteMeta(`SELECT COUNT(*) FROM user_roles WHERE role_id = ?`)). WithArgs(int64(1)). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1)) mock.ExpectRollback() err = NewUserRepository().Delete(7) if !errors.Is(err, ErrLastSuperAdmin) { t.Fatalf("delete error = %v, want ErrLastSuperAdmin", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestUpdateRejectsRemovingRoleFromLastSuperAdmin(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("open sql mock: %v", err) } t.Cleanup(func() { database.DB = nil; db.Close() }) database.DB = db mock.ExpectBegin() mock.ExpectQuery(regexp.QuoteMeta(`SELECT COUNT(*) FROM roles WHERE enabled = TRUE AND id IN (?)`)). WithArgs(int64(2)). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1)) mock.ExpectQuery(regexp.QuoteMeta(`SELECT id FROM roles WHERE code = 'super_admin' FOR UPDATE`)). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) mock.ExpectQuery(regexp.QuoteMeta(`SELECT COUNT(*) FROM user_roles WHERE user_id = ? AND role_id = ?`)). WithArgs(int64(7), int64(1)). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1)) mock.ExpectQuery(regexp.QuoteMeta(`SELECT COUNT(*) FROM user_roles WHERE role_id = ?`)). WithArgs(int64(1)). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1)) mock.ExpectRollback() user := &model.User{ID: 7, Description: "系统管理员"} err = NewUserRepository().Update(user, []int64{2}) if !errors.Is(err, ErrLastSuperAdmin) { t.Fatalf("update error = %v, want ErrLastSuperAdmin", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestCreateUserAssignsMultipleEnabledRolesInTransaction(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("open sql mock: %v", err) } t.Cleanup(func() { database.DB = nil; db.Close() }) database.DB = db mock.ExpectBegin() mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO users (username, password, description) VALUES (?, ?, ?)`)). WithArgs("zhangsan", "hash", "测试用户"). WillReturnResult(sqlmock.NewResult(7, 1)) mock.ExpectQuery(regexp.QuoteMeta(`SELECT COUNT(*) FROM roles WHERE enabled = TRUE AND id IN (?, ?)`)). WithArgs(int64(2), int64(3)). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(2)) mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO user_roles (user_id, role_id) VALUES (?, ?), (?, ?)`)). WithArgs(int64(7), int64(2), int64(7), int64(3)). WillReturnResult(sqlmock.NewResult(0, 2)) mock.ExpectCommit() user := &model.User{Username: "zhangsan", Password: "hash", Description: "测试用户"} if err := NewUserRepository().Create(user, []int64{2, 3}); err != nil { t.Fatalf("create user: %v", err) } if user.ID != 7 { t.Fatalf("expected user id 7, got %d", user.ID) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } }