feat(users): admin promote/demote endpoints
POST /users/{id}/promote and /demote set a user's role (admin/user), admin-only
(403 otherwise). Mirrors the verify endpoints: SetRole use case + repo method,
validated role. Unit, admin-flow, and integration tests included.
This commit is contained in:
@@ -145,3 +145,21 @@ func TestUserRepoSetVerifiedUnknown(t *testing.T) {
|
||||
t.Errorf("got %v, want ErrUserNotFound", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserRepoSetRole(t *testing.T) {
|
||||
repo := NewUserRepo(testPool(t))
|
||||
ctx := context.Background()
|
||||
u := seedUser(t, repo)
|
||||
|
||||
if err := repo.SetRole(ctx, u.ID, domain.RoleAdmin); err != nil {
|
||||
t.Fatalf("promote: %v", err)
|
||||
}
|
||||
got, _ := repo.GetByID(ctx, u.ID)
|
||||
if got.Role != domain.RoleAdmin {
|
||||
t.Errorf("role = %q, want admin", got.Role)
|
||||
}
|
||||
|
||||
if err := repo.SetRole(ctx, uuid.New(), domain.RoleAdmin); !errors.Is(err, usecase.ErrUserNotFound) {
|
||||
t.Errorf("unknown user: got %v, want ErrUserNotFound", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,27 @@ func (r *UserRepo) SetVerified(ctx context.Context, id uuid.UUID, verified bool)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetRole changes a user's role and returns ErrUserNotFound when the id matches
|
||||
// no row.
|
||||
func (r *UserRepo) SetRole(ctx context.Context, id uuid.UUID, role domain.Role) error {
|
||||
sql, args, err := psql.Update("users").
|
||||
Set("role", string(role)).
|
||||
Set("updated_at", sq.Expr("now()")).
|
||||
Where(sq.Eq{"id": id}).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tag, err := conn(ctx, r.pool).Exec(ctx, sql, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tag.RowsAffected() == 0 {
|
||||
return usecase.ErrUserNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func scanUser(row pgx.Row) (*domain.User, error) {
|
||||
var (
|
||||
u domain.User
|
||||
|
||||
Reference in New Issue
Block a user