From 8d1b14e7fe1a573ba48b0daa5de0bee049f7bb1d Mon Sep 17 00:00:00 2001 From: Joseph Spurrier Date: Wed, 8 Mar 2017 23:09:54 -0500 Subject: [PATCH] Add new usecase package. --- controller/login_test.go | 16 ++++--- controller/register_test.go | 16 ++++--- database/user_repo.go | 50 ++++++++++++++++++++ database/user_repo_test.go | 26 +++++++++++ database/user_service.go | 83 --------------------------------- database/user_service_test.go | 88 ----------------------------------- domain/user.go | 4 +- lib/boot/service.go | 5 +- usecase/user_service.go | 47 +++++++++++++++++++ 9 files changed, 149 insertions(+), 186 deletions(-) create mode 100644 database/user_repo.go create mode 100644 database/user_repo_test.go delete mode 100644 database/user_service.go delete mode 100644 database/user_service_test.go create mode 100644 usecase/user_service.go diff --git a/controller/login_test.go b/controller/login_test.go index e2b9524..fbb6caf 100644 --- a/controller/login_test.go +++ b/controller/login_test.go @@ -10,6 +10,7 @@ import ( "github.com/josephspurrier/gocleanarchitecture/database" "github.com/josephspurrier/gocleanarchitecture/domain" "github.com/josephspurrier/gocleanarchitecture/lib/view" + "github.com/josephspurrier/gocleanarchitecture/usecase" ) // TestLoginIndex ensures the index function returns a 200 code. @@ -41,8 +42,9 @@ func TestLoginStoreMissingRequiredFields(t *testing.T) { // Call the handler. h := new(controller.LoginHandler) - db := new(database.MockService) - h.UserService = database.NewUserService(db) + h.UserService = &usecase.UserService{ + UserRepo: database.NewUserRepo(new(database.MockService)), + } h.ViewService = view.New("../view", "tmpl") h.Index(w, r) @@ -67,8 +69,9 @@ func TestLoginStoreAuthenticateOK(t *testing.T) { // Call the handler. h := new(controller.LoginHandler) - db := new(database.MockService) - h.UserService = database.NewUserService(db) + h.UserService = &usecase.UserService{ + UserRepo: database.NewUserRepo(new(database.MockService)), + } h.ViewService = view.New("../view", "tmpl") // Create a new user. @@ -100,8 +103,9 @@ func TestLoginStoreAuthenticateFail(t *testing.T) { // Call the handler. h := new(controller.LoginHandler) - db := new(database.MockService) - h.UserService = database.NewUserService(db) + h.UserService = &usecase.UserService{ + UserRepo: database.NewUserRepo(new(database.MockService)), + } h.ViewService = view.New("../view", "tmpl") // Create a new user. diff --git a/controller/register_test.go b/controller/register_test.go index 3b80239..dd3bffb 100644 --- a/controller/register_test.go +++ b/controller/register_test.go @@ -9,6 +9,7 @@ import ( "github.com/josephspurrier/gocleanarchitecture/controller" "github.com/josephspurrier/gocleanarchitecture/database" "github.com/josephspurrier/gocleanarchitecture/lib/view" + "github.com/josephspurrier/gocleanarchitecture/usecase" ) // TestRegisterIndex ensures the index function returns a 200 code. @@ -48,8 +49,9 @@ func TestRegisterStoreCreateOK(t *testing.T) { // Call the handler. h := new(controller.RegisterHandler) - db := new(database.MockService) - h.UserService = database.NewUserService(db) + h.UserService = &usecase.UserService{ + UserRepo: database.NewUserRepo(new(database.MockService)), + } h.ViewService = view.New("../view", "tmpl") h.Index(w, r) @@ -73,8 +75,9 @@ func TestRegisterStoreCreateNoFieldFail(t *testing.T) { // Call the handler. h := new(controller.RegisterHandler) - db := new(database.MockService) - h.UserService = database.NewUserService(db) + h.UserService = &usecase.UserService{ + UserRepo: database.NewUserRepo(new(database.MockService)), + } h.ViewService = view.New("../view", "tmpl") h.Index(w, r) @@ -102,8 +105,9 @@ func TestRegisterStoreCreateOneMissingFieldFail(t *testing.T) { // Call the handler. h := new(controller.RegisterHandler) - db := new(database.MockService) - h.UserService = database.NewUserService(db) + h.UserService = &usecase.UserService{ + UserRepo: database.NewUserRepo(new(database.MockService)), + } h.ViewService = view.New("../view", "tmpl") h.Index(w, r) diff --git a/database/user_repo.go b/database/user_repo.go new file mode 100644 index 0000000..6b2414c --- /dev/null +++ b/database/user_repo.go @@ -0,0 +1,50 @@ +package database + +import "github.com/josephspurrier/gocleanarchitecture/domain" + +// UserRepo represents a service for managing users in a database. +type UserRepo struct { + client Service +} + +// NewUserRepo returns the service for managing users in a database. +func NewUserRepo(client Service) *UserRepo { + s := new(UserRepo) + s.client = client + return s +} + +// Store adds a user to the database. +func (s *UserRepo) Store(item *domain.User) error { + // Load the data. + err := s.client.Read() + if err != nil { + return err + } + + // Add the record. + s.client.AddRecord(*item) + + // Save the record to the database. + return s.client.Write() +} + +// FindByEmail returns a user by an email. +func (s *UserRepo) FindByEmail(email string) (*domain.User, error) { + item := new(domain.User) + + // Load the data. + err := s.client.Read() + if err != nil { + return item, err + } + + // Determine if the record exists. + for _, v := range s.client.Records() { + if v.Email == email { + return &v, nil + } + } + + return item, domain.ErrUserNotFound +} diff --git a/database/user_repo_test.go b/database/user_repo_test.go new file mode 100644 index 0000000..0f67075 --- /dev/null +++ b/database/user_repo_test.go @@ -0,0 +1,26 @@ +package database_test + +import ( + "testing" + + "github.com/josephspurrier/gocleanarchitecture/database" + "github.com/josephspurrier/gocleanarchitecture/domain" +) + +// TestUserRepo tests the user repo. +func TestUserRepo(t *testing.T) { + db := new(database.MockService) + s := database.NewUserRepo(db) + + _, err := s.FindByEmail("bad@example.com") + AssertEqual(t, err, domain.ErrUserNotFound) + + u := new(domain.User) + u.Email = "jdoe@example.com" + u.Password = "Pa$$w0rd" + err = s.Store(u) + AssertEqual(t, err, nil) + + _, err = s.FindByEmail("jdoe@example.com") + AssertEqual(t, err, nil) +} diff --git a/database/user_service.go b/database/user_service.go deleted file mode 100644 index 751d79c..0000000 --- a/database/user_service.go +++ /dev/null @@ -1,83 +0,0 @@ -package database - -import ( - "strings" - - "github.com/josephspurrier/gocleanarchitecture/domain" -) - -// UserService represents a service for managing users. -type UserService struct { - client Service -} - -// NewUserService returns the service for managing users. -func NewUserService(client Service) *UserService { - s := new(UserService) - s.client = client - return s -} - -// Authenticate returns an error if the email and password don't match. -func (s *UserService) Authenticate(d *domain.User) error { - // Load the data. - err := s.client.Read() - if err != nil { - return err - } - - // Determine if the record exists. - for _, v := range s.client.Records() { - if v.Email == d.Email { - if v.Password == d.Password { - return nil - } - return domain.ErrUserPasswordNotMatch - } - } - - return domain.ErrUserNotFound -} - -// User returns a user by email. -func (s *UserService) User(email string) (*domain.User, error) { - item := new(domain.User) - - // Load the data. - err := s.client.Read() - if err != nil { - return item, err - } - - // Determine if the record exists. - for _, v := range s.client.Records() { - if v.Email == email { - // Return the record. - return &v, nil - } - } - - return item, domain.ErrUserNotFound -} - -// CreateUser creates a new user. -func (s *UserService) CreateUser(d *domain.User) error { - err := s.client.Read() - if err != nil { - return err - } - - // Check if the user already exists - for _, v := range s.client.Records() { - if strings.ToLower(v.Email) == strings.ToLower(d.Email) { - // Return an error since the record exists. - return domain.ErrUserAlreadyExist - } - } - - // Add the record. - s.client.AddRecord(*d) - - // Save the record to the database. - return s.client.Write() -} diff --git a/database/user_service_test.go b/database/user_service_test.go deleted file mode 100644 index 71a4d1f..0000000 --- a/database/user_service_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package database_test - -import ( - "testing" - - "github.com/josephspurrier/gocleanarchitecture/database" - "github.com/josephspurrier/gocleanarchitecture/domain" -) - -// TestCreateUser ensures user can be created. -func TestCreateUser(t *testing.T) { - // Test user creation. - db := new(database.MockService) - s := database.NewUserService(db) - u := new(domain.User) - u.Email = "jdoe@example.com" - u.Password = "Pa$$w0rd" - err := s.CreateUser(u) - AssertEqual(t, err, nil) - - // Test user creation fail. - err = s.CreateUser(u) - AssertEqual(t, err, domain.ErrUserAlreadyExist) - - // Test user query. - uTest, err := s.User("jdoe@example.com") - AssertEqual(t, err, nil) - AssertEqual(t, uTest.Password, "Pa$$w0rd") - - // Test failed user query. - _, err = s.User("bademail@example.com") - AssertEqual(t, err, domain.ErrUserNotFound) -} - -// TestAuthenticate ensures user can authenticate. -func TestAuthenticate(t *testing.T) { - // Test user creation. - db := new(database.MockService) - s := database.NewUserService(db) - u := new(domain.User) - u.Email = "ssmith@example.com" - u.Password = "Pa$$w0rd" - err := s.CreateUser(u) - AssertEqual(t, err, nil) - - // Test user authentication. - err = s.Authenticate(u) - AssertEqual(t, err, nil) - - // Test failed user authentication. - u.Password = "BadPa$$w0rd" - err = s.Authenticate(u) - AssertEqual(t, err, domain.ErrUserPasswordNotMatch) - - // Test failed user authentication. - u.Email = "bfranklin@example.com" - err = s.Authenticate(u) - AssertEqual(t, err, domain.ErrUserNotFound) -} - -// TestUserFailures ensures fails properly. -func TestUserFailures(t *testing.T) { - // Test user creation. - db := new(database.MockService) - s := database.NewUserService(db) - - db.WriteFail = true - db.ReadFail = true - - u := new(domain.User) - u.Email = "ssmith@example.com" - u.Password = "Pa$$w0rd" - err := s.CreateUser(u) - AssertNotNil(t, err) - - // Test user authentication. - err = s.Authenticate(u) - AssertNotNil(t, err) - - // Test failed user query. - _, err = s.User("favalon@example.com") - AssertNotNil(t, err) - - // Test failed user authentication. - u.Email = "bfranklin@example.com" - err = s.Authenticate(u) - AssertNotNil(t, err) -} diff --git a/domain/user.go b/domain/user.go index add8c5f..e1b5827 100644 --- a/domain/user.go +++ b/domain/user.go @@ -22,6 +22,6 @@ type User struct { // UserService represents a service for managing users. type UserService interface { User(email string) (*User, error) - CreateUser(user *User) error - Authenticate(user *User) error + CreateUser(item *User) error + Authenticate(item *User) error } diff --git a/lib/boot/service.go b/lib/boot/service.go index 2d03b2c..2729cfc 100644 --- a/lib/boot/service.go +++ b/lib/boot/service.go @@ -4,6 +4,7 @@ import ( "github.com/josephspurrier/gocleanarchitecture/database" "github.com/josephspurrier/gocleanarchitecture/domain" "github.com/josephspurrier/gocleanarchitecture/lib/view" + "github.com/josephspurrier/gocleanarchitecture/usecase" ) // Service represents all the services that the application uses. @@ -21,7 +22,9 @@ func RegisterServices() *Service { db := database.NewClient("db.json") // Store all the services for the application. - s.UserService = database.NewUserService(db) + s.UserService = &usecase.UserService{ + UserRepo: database.NewUserRepo(db), + } s.ViewService = view.New("../../view", "tmpl") return s diff --git a/usecase/user_service.go b/usecase/user_service.go new file mode 100644 index 0000000..f57973a --- /dev/null +++ b/usecase/user_service.go @@ -0,0 +1,47 @@ +package usecase + +import "github.com/josephspurrier/gocleanarchitecture/domain" + +type UserRepo interface { + Store(item *domain.User) error + FindByEmail(email string) (*domain.User, error) +} + +type UserService struct { + UserRepo UserRepo +} + +// Authenticate returns an error if the email and password don't match. +func (s *UserService) Authenticate(item *domain.User) error { + q, err := s.UserRepo.FindByEmail(item.Email) + if err != nil { + return domain.ErrUserNotFound + } + + // If passwords match. + if q.Password == item.Password { + return nil + } + + return domain.ErrUserPasswordNotMatch +} + +// User returns a user by email. +func (s *UserService) User(email string) (*domain.User, error) { + item, err := s.UserRepo.FindByEmail(email) + if err != nil { + return item, domain.ErrUserNotFound + } + + return item, nil +} + +// CreateUser creates a new user. +func (s *UserService) CreateUser(item *domain.User) error { + _, err := s.UserRepo.FindByEmail(item.Email) + if err == nil { + return domain.ErrUserAlreadyExist + } + + return s.UserRepo.Store(item) +}