adds create user func

This commit is contained in:
Deepak Mallubhotla 2021-01-12 11:17:03 -06:00
parent c8e33884c0
commit f59593e9e8
Signed by: deepak
GPG Key ID: 64BF53A3369104E7
9 changed files with 207 additions and 8 deletions

1
go.mod
View File

@ -10,4 +10,5 @@ require (
github.com/jmoiron/sqlx v1.2.0
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.5.1
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
)

View File

@ -40,6 +40,10 @@ func (e *errorStore) SelectUserByID(id int) (*models.User, error) {
return nil, e.error
}
func (e *errorStore) InsertUser(user *models.User) (int, error) {
return 0, e.error
}
func (e *errorStore) ConnectionLive() error {
return e.error
}

View File

@ -16,6 +16,7 @@ type Store interface {
InsertPlan(plan *Plan) (int, error)
SelectActionsByPlanID(plan *Plan) ([]*Action, error)
SelectUserByID(id int) (*User, error)
InsertUser(user *User) (int, error)
}
// Model represents a current model item.

View File

@ -47,6 +47,10 @@ func (ms *multiStore) SelectUserByID(id int) (*models.User, error) {
return &models.User{UserID: int64(id), Username: "test", DisplayName: "Ted Est", Password: []byte("oh no")}, nil
}
func (ms *multiStore) InsertUser(user *models.User) (int, error) {
return int(user.UserID), nil
}
func (ms *multiStore) ConnectionLive() error {
return nil
}

View File

@ -1,5 +1,10 @@
package models
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
// User represents the full DB user field, for inserts and compares.
// No reason to return the hashed pw on the route though.
type User struct {
@ -34,3 +39,38 @@ func (u *User) NoPassword() *UserNoPassword {
DisplayName: u.DisplayName,
}
}
// CreateUserRequest represents a desired user creation.
type CreateUserRequest struct {
Username string `json:"username"`
DisplayName string `json:"display_name"`
Password string `json:"password"`
}
// CreateUser takes in a create user request and returns the ID of the newly created user.
func (m *Model) CreateUser(req *CreateUserRequest) (int, error) {
if req.Username == "" {
return -1, fmt.Errorf("No username provided")
}
if req.Password == "" {
return -1, fmt.Errorf("No password provided")
}
hash, err := hashPassword(req.Password)
if err != nil {
return -1, err
}
desiredUser := &User{
Username: req.Username,
DisplayName: req.DisplayName,
Password: hash,
}
return m.InsertUser(desiredUser)
}
// hashPassword hashes a password
func hashPassword(password string) ([]byte, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 11)
return bytes, err
}

View File

@ -31,16 +31,49 @@ func TestErrorUsers(t *testing.T) {
assert.NotNil(err)
}
func TestUserNoPassword(t *testing.T) {
func TestCreateUser(t *testing.T) {
assert := assert.New(t)
id := int64(3)
username := "test"
displayName := "Ted Est"
pass := []byte("abc")
u := &models.User{UserID: id, Username: username, DisplayName: displayName, Password: pass}
pass := "abc"
u := &models.CreateUserRequest{Username: username, DisplayName: displayName, Password: pass}
unp := u.NoPassword()
assert.EqualValues(id, unp.UserID)
assert.Equal(username, unp.Username)
assert.Equal(displayName, unp.DisplayName)
ss := &multiStore{
[]*models.Action{},
[]*models.Plan{}}
m := models.New(ss)
_, err := m.CreateUser(u)
assert.Nil(err)
}
func TestCreateUserFailValidation(t *testing.T) {
assert := assert.New(t)
username := ""
displayName := "Ted Est"
pass := "abc"
u := &models.CreateUserRequest{Username: username, DisplayName: displayName, Password: pass}
ss := &multiStore{
[]*models.Action{},
[]*models.Plan{}}
m := models.New(ss)
_, err := m.CreateUser(u)
assert.NotNil(err)
}
func TestCreateUserFailValidationPassword(t *testing.T) {
assert := assert.New(t)
username := "aoeu"
displayName := "Ted Est"
pass := ""
u := &models.CreateUserRequest{Username: username, DisplayName: displayName, Password: pass}
ss := &multiStore{
[]*models.Action{},
[]*models.Plan{}}
m := models.New(ss)
_, err := m.CreateUser(u)
assert.NotNil(err)
}

View File

@ -62,6 +62,10 @@ func (ms *multiStore) SelectUserByID(id int) (*models.User, error) {
return nil, nil
}
func (ms *multiStore) InsertUser(user *models.User) (int, error) {
return int(user.UserID), nil
}
func (ms *multiStore) ConnectionLive() error {
return nil
}
@ -119,6 +123,10 @@ func (e *errorStore) SelectUserByID(id int) (*models.User, error) {
return nil, e.error
}
func (e *errorStore) InsertUser(user *models.User) (int, error) {
return 0, e.error
}
func (e *errorStore) ConnectionLive() error {
return e.error
}
@ -168,6 +176,10 @@ func (e *onlyCreateStore) SelectUserByID(id int) (*models.User, error) {
return nil, nil
}
func (e *onlyCreateStore) InsertUser(user *models.User) (int, error) {
return 0, e.error
}
func (e *onlyCreateStore) ConnectionLive() error {
return e.error
}

View File

@ -143,3 +143,19 @@ func (store *postgresStore) SelectUserByID(id int) (*models.User, error) {
}
return &user, nil
}
func (store *postgresStore) InsertUser(user *models.User) (int, error) {
queryString := store.db.Rebind("INSERT INTO users (username, display_name, password) VALUES (?, ?, ?) RETURNING user_id")
tx := store.db.MustBegin()
var id int
err := tx.Get(&id, queryString, user.Username, user.DisplayName, user.Password)
if err != nil {
tx.Rollback()
return -1, err
}
err = tx.Commit()
if err != nil {
return -1, err
}
return id, nil
}

View File

@ -2,6 +2,7 @@ package store_test
import (
"fmt"
"gitea.deepak.science/deepak/gogmagog/models"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"testing"
@ -62,3 +63,90 @@ func TestErrUserByID(t *testing.T) {
t.Errorf("unfulfilled expectations: %s", err)
}
}
func TestInsertUser(t *testing.T) {
// setup
assert := assert.New(t)
str, mock := getDbMock(t)
username := "test"
displayName := "Tom Est"
password := []byte("ABC€")
usr := &models.User{Username: username, DisplayName: displayName, Password: password}
idToUse := 8
rows := sqlmock.NewRows([]string{"user_id"}).AddRow(8)
mock.ExpectBegin()
mock.ExpectQuery(`^INSERT INTO users \(username, display_name, password\) VALUES \(\$1, \$2, \$3\) RETURNING user_id$`).
WithArgs(username, displayName, password).
WillReturnRows(rows)
mock.ExpectCommit()
// function under test
insertedId, err := str.InsertUser(usr)
// check results
assert.Nil(err)
assert.EqualValues(idToUse, insertedId)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unfulfilled expectations: %s", err)
}
}
func TestInsertUserErr(t *testing.T) {
// setup
assert := assert.New(t)
str, mock := getDbMock(t)
username := "test"
displayName := "Tom Est"
password := []byte("ABC€")
usr := &models.User{Username: username, DisplayName: displayName, Password: password}
mock.ExpectBegin()
mock.ExpectQuery(`^INSERT INTO users \(username, display_name, password\) VALUES \(\$1, \$2, \$3\) RETURNING user_id$`).
WithArgs(username, displayName, password).
WillReturnError(fmt.Errorf("example error"))
mock.ExpectRollback()
// function under test
_, err := str.InsertUser(usr)
// check results
assert.NotNil(err)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unfulfilled expectations: %s", err)
}
}
func TestInsertUserCommitErr(t *testing.T) {
// setup
assert := assert.New(t)
str, mock := getDbMock(t)
username := "test"
displayName := "Tom Est"
password := []byte("ABC€")
usr := &models.User{Username: username, DisplayName: displayName, Password: password}
idToUse := 8
rows := sqlmock.NewRows([]string{"user_id"}).AddRow(idToUse)
mock.ExpectBegin()
mock.ExpectQuery(`^INSERT INTO users \(username, display_name, password\) VALUES \(\$1, \$2, \$3\) RETURNING user_id$`).
WithArgs(username, displayName, password).
WillReturnRows(rows)
mock.ExpectCommit().WillReturnError(fmt.Errorf("another error example"))
// function under test
_, err := str.InsertUser(usr)
// check results
assert.NotNil(err)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unfulfilled expectations: %s", err)
}
}